IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> pg 锁机制深析 -> 正文阅读

[游戏开发]pg 锁机制深析

spin lock

使用 cas 去获取锁,先获取 spins_per_delay 次数,如果还失败,则每次获取失败将 delay 时长延长至 1~2倍 delay 值加 0.5 us,spins_per_delay 的值在获取锁后会做更新,如果这次没有等待,则下次可以多尝试 100 次(最多不超过1000次),如果这次第一次尝试是失败的,则下次尝试少一次,(最少 10 次)

fast path 加锁失败路径

首先将 fast path 锁转移至 hash 表

锁的链接

lock 指锁实例。
lock.procLocks 指获取锁的进程对应这个锁的 procLock 链表,可以通过 (PROCLOCK *)(lock.procLocks.next - 32) 来还原。
lock.waitProcs 指等待这一锁的进程结构的链表。可以 通过 (PGPROC *)lock.waitProcs.links.next 来还原

:链接为空的条件不是next 和 prev 为空,而是 next 和 prev 都指向链表自己(lock.procLocks.next == &lock.procLocks)

regular 锁授予

每当请求一个 lock 的一个 mode 时,lock->requested[mode] 就会加1,当成功授予一个锁时,lock 上的 lock->granted[lockmode] 计数会加一(如果 grant[mode] 数与 request[mode] 数一样时,还会把 waitmask[mode] 置为 false),且 grantMask[mode] 置为 true

锁请求与等待进程的冲突检查

锁冲突矩阵:

Requested Lock ModeACCESS SHAREROW SHAREROW EXCLUSIVESHARE UPDATE EXCLUSIVESHARESHARE ROW EXCLUSIVEEXCLUSIVEACCESS EXCLUSIVE
ACCESS SHAREX
ROW SHAREXX
ROW EXCLUSIVEXXXX
SHARE UPDATE EXCLUSIVEXXXXX
SHAREXXXXX
SHARE ROW EXCLUSIVEXXXXXX
EXCLUSIVEXXXXXXX
ACCESS EXCLUSIVEXXXXXXXX

锁请求与等待进程的冲突检查:

lock->waitMask 表示等待锁的人等的 lock mode,如果自己即将获取的锁与其它等待进程的 lock mode 冲突(比如我要拿 shared 锁,但有人拿了 exclusive 锁),意味着我拿这个锁后会有人等我,即有冲突。

锁请求与拿锁进程的冲突检查:

如果我要拿的锁与已经拿锁人的 mode 不冲突(比如有人拿了share update,有人在等 share,而我要拿的是 row share,与两者都不冲突),则检查无冲突
如果我要拿的锁与我自己的锁冲突,或与我自己所在组拿的锁冲突,则不算冲突,检查如下:

  • 将我的 proclock→holdmask 中的mode 从 lock→granted[mode]中减1,看是否还有 lock→granted 与我将要拿的mode 冲突
  • 将已经拿锁的 lock->procLocks 中属于我的锁组的找出来,将这部分从 lock→granted[mode] 中减去1后,看是否还有 lock→granted 与 我要的 mode 冲突

检查到冲突时等待队列位置判断:

如果有冲突,则要等锁 WaitOnLock→ProcSleep,这里需要判断自己加到等待队列的什么位置,这里的判断需要找到冲突的锁组,将自己加在冲突锁组的前面

  • 找出所有我在锁组获取的 lock mode
  • 从前往后找出其它锁组的等待进程,检查它们是否等待我在的锁组(它们等的锁 mode 与我的锁组拿的锁 mode 冲突)
    • 如果它们在等我的锁组,我也在等它们的锁组(它们拿的锁 mode 与我等的锁 mode 冲突),则发现死锁,这种情况不用等,直接 error
    • 如果它们不需要等我(我与它无冲突),则我排在它后面
    • 如果找到了等我的进程,而我又不需要等它们,则我要的等待要排在它前面
    • 这里代码里有一个特殊情况(我感觉,事实上这种情况应该永远不存在,因为前面已经过滤掉了)如果排在前面的人,它们等我的锁与我要等的锁不冲突,意味它们虽然等我的锁组,但等的不是我,而是等我的锁组其它成员(比如前面都等 shared row exclusive 锁,我要等的是 share 锁与前面等待的锁不冲突,但与我们组拿的row exclusive 锁冲突,则我也可以直接拿这个 share 锁),这种情况要再判断一次是否是与我自己所在组拿的锁冲突,如果是这种情况,不用 sleep,直接获取。

以后每次唤醒,我需要去检查一次死锁,如果是被 auto vacuum 卡了,则允许我 kill 它一次。

锁的使用

我们可以人为将数据分为两类:表的 schema,表内 data。但其实还存在介于 schema 与 data 之间的数据 ---- index,锁的选择也受修改数据的种类的影响,主要围绕两个问题:

  • 别人修改时,自己是否也能修改
  • 自己修改时,会不会影响到别人读和改

或者更简单地讲:写是否影响读,写是否影响写
pg 中的 8 种 lock 使用并非绝对,很可能随着新功能的增加或者新约束的存在而增减锁的等级。下面描述下锁使用的推荐姿势(个人观点):

  • AccessShareLock: 最弱锁,任何 data、index 的写都不影响结果,用于 SELECT 语句
  • AccessExclusiveLock:最强锁,大部分 schema 的修改会影响读的方式,必须独占进行,如 drop table,add column 等
  • RowShareLock:用的比较窄,代码中几乎找不到它的存在,它主要是为了支持 SELECT FOR SHARE/UPDATE,用于预读取数据并在相应 tuple 上标记 ---- 即将写(exclusive)或读(share)的谓词锁,它不影响别人读 data 和创建 index,但影响写入,heap_tuple_update 时可能因发现 tuple 上的标记而排队。(全排他行为必须等待,行排他行为会在 update 时等待),要提前避免排队的话(大部分场景无需避免),需要用 ExclusiveLock 来限制。
  • RowExclusiveLock:用于对系统表 update 和对普通表做 DML 的场景,很多 recovery 阶段可能不需要这个锁(能保证 readonly 或者只有一个修改者时)
  • ShareRowExclusiveLock:代码中使用场景不太清晰,但核心是与 unique 语义相关,用于完全不允许修改普通表的数据的场景,如添加外键,创建trigger(也可能涉及外键)时。(即以 data 为 index 的场景)。
  • ShareLock: 用于保证读数据的完整性(repeated read),不允许别人改 data 和 schema,可以作为一个 pause 动作出现,即等所有修改表数据的行为完成再读,读期间不允许别人再改数据。如果读期间,数据的修改并不影响它的可重读,则可以降级为最弱的 AccessShareLock
  • ShareUpdateExclusiveLock:相比于 ShareRowExclusiveLock,它允许修改表的数据,但保证 index 同一时间只有一个人改 index,代码中它的使用比较混乱(我感觉很多场景用 RowExclusiveLock 也是 ok 的,只是为了防止以后添加新功能时会受到影响,索性直接升级到了ShareUpdateExclusiveLock)。它可以当作辅助性的 RowExclusiveLock 来使用,即一段代码可能被多进程并发访问,且不容易处理正确的场景,如 analyze,create index(其中 index 是个很典型场景,因为他介于 schema 与 data 之间),如果能保证一段 index 相关代码不会被并发访问到,则可以用 RowExclusiveLock 来代替,如果一个表 data 的修改会影响到另一个表的 data,则需要升级到更高的ShareRowExclusiveLock,比如一个表的数据实际是另一个表的外键的场景。
  • ExclusiveLock:除了比 AccessExclusiveLock 锁弱一点以外,它还经常用于非表的排他锁,如 advisory 锁,transaction 锁, session 锁等,这两者含义不同,但用了同一个名词,因为对于非表的场景,share 与 exclusive 已经能说明 read 和 write 的语义了。代码中使用:对于普通表,用于必须排他修改,但又大部分情况下不会修改到 select for shared/update 的 row 的场景;对于系统表的增删,为了绝对安全,大部分场景使用 ExclusiveLock ,如增删 catalog tuple,syscache 等场景,改(update)的话用 RowExclusiveLock 就可以了。(这里有个有趣的场景,对于 pg_enum 的 label 的修改,对 pg_type 拿 ExclusiveLock 以防有人删了这个 pg_enum,而对pg_enum 表只需要 RowExclusiveLock)
  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2022-12-25 11:39:00  更:2022-12-25 11:41:39 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年4日历 -2024/4/16 16:08:39-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码