王小川|MySQL强人“锁”难《死磕MySQL系列 三》

王小川|MySQL强人“锁”难《死磕MySQL系列 三》

文章图片

王小川|MySQL强人“锁”难《死磕MySQL系列 三》

文章图片

王小川|MySQL强人“锁”难《死磕MySQL系列 三》

文章图片


系列文章一、原来一条select语句在MySQL是这样执行的《死磕MySQL系列 一》
二、一生挚友redo log、binlog《死磕MySQL系列 二》
前言
最近数据库老是出现下面死锁情况 , 借着这俩种情况出发详细的理解一下MySQL中的锁 。
Lock wait timeout exceeded; try restarting transaction

Deadlock found when trying to get lock; try restarting transaction

一、MySQL中有那些锁全局锁
根据全局两个字 , 就可以肯定的是给一个整体加上锁 。 全局锁就是对整个数据库实例加锁 。
对于flush tables with read lock , 执行完成后整库就处于只读状态 , 所有语句将被堵塞 , 包括增删改查、创建表、修改表结构等语句 。
表锁
表锁大家都非常熟悉了 , 执行命令lock tables kaka read kaka2 write直到unlock tables之前 , 其它线程是无法对kaka写kaka2读的 。
执行命令的这个线程也只可以对kaka读 , kaka2写 。
行锁
行锁是在引擎层由各个引擎自己实现的 。 在MySQL中Innodb存储引擎支持行锁 , 若不支持行锁意味着并发控制只能使用表锁 , 对于这种引擎的表 , 同一张表上任何时刻只能有一个更新在执行 , 这就会影响到业务并发度 。 (由于篇幅的原因 , 下期细谈)
二、全局锁演示执行flush tables with read lock命令后数据库处于什么状态 。
终端1执行全局锁命令

端口2执行删除操作 , 它不会直接执行成功 , 而是在端口1解锁后返回 。
这个SQL需要3分钟的执行时间 , 这3分钟就是咔咔打开终端2并连接数据库的时间 。

现在见证了开篇所说的全局锁直接让整个库处于只读状态 , 这里只演示了删除操作其它的几个操作自己尝试一下 。
在蒋老师的文章中看到全局锁最典型的场景是用于逻辑备份 , 即是将整个库的每一个表都select存储成文本 。
现在 , 你想想这种场景是在什么需要下出现的 。
假如只有一个主库 , 执行了全局锁整库处于只读状态 , 那么业务基本停摆 , 产品无法使用 。
此时你会有疑问我在从库上备份啊!备份期间 , 不能执行主库同步过来的binlog的 , 数据量如果非常大 , 将引发主从延迟过大 , 必须进行全量备份 。
以上是全局锁引发的负面情况 , 但再看备份不加全局锁会出现什么问题 。
相信大多数小伙伴都开发过支付类项目 , 接下来就用支付案例让大家很清晰的理解备份不加全局锁引发的问题 。
发起一个逻辑备份 。 如果一个用户在备份期间购买了你公司的服务 , 在业务逻辑先扣除用户余额 , 然后给用户添加你公司对应的产品 。
显然 , 这个逻辑没有问题的 , 但在特殊案例下执行备份操作就会引发问题 。
若在时间顺序上先备份用户余额 , 然后用户发起购买 , 接着备份用户购买的产品表 。
一个非常清晰的问题出现了 , 用户余额没减成功但用户却获得了对应的产品 。
从用户的角度出发那是赚大发了 , 但这种执行顺序如果反过来的话就会产生不一样的结果 。
先备份用户产品表 , 然后备份用户余额表 , 就会出现用户钱花了东西没得着 , 这还得了 , 用户都是衣食父母这不是再割父母的韭菜 。