事务
ACID属性(原子性,一致性,隔离性,持久性)
- 并发事务可能带来的问题
- 更新丢失(脏写):事务之间相互覆盖结果
- 脏读:读到其他事务未提交的数据
- 不可重复读:一次事务中,读到其他事务已提交的变更结果
- 幻读:一次事务中,读到其他事务新增的数据
- 事务隔离级别
- 读未提交(READ-UNCOMMITTED)
- 读已提交(READ-COMMITTED)
- 可重复读(REPEATABLE-READ) 没有解决幻读问题,在对新数据执行一次更新操作时,会感知到新增的数据。 可通过间隙锁或修改隔离级别解决幻读问题。
- 可串行化(SERIALIZABLE)
查看/设置事务隔离级别:
mysql> show global variables like 'tx_isolation';
# 读未提交
mysql> set tx_isolation='read-uncommitted';
# 读已提交
mysql> set tx_isolation='read-committed';
# 可重复读
mysql> set tx_isolation='repeatable-read';
# 串行化
mysql> set tx_isolation='serializable';
锁
- 乐观锁和悲观锁
- 乐观锁:通过版本号进行判断数据是否有被修改过,没有锁等待,类似CAS。
- 悲观锁:事务之间会相互等待
- 读锁(共享锁,S锁)和写锁(排他锁,X锁)
- 读锁
- 写锁
- 意向锁(IS锁和IX锁)
意向锁是表级锁且意向锁之间不互斥。当事务加表锁时,需要检查当前表是否存在行锁,意向锁就是为了提高检查效率的,因为加行锁之前,会先加对应的意向锁,如加S锁前,先加IS锁;加X锁前,先加IX锁。所以INNODB支持表锁和行锁是可以共存的。
- 表锁和行锁
- 表锁:一般会在数据迁移的时候使用,保证数据不会变。
- 行锁:锁住单行记录,可能会产生死锁(大部分情况mysql会自动检测到死锁,并回滚产生死锁的事务)。InnoDB的行锁是针对索引的。
select * from users where inx_column=xxx for update;
- 间隙锁
select * from users where column=xxx lock in share mode;
锁住记录之间以及值所在的区间所有的行,在可重复读隔离级别下生效。尽量避免间隙锁。 以下示例中,mysql会锁住(5,10]以及(10, 20]区间的所有行。其中(3,20]这个区间也叫临键锁。
mysql> select * from users;
+----+---------+
| id | name |
+----+---------+
| 4 | heoller |
| 5 | wjy |
| 10 | wjy1 |
| 20 | wjy2 |
+----+---------+
4 rows in set (0.00 sec)
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> update users set name='aaa' where id > 8 and id < 15;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
MyISAM存储引擎不支持事务;执行查询时,会在表上加读锁;非读操作时,会在表上加写锁,且不支持行锁
select for update到底加了哪些锁?
RC隔离级别
- 命中主键索引记录:X锁,IX锁
- 命中唯一索引记录:X锁(唯一索引),X锁(主键索引),IX锁
- 命中普通索引记录:X锁(普通索引),X锁(主键索引),IX锁
- 未命中普通索引记录:IX锁
- 未走索引:X锁(全表扫描命中的主键索引记录),IX锁
RR隔离级别
- 命中主键索引记录:X锁,IX锁
- 命中唯一索引记录:X锁(唯一索引),X锁(主键索引),IX锁
- 命中普通索引记录:X锁(普通索引),X锁(主键索引),IX锁,Gap锁(索引值前后区间)
- 未命中普通索引记录:IX锁,Gap锁(索引值前后区间)
- 未走索引:虚拟全表行锁(锁表)