当前位置: 首页 > news >正文

postgresql源码学习(十五)—— 行锁③-死锁检测

本篇主要学习下死锁检测的原理,不研究具体算法和代码,有兴趣请参考原书。

一、 等待与图

1. 等待与有向图

       事务T1在等待事务T2,可以用一个有向图表示。如果每个等待是一条“边”,那么死锁检测其实就是一个找“环”的过程。

2. 实边与虚边

  • 实边(Hard Edge):等待队列中的事务等待持锁事务。假设事务A持有表的共享锁或排它锁,当事务B申请表的排他锁时,就要进入等待。pg称这种等待的边为“实边”。
  • 虚边(Soft Edge):均发生在等待队列中的事务。假设事务A持有表的共享锁,事务B申请表的排他锁被阻塞,事务C又想申请该表共享锁。此时事务C需要等待事务B,于是BC都在等待队列中,pg称这种等待的边为“虚边”。

3. 死锁

       假设等待图中有环,且环全部由实边构成,那么此时只能中断某个事务来打破这个环,这就是死锁。若环还有虚边,说明有事务尚未真正持有锁,此时还可以通过调整事务顺序来避免死锁。

二、 死锁检测定时器

每个会话启动时都会注册一个死锁检测定时器,用于提示进行死锁检测。

以下在postinit.c文件InitPostgres函数

    /*
     * Also set up timeout handlers needed for backend operation.  We need these in every case except bootstrap.
     */
    if (!bootstrap)
    {
        RegisterTimeout(DEADLOCK_TIMEOUT, CheckDeadLockAlert);
        RegisterTimeout(STATEMENT_TIMEOUT, StatementTimeoutHandler);
        RegisterTimeout(LOCK_TIMEOUT, LockTimeoutHandler);
        RegisterTimeout(IDLE_IN_TRANSACTION_SESSION_TIMEOUT,
                        IdleInTransactionSessionTimeoutHandler);
        RegisterTimeout(IDLE_SESSION_TIMEOUT, IdleSessionTimeoutHandler);
        RegisterTimeout(CLIENT_CONNECTION_CHECK_TIMEOUT, ClientCheckTimeoutHandler);
    }

其中CheckDeadLockAlert作为定时任务,会定时设定一个死锁检测标志got_deadlock_timeout。

当事务处于等待状态时,ProcSleep函数(proc.c文件)会根据此标志决定是否进行死锁检测。

/* check for deadlocks first, as that's probably log-worthy */
            if (got_deadlock_timeout)
            {
                CheckDeadLock();
                got_deadlock_timeout = false;
            }

CheckDeadLock是死锁检测入口函数。

检测过程是互斥的,也就是说在检测开始时,主锁表就需要被保护起来,避免被修改(因为要检查锁持有和等待队列)。如果事务只通过本地锁表或fast path就持有锁,则它不在死锁检测范围内。

三、 主要结构体

等待图由PGPROC(会话或进程)和EDGE(边)两个结构体构成。

deadlock.c文件

typedef struct
{
    PGPROC     *waiter;         /* 等待者 */
    PGPROC     *blocker;        /* 阻塞者 */
    LOCK       *lock;           /* 等待的锁 */
    int         pred;           /* 拓扑排序使用的额外变量 */
    int         link;           /*拓扑排序使用的额外变量 */
} EDGE;

       如果环中包含虚边,可以尝试通过调整等待队列来消除环。新的等待队列保存在waitOrders数组中,每个数组元素但是一个等待队列,由WAIT_ORDER结构体保存该队列。

/* One potential reordering of a lock's wait queue */
typedef struct
{
    LOCK       *lock;           /* 调整的是哪个锁上的等待队列 */
    PGPROC    **procs;          /* 在该锁上新的等待队列 */
    int         nProcs;          /* procs数组长度 */
} WAIT_ORDER;

       在环的查找过程中,可以被调整的虚边记录在possibleConstraints数组中。每次向前推进都会从possibleConstraints找到一个虚边进行探索,被探索的边保存在curConstraints数组中。

       这两个数组长度要足够大,例如curConstraints数组长度是MaxBackends,被调整的虚边数不能超过该值。

四、 主要函数及作用

1. CheckDeadLock函数

死锁检测入口函数

2. DeadLockCheckRecurse函数

不断被递归调用,检测死锁

  • 如果遇到实边构成的环,则停止探测并报出死锁错误
  • 如果遇到虚边构成的环,则尝试在curConstraints数组中推进探测的Configuration

3. TestConfiguration函数

  • 根据当前Configuration(即curConstraints)产生新的等待队列
  • 使用新等待队列检测是否存在环。
  •        如果遇到实边构成的环,则停止探测并报出死锁错误
  •        如果遇到虚边构成的环,则将当前虚边保存到possibleConstraints数组中

4. FindLockCycle系列函数

       FindLockCycle函数实现找环操作。具体而言,它不停调用FindLockCycleRecurse函数分别查找实边和虚边构成的环。

       FindLockCycleRecurse函数会再调用FindLockCycleRecurseMember函数,该函数还是两个功能:

  • 如果遇到实边构成的环,则停止探测并报出死锁错误
  • 如果遇到虚边构成的环,则将当前虚边保存到possibleConstraints数组中

       在查找环的过程中,它会优先从waitOrders数组中选择等待队列,如果没有,才会使用锁本身的等待队列。

参考

《PostgreSQL技术内幕:事务处理深度探索》第2章

相关文章:

  • 【linux】shell 编程之流程控制语句详解
  • [PAT练级笔记] 34 Basic Level 1034 有理数四则运算
  • 【探花交友】保存用户信息、上传用户头像、用户信息管理
  • ElasticSearch Client问题整理2
  • Python必知必会 os 模块详解
  • Promise系列学习
  • 线程同步的几种方式(2)
  • 第七章:面向对象编程(中级部分)
  • Redis知识-实战篇(1)
  • Pytorch搭建基本的GAN模型及训练过程
  • 3.7Docker consul的容器服务更新与发现
  • <位图(bitset)和布隆过滤器(BloomFilter)>——《C++高阶》
  • RxJava(四)-过滤操作符
  • 高级数据结构——图
  • 数据库(mysql)之事务
  • [分享]iOS开发 - 实现UITableView Plain SectionView和table不停留一起滑动
  • [分享]iOS开发-关于在xcode中引用文件夹右边出现问号的解决办法
  • [译]前端离线指南(上)
  • canvas 高仿 Apple Watch 表盘
  • FineReport中如何实现自动滚屏效果
  • java8-模拟hadoop
  • Java多线程(4):使用线程池执行定时任务
  • java架构面试锦集:开源框架+并发+数据结构+大企必备面试题
  • Laravel Telescope:优雅的应用调试工具
  • PHP 程序员也能做的 Java 开发 30分钟使用 netty 轻松打造一个高性能 websocket 服务...
  • Rancher如何对接Ceph-RBD块存储
  • Redux 中间件分析
  • Xmanager 远程桌面 CentOS 7
  • 百度小程序遇到的问题
  • 从tcpdump抓包看TCP/IP协议
  • 分享几个不错的工具
  • 给Prometheus造假数据的方法
  • 官方解决所有 npm 全局安装权限问题
  • 普通函数和构造函数的区别
  • 十年未变!安全,谁之责?(下)
  • 我有几个粽子,和一个故事
  • 用jQuery怎么做到前后端分离
  • ​软考-高级-系统架构设计师教程(清华第2版)【第9章 软件可靠性基础知识(P320~344)-思维导图】​
  • #!/usr/bin/python与#!/usr/bin/env python的区别
  • #FPGA(基础知识)
  • #Linux(Source Insight安装及工程建立)
  • (07)Hive——窗口函数详解
  • (10)ATF MMU转换表
  • (3)STL算法之搜索
  • (31)对象的克隆
  • (pojstep1.1.1)poj 1298(直叙式模拟)
  • (zhuan) 一些RL的文献(及笔记)
  • (二十六)Java 数据结构
  • (每日持续更新)信息系统项目管理(第四版)(高级项目管理)考试重点整理第3章 信息系统治理(一)
  • (欧拉)openEuler系统添加网卡文件配置流程、(欧拉)openEuler系统手动配置ipv6地址流程、(欧拉)openEuler系统网络管理说明
  • (生成器)yield与(迭代器)generator
  • (十八)三元表达式和列表解析
  • (四)activit5.23.0修复跟踪高亮显示BUG
  • (一)【Jmeter】JDK及Jmeter的安装部署及简单配置
  • (一)VirtualBox安装增强功能