一、事务相关
数据库操作的最小工作单元,是作为单个逻辑工作单元执行的一系列操作;
事务是一组不可在分割的操作集合
Innodb存储引擎支持事务
四大特性:
原子性:同个事务中的操作同时成功或失败
一致性:指的是业务上的一致
隔离性:所有事务之间对数据的操作相互不可见
持久性:对数据的操作能持久保存
事务并发的三大问题:
脏读:一个事务读取到了另个未提交事务的数据
不可重复读:A事务两次读取到了B事务(update/delete)提交的数据导致数据不一致
幻读:和不可重复读差不多只不过B事务为insert操作导致A事务读取的行数不一致
事务的四种隔离级别:
Read Uncommitted(未提交读) --未解决任何并发问题
事务未提交的数据读其他事务也是可见的,会出现脏读
Read Committed(已提交读) -- 解决脏读问题
一个事务开始之后,只能看到已提交的事务所做的修改,会出现不可重复读
Repeatable Read(可重复读) -- 解决不可重复读的问题
在同一个事务中多次读取同样的数据结果是一样的,这种隔离界别未定义解决幻读的问题
Serializable(串行化)
最高的隔离级别,通过强制事务的串行执行
事务隔离级别的实现:
第一种:在读取数据前,对其加锁,阻止其他事务对数据进行修改(LBCC)
第二种:生成一个数据请求时间点的一致性数据快照(Snapshot),并用这个快照来提供一定级别(语句级或事务级)的一致性读取(MVCC)
二、锁的介绍
锁是用于管理不同事务对共享资源的并发访问
锁分为表锁和行锁
表锁和行锁的区别:
锁定粒度:表锁 > 行锁
加锁效率:表锁 > 行锁
冲突概率:表锁 > 行锁
并发性能:表锁 < 行锁
MyISAM支持表锁
InnoDB支持行锁和表锁
InnoDB锁类型:
共享锁(行锁):Shared Locks
排它锁(行锁):Exclusive Locks
意向共享锁(表锁):Intention Shared Locks
意向排它锁(表锁):Intention Exclusive Locks
行锁算法:
记录锁:Record Locks
间隙锁:Gap Locks
临键锁:Next-key Locks
共享锁:
又称读锁,简称S锁,共享锁就是多个事务对于同一个数据可以共享一把锁,都鞥访问到数据,但是只能读不能修改;
加锁释锁方式:
select * from table_name where id=x LOCK IN SHARE MODE;
commit/rollback;
排它锁:
又称写锁,简称X锁,排它锁不能和其他锁并存,如一个事务获取了一个数据行的排它锁,其他事务就不能再获取该行的锁(共享锁、排它锁),只有该获取了排它锁的事务是可以对数据进行读取和修改;
加锁释放锁方式:
自动:delet / update / insert 默认加上X锁
手动:select * from table_name where id = x FOR UPDATE;
commit/rollback;
意向 共享/排它 锁:
意向锁是由数据引擎自己维护的,用户无法手动操作意向锁,在为数据行加上共享锁/排它锁之前,InnoDB会先获取该数据行所在的数据表的对应意向锁
三、锁的原理
其实数据库锁锁住的该行数据对应的索引区间(下面会讲到)
四、行锁算法
假设有个表t字段为id,name 主键是id
数据为:
id name
1 xiaoming
4 xiaohong
7 lilei
这几条数据将数据库表分为了一下几部分
(-∞,1) 1 (1,4) 4 (4,7) 7 (7,+∞)
记录:{1,4,7}
间隙:(-∞,1) (1,4) (4,7) (7,+∞)
临键:(-∞,1] (1,4] (4,7] (7,+∞)
临键锁:
临键锁是InnoDB默认的行锁算法
举例:select * from t where id > 5 for update;
则锁住 id > 5所占有的临键区间 :(4,7] 和((7,+∞)
间隙锁:
记录不存在,退化成间隙锁,间隙锁之间不冲突
举例:select * from t where id > 4 and id < 7 for update 或者select * from t where id=6 for update
则锁住间隙区间:(4,7)
间隙锁只在RR事务隔离级别中存在
记录锁:
当使用等值查询并且该字段为唯一索引时并且查询命中某条数据时退化成记录锁
举例:select * from t where id=4 for update;
则锁住 id=4这条数据
注意:select * from t where name='xxx' for update xxx不存在时锁住该表所有主键索引
利用锁解决并发问题
解决脏读:
修改操作上加上X锁则其他事务无法读取到未提交的数据
解决不可重复读
在查询事务上加上S锁
解决幻读
使用范围查询加上X锁则两次查询的数据行数一致
五、死锁
产生:
多个并发事务(2个或者以上)
每个事务都持有锁(或者是已经在等待锁)
每个事务都需要再继续持有锁
事务之间产生加锁的循环等待,形成死锁
出现死锁之后怎么办:
两个事务相互等待时,当一个等待时间超过设置的某一阈值时,对其中一个事务进行回滚,另一个事务就能继续执行。
在InnoDB中参数innodb_lock_wait_timeout用来设置超时时间默认50S
InnoBD还提供了wait-for graph算法来主动进行死锁检测,每当加锁请求无法立即满足需要进入等待时,wait-for graph算法都会被触发
死锁的避免:
以固定的顺序访问表和行,
将复杂事务拆成多个小事务
在同一个事务中,尽可能做到一次锁定所有资源,减少死锁的概率
降低隔离级别
为表添加合理的索引。如果不走索引将会为表的每一行记录添加上锁,死锁的概率大大增加
在程序中捕获死锁异常
本人QQ/Wechat:806751350
github地址:https://github.com/linminlm