Mysql数据库的锁和事物

211 阅读3分钟

1 Mysql中有哪些锁

根据加锁的范围,MySQL里面的锁大致可以分成全局锁、表级锁和行锁三类

全局锁 全局锁就是对整个数据库实例加锁。加锁的语句是FTWRL

Flush tables with read lock 

当你需要让整个库处于只读状态的时候,可以使用这个命令,之后其他线程的以下语句会被阻塞:数据更新语句(数据的增删改)、数据定义语句(包括建表、修改表结构等)和更新类事务的提交语句。

全局锁使用场景 做全库逻辑备份

官方自带的逻辑备份工具是mysqldump。当mysqldump使用参数–single-transaction的时候,导数据之前就会启动一个事务,来确保拿到一致性视图。而由于MVCC的支持,这个过程中数据是可以正常更新的。

1.1 行锁

image.png 上图事物A获取的两个行锁不是使用完就释放,而是提交后才释放。

两阶段锁 在InnoDB事务中,行锁是在需要的时候才加上的,但并不是不需要了就立刻释放,而是要等到事务结束时才释放。这个就是两阶段锁协议。

知道了两阶段锁这个概念,对我们使用事务有什么帮助呢?那就是,如果你的事务中需要锁多个行,要把最可能造成锁冲突、最可能影响并发度的锁尽量往后放。我给你举个例子。

假设你负责实现一个电影票在线交易业务,顾客A要在影院B购买电影票。我们简化一点,这个业务需要涉及到以下操作:

  1. 从顾客A账户余额中扣除电影票价;
  2. 给影院B的账户余额增加这张电影票价;
  3. 记录一条交易日志。

这样我们把 “给影院B的账户余额增加这张电影票价;”这个操作放在执行语句的后面。可以使得这个锁在事务中停留的时间变短。

2 死锁和死锁检测

当并发系统中不同线程出现循环资源依赖,涉及的线程都在等待别的线程释放资源时,就会导致这几个线程都进入无限等待的状态,称为死锁。

image.png 上图这种情况事务1占有id=1的行锁并尝试获取id=2锁,事务2反过来占有id=2行锁尝试获取id=1的行锁。

出现死锁有两种处理策略

  1. 等待,直到超时。这个超时时间可以通过参数innodb_lock_wait_timeout来设置。
  2. 发起死锁检测,发现死锁后,主动回滚死锁链条中的某一个事务,让其他事务得以继续执行。将参数innodb_deadlock_detect设置为on,表示开启这个逻辑。

在InnoDB中,innodb_lock_wait_timeout的默认值是50s。业务线程正常处理最快也要在死锁发生50s后。显然不能被接受,但是设置太短又会误伤只是单纯锁等待的场景。因此一般采用第二种策略。