learning_notes

学习笔记

View project on GitHub

事务和锁

隔离级别和锁

事务

事务的ACID属性

  • A(Actomicity) 原子性:事务是一个原子操作,通过 undo log 来实现的
  • C(Consistent) 一致性:事务的操作(数据,索引等)保证一致
  • I(Isolation) 隔离性:每个事务的状态对其他事务隔离,通过 (读写锁+MVCC)来实现的
  • D(Durable) 持久性:事务操作永久性,通过 redo log 来实现的

事务的并发问题

  • 脏读:一个事务读到另一个事务未提交的数据
  • 不可重复读:一次事务2次读取数据不一样,强调的是”update”操作的影响
  • 幻读:和不可重复读一样,强调的是”insert”,”delete”操作的影响
  • 更新丢失:多个事务同时编辑,后者覆盖前者的操作,加锁可避免

查看和设置事务级别

select @@tx_isolation;  #查看当前会话隔离级别
select @@global.tx_isolation; #查看系统当前隔离级别
set tx_isolation='read-committed';  #设置当前会话隔离级别
set global transaction isolation level read committed; #设置系统当前隔离级别
[mysqld]transaction-isolation= REPEATABLE-READ #修改my.cnf

事务的隔离级别

  • read uncommitted(读取未提交内容) : 别人随便说说,没实际做
  • read committed(读取提交内容):
  • repeatable read(可重读): 可能产生幻读
  • serializable(可串行化): 强制事务排序

如何实现事务的隔离级别

快照读(Snapshot Read),这种不加锁的读,也是InnoDB高并发的核心原因之一 使用不同的锁策略(Locking Strategy)来实现不同的隔离级别

  1. RU: select语句不加锁
  2. Serializable: 所有select语句都会被隐式的转化为select … in share mode,其它事务的查询可以并发,但增删改就只能阻塞了
  3. RR: 普通select快照读,锁select /update /delete 根据查询条件情况,会选择记录锁,或者间隙锁/临键锁,以防止读取到幻影记录
  4. RC: 普通select快照读,锁select /update /delete 会使用记录锁,可能出现不可重复读;

加锁规则

  1. 原则1:加锁的基本单位是next-key lock,next-key lock是前开后闭区间
  2. 原则2: 查询过程中访问到的对象才会加锁
  3. 优化1:索引上的等值查询,给唯一索引加锁的时候,next-key lock退化为行锁
  4. 优化2:索引上的等值查询,向右遍历时最后一个值不满足等值条件的时候,next-key lock退化为间隙锁
  5. bug:唯一索引上的范围查询会访问到不满足条件的第一个值

锁的类型

  • 乐观锁
  • 悲观锁(共享锁,排它锁) 排它锁又分为:
    1. 记录锁(Record Locks) : select * from t where id=1 for update; 在记录为1上加锁
    2. 间隙锁(Gap Locks): select * from t where id between 8 and 15 for update; 区间加锁 **间隙锁是在可重复读 串行化隔离级别下才会生效的**
    3. 临键锁(next-key Locks): 记录锁和间隙锁的组合
解决死锁
  1. 锁等待,直到超时innodb_lock_wait_timeout=50
  2. 死锁检测,回滚一个事务,innodb_deadlock_detect=on
  3. 尽量将可能造成锁冲突的申请时机往后放
查看运行的事务和锁
SELECT * FROM information_schema.INNODB_TRX; 
SELECT * FROM information_schema.INNODB_LOCKs;
SELECT * FROM information_schema.INNODB_LOCK_waits;