这是我参与「第五届青训营 」伴学笔记创作活动的第 8 天
高并发数据安全性:
高并发下线程安全问题,利用锁来解决这个问题。
锁的由来:
客户端向mysql发送的一条sql可以理解为一个但是事务,事务基于数据库连接,需要一个工作线程来维护,多个事务就是并发事务,需要处理脏读,不可重复读,幻读等问题,锁机制主要用来处理并发线程的数据安全性问题,事务线程在操作同一表过程中的才会出现数据并发问题。
锁机制的分类:
锁由存储引擎复杂是新,锁体系:
以锁粒度进行划分:
- 表锁:
-
- 全局锁:上锁全数据库,只允许读
- 元数据锁/MDL锁:基于表的元数据加锁,上锁后整张表不允许其他事务操作
- 意向锁:
- 自增锁:
- 页面锁:
- 行锁:
-
- 记录锁/Record锁:也就是行锁
- 间隙锁/Gap锁:InnoDB中解决幻读的一种锁机制
- 临建锁/Next-Key锁:间隙锁升级,同时具备记录锁和间隙锁的功能。
以互斥性的维度划分:
- 共享锁/S锁:不同事务之间不会互斥,可以同时获取锁。
- 排他锁/X锁:不同事务之间互相排斥,同时只能允许一个事物获取锁。
以操作类型的维度划分:
- 读锁:查询数据使用的锁
- 写锁:执行插入,删除,修改,DDL语句使用的锁
以加锁方式的维度划分:
- 显示锁:编写SQL,手动指定加锁的粒度
- 隐式锁:执行SQL,根据隔离界别自动为SQL加锁。
以思想维度进行划分:
- 乐观锁:每次执行前认为自己成功,先尝试执行,失败后再获取锁。
- 悲观锁:每次执行都认为自己会失败,先获取再执行。
共享锁和排他锁
共享锁/S锁:读操作之间共享锁,写操作会出现排斥行为。
排他锁/X锁:也叫做独占锁,也就是数据只能由一个线程进行操作,对于事务并行操作进行会进行排斥。
锁的释放:
锁的释放时隐式的。释放的时机时不同的。
表锁:
一张表只能够有一个同一类型的表锁,不同存储引擎上表锁方式不同。
select*from zz_users forupdate;
InnoDB支持多种锁机制,基于存储引擎的,基于聚簇索引实现的,执行SQL命中数据就是行锁,没有命中数据则加表锁。
MyISAM不支持聚簇索引,所以无法通过这种机制进行上锁,
LOCK TABLES table_name READ;
LOCK TABLES table_name WRITE;
MyISAM中,获取读锁和写锁Read,Write,对应具有共享和排他特性,这就是共享锁和排他锁翻译为读锁和写锁的由来。
- MyISAM中获取了锁,还需要自己手动释放锁,否则死锁,
元数据锁:
MDL锁主要作用是维护表元数据的数据一致性,在表上有活动事务(显式或隐式)的时候,不可以对元数据进行写入操作。修改表结构的时候使用,这时候不许进行其他操作,所以是要给表锁。
意向锁:
一个事务操作一条数据,另一个事务给表上写锁,难道需要遍历表看是否含有写锁吗,错误这时候只需要有一个意向锁,表示表中有数据被上锁了就可以,这就是意向锁,更加像一个概念。
自增锁:
专门为了自增ID的并发性能生成的,自增锁主要负责维护并发事务下自增列的顺序,防止并发插入ID重复,使用一种轻量级锁Mutex-Lock来防止自增值重复分配。
全局锁:
特殊的表锁,对整个数据库进行上锁,只需读,不许写。
行锁:
很多数据操作只会影响到行,锁的范围不用到表,于是就使用行锁:
记录锁:
实际上就是行锁,锁定一条数据,InnoDB行锁基于索引实现。
-- 获取行级别的 共享锁
select * from `zz_users` where user_id = 1 lock in share mode;
-- 获取行级别的 排他锁
select * from `zz_users` where user_id = 1 for update;
间隙锁:
用于解决幻读问题,例如主键4-9中间没有数据,间隙锁就是锁定中间区域,上锁满足左右开区间的规则,其他事务插入间隙锁的区间会阻塞。
临键锁:
临键锁是间隙锁的Plus版本,或者可以说成是一种由记录锁+间隙锁组成的锁。
- 记录锁:包含真实数据
- 间隙锁:包含空白数据
结合后就是,左闭右开的区间。
插入意向锁:
插入意向锁,听起来似乎跟前面的表级别意向锁有些类似,但实际上插入意向锁是一种间隙锁,这种锁是一种隐式锁,也就是咱们无法手动的获取这种锁。再间隙锁和临键锁的时候插入进入阻塞,等待过程中插入一个插入意向锁,表示有数据进行等待,再同样位置插入数据就会异常。不会排斥其他事务,也是一种共享锁
页面锁,乐观锁,悲观锁
页面锁:
这是Berkeley DB的一种锁,很少用。
以也为单位,锁住一页。
乐观锁:
乐观锁:每次执行都认为只会有自身一条线程操作,因此无需拿锁直接执行。一般都是基于CAS思想实现的,而在MySQL中则可以通过version版本号+CAS的形式实现乐观锁,也就是在表中多设计一个version字段。
悲观锁:
每次执行时都会加锁再执行