事务和锁
事务
事务的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)来实现不同的隔离级别
- RU: select语句不加锁
- Serializable: 所有select语句都会被隐式的转化为select … in share mode,其它事务的查询可以并发,但增删改就只能阻塞了
- RR: 普通select快照读,锁select /update /delete 根据查询条件情况,会选择记录锁,或者间隙锁/临键锁,以防止读取到幻影记录
- RC: 普通select快照读,锁select /update /delete 会使用记录锁,可能出现不可重复读;
锁
加锁规则
- 原则1:加锁的基本单位是next-key lock,next-key lock是前开后闭区间
- 原则2: 查询过程中访问到的对象才会加锁
- 优化1:索引上的等值查询,给唯一索引加锁的时候,next-key lock退化为行锁
- 优化2:索引上的等值查询,向右遍历时最后一个值不满足等值条件的时候,next-key lock退化为间隙锁
- bug:唯一索引上的范围查询会访问到不满足条件的第一个值
锁的类型
- 乐观锁
- 悲观锁(共享锁,排它锁)
排它锁又分为:
- 记录锁(Record Locks) : select * from t where id=1 for update; 在记录为1上加锁
-
间隙锁(Gap Locks): select * from t where id between 8 and 15 for update; 区间加锁 **间隙锁是在可重复读 串行化隔离级别下才会生效的** - 临键锁(next-key Locks): 记录锁和间隙锁的组合
解决死锁
- 锁等待,直到超时
innodb_lock_wait_timeout=50
- 死锁检测,回滚一个事务,
innodb_deadlock_detect=on
- 尽量将可能造成锁冲突的申请时机往后放
查看运行的事务和锁
SELECT * FROM information_schema.INNODB_TRX;
SELECT * FROM information_schema.INNODB_LOCKs;
SELECT * FROM information_schema.INNODB_LOCK_waits;