锁
锁的类型
- 共享锁(读锁,S锁)
仅允许[所有人]读,[会话外]如需写、更新要等待[会话退出],[会话内]如需写、更新会引发mysql报错
- 互斥锁(写锁,X锁)
仅允许[会话内]读写更新,[会话外]如需写、更新要等待[会话退出]
读读不互斥,读写互斥,写写互斥
锁的粒度
- 数据库实例锁
对整个数据实例加锁, mysql 提供了 Flush tables with read lock (FTWRL) 命令可以对数据库实例加全局读锁,让整个库处于只读状态,DDL,DML都会被阻塞,可以用 unlock tables 解锁,加锁客户端断开的时候也会自动解锁。
- 表锁
LOCK TABLES table_name [READ | WRITE]
为当前线程锁定表
UNLOCK TABLES
释放被当前线程持有的任何锁。当线程发出另外一个LOCK TABLES时,或当服务器的连接被关闭时,当前线程锁定的所有表会自动被解锁
另一个表级锁是 MDL 锁( Metadata Lock、元数据锁),MDL 锁不需要显示的加上,当对一个表做 DML (Data Manipulation Language 增删改)时,自动加 MDL 读锁,当对表做 DDL( Data Definition Language 表结构变更)时,自动加 MDL 写锁。
MyISAM 引擎也支持表锁,执行查询语句前会自动给表加读锁,在执行更新操作前,会自动给表加写锁
默认情况下,写锁比读锁具有更高的优先级:当一个锁释放时,这个锁会优先给写锁队列中等候的获取锁请求,这也正是 MyISAM 表不太适合于有大量更新操作和查询操作应用的原因,因为,大量的更新操作会造成查询操作很难获得读锁,从而可能长时间读阻塞。
- 行锁
InnoDB 引擎支持行锁,共享锁(S)和 独占锁(X),执行更新操作时,会自动加 X 锁,对于普通的查询语句,InnoDB 不会主动加任何锁,可以显示的加锁。
select * from t where ... lock in share mode // 加共享锁
select * from t where ... for update // 加独占锁
InnoDB 的行锁是加在索引上的,如果我们的更新没有走索引导致扫描全表,就会锁全表,锁的是主键索引。
锁的方式
- 乐观锁
乐观锁是一种无锁的思想,假设并发冲突总是不会发生,提交时检查数据一致性,如果一致性被破坏则放弃提交,更新时带着 version 就是乐观锁
- 悲观锁
假设并发冲突一定会发生,每次操作前都会拿锁,通过锁的互斥顺序执行来控制并发,可以认为数据库中的锁都是悲观锁