MySQL的众多存储引擎中,只有InnoDB支持事务,所有这里说的事务隔离级别指的是InnoDB下的事务隔离级别。
读未提交:一个事务可以读取到另一个事务未提交的修改。这会带来脏读、幻读、不可重复读问题。(基本没用)
读已提交:一个事务只能读取另一个事务已经提交的修改。其避免了脏读,但仍然存在不可重复读和幻读问题。
可重复读:同一个事务中多次读取相同的数据返回的结果是一样的。其避免了脏读和不可重复读问题,但幻读依然存在。
串行化:事务串行执行。避免了以上所有问题。
以上是SQL-92标准中定义的四种隔离级别。在MySQL中,默认的隔离级别是REPEATABLE-READ(可重复读),并且解决了幻读问题。简单的来说,mysql的默认隔离级别解决了脏读、幻读、不可重复读问题。
不可重复读重点在于update和delete,而幻读的重点在于insert。
事务基本要数(ACID)
1 原子性(ATOMICITY)
事务开始所有操作要么全部做完,要么全部不做,不可能停留在中间环节。事务执行过程中出错要回滚到事务开始前的状态。
2 一致性(CONSISTENCY)
事务开始前和结束后,数据库的完整性约束没有被破坏。比如A向B转账不可能A扣了钱,B却没有收到
3 隔离性(ISOLATION)
同一时间,只容许一个事务请求同一数据,不同事务之间彼此没有任何干扰。
4 持久性 (DURABILITY)
事务并发带来隔离问题
1 脏读:事务A读取事务B正在更新的数据,然后B回滚操作,那么事务A读取到的数据是脏的。
2 不可重复:事务A多次读取同一批数据,事务B在事务A多次读取过程中,对数据做了更新提交操作。导致事务A前后读取数据不一致。
3 幻读:事务A修改数据,事务B插入一条件数据,事务A再读的时候发现还有一笔数据没有修改。
事务是有1条语句和多条语句组成
事务->多条语句(SELECT UPDATE INSERT DELETE)-->每条语句锁定 1条记录或多条记录
MYSQL隔离级别
读未提交 (RU) 可脏读,可幻读,不可重复读
读已提交 (RC) 不可, 可幻读,不可重复读
可重复读(RR) 不可 , 可幻读, 可重复读
串化 (SR) 不可 不可 可
ORACLE 是读已提交 MYSQL 默认是RR级别 可重复读,使用间隙锁解决幻读。
在分布式数据库当中,事务该怎么解决上述事务并发问题呢?
假设有两个分片表,T_USER 和T_ACCOUNT 分别以USERID和ACCOUNT_ID为HASH分片
假设我们有4个分片数据库,按MYCAT说法叫4个数据节点。
比如单语句事务 UPDATE T_USER SET AGE=38 WHERE USER_NAME LIKE '老王%'
比如多语句事务
BEGIN
UDPATE T_ACCOUNT SET MONEY-50 WHERE USER_NAME='老王'
UPDATE T_ACCOUNT SET MONEY+50 WHERE USER_NAME='老王的老婆'
commit;
END;
MVCC
每行有两个隐藏字段 分别是 创建事务号,删除事务号
SELECT:先查询会获得一个事务号,我们叫它为查询事务号
然后会对比 (创建事务号<=查询的事务号 )AND (删除事务号=NULL OR 删除事务号> 查询事务号)
INSERT 把自身的事务号 保存在创建事务号里
UPDATE:先插入一行把自身的事务号保存在创建事务号里,然后把原有的行删除事务号换成自己的事务号
DELETE:把自身的事务号 保存在删除事务号里
快照读和当前读
快照读是读取历史版本,当前读是读最新版本 SELECT 是快照读。UPDATE,DELETE INSERT,SELECT LOCK IN SHARE MODE,SELECT FOR UPDATE 当前读。
锁定读和一致性非锁定读
在一个事务中,SELECT 语句不加锁,除非SELECT LOCK IN SHARE MODE和SELECT FOR UDPATE
SELECT LOCK IN SHARE MODE 给记录加共享锁SR,其它事务只读不能修改,直到本事务提交为止。
SELECT FOR UDPATE 给记录加独占锁,RX 其它事务只能快照读不能当前读。
一致性非锁定读 常说的一致性读
就是为了读的数据前后一致性同时不锁定记录来实现
如果在RR隔离级别下:那么在同一个事务中的所有一致性读都是事务中第一个这样的读读到的快照;
如果在RC隔离级别下:那么一个事务中的每个一致性读都会读到它自己涮新的快照版本
RR和RC下普通SELECT是一致性读
MYSQL 锁
RECORD LOCKS 记录锁 在记录上加锁
GAP LOCKS 间隙锁 在记录之间加锁,或者第一个记录前加锁,最后一个记录后加锁
NEXTKES 下个关键字锁:GAP+RECORD LOCKS
索引
KEY+RECORDS
10,11,13,20 那么这个索引的NEXT-KEY锁将覆盖区间
(NEGATIVE INFINITY,10] 负无穷大
(10,11]
(11,13]
(13,20]
(20,POSITIVE INFINITY) 正无穷大
在默认隔离级别下RR 对普通SELECT采用一致性读不加锁
对UPDATE DELETE则需要加锁,至于加什么锁看情况
1 使用唯一索引作为检索条件 则需要加记录锁
2 没有使用唯一索引,或者使用索引范围扫描 使用GAP 或 NEXTKEY 锁
这里应该包含唯一索引 范围扫描
按MVCC下的增删查改 就不会出现任何问题,幻读和重复读。
不过是错误的 因为事务采用快照读会造成数据丢失。
所以 DML 事务语句 使用当前读,而SELECT采用快照读。
MVCC 一致性读保证了可重复读
间隙锁 保证了不幻读
ORACLE 支持两种隔离级别 RC SR 没有RR
SET TRANSACTION ISOLATION LEVEL READ COMMITTED|SERIALIZABLE
通过设置事务级别SR 可以避免幻读 而不是通过锁来实现。SR读取的数据是事务开始的时间之前的数据
这需要MVCC来支持