您的位置:首页 > 数据库 > Oracle

MySQL、Oracle、SqlServer 的默认事务隔离级别,互联网项目中默认使用的事务隔离级别

2020-07-14 06:26 591 查看

Mysql默认的事务隔离级别是可重复读(Repeatable Read)
在Oracle,SqlServer中事务隔离级别都是 读已提交(Read Commited)

为什么Mysql不选择读已提交而选择可重复读?

MySQL的主从复制是基于binlog(记录数据库更改的文件)复制

binlog的格式分为以下三种:

  • statement:记录的是修改SQL语句
  • row:记录的是每行实际数据的变更
  • mixed:statement和row模式的混合

在Mysql 5.0以前,binlog只支持statement格式,这种格式在读已提交(Read Commited)这个隔离级别下主从复制存在问题,因此Mysql将可重复读(Repeatable Read)作为默认的隔离级别。
eg:

此时执行select,master主库中数据为b 3 ;从库查询为 Empty,出现了主从不一致问题,原因是:在master上执行的顺序为先删后插,而此时binlog为STATEMENT格式,它记录的顺序为先插后删,从(slave)同步的是binglog,因此从机执行的顺序和主机不一致,就会出现主从不一致!

解决方案:
1、隔离级别设为可重复读(Repeatable Read), 在该隔离级别下引入间隙锁。当Session 1执行delete语句时,会锁住间隙。那么,Ssession 2执行插入语句就会阻塞住

2、将binlog的格式修改为row格式,此时是基于行的复制,自然就不会出现sql执行顺序不一样的问题,但这个格式在mysql5.1版本开始才引入。由于历史原因,mysql将默认的隔离级别设为可重复读(Repeatable Read),保证主从复制不出问题。

为什么在互联网项目中将隔离级别设为读已提交(Read Commited)

  • 采用读未提交(Read UnCommitted),一个事务读到另一个事务未提交读数据
  • 采用串行化(Serializable),每个次读操作都会加锁,快照读失效,一般是使用mysql自带分布式事务功能时才使用该隔离级别

对比读已提交和可重复读
eg:表结构和存储内容如下:

CREATE TABLE `test` (
`id` int(11) NOT NULL,
`color` varchar(20) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB
数据如下图:
+----+-------+
| id | color |
+----+-------+
|  1 |  red  |
|  2 | white |
|  5 |  red  |
|  7 | white |
+----+-------+

1、在 可重复读 隔离级别下,存在间隙锁,导致出现死锁的几率比 读已提交 大的多
注:在 读已提交 隔离级别下并不是不会出现死锁,只是出现几率比 可重复读 低

此时执行语句:

select * from test where id<3 for update;

在 可重复读 隔离级别下,存在间隙锁,可以锁住(1,2,5)这个间隙,防止其他事务插入数据,而在 读已提交 隔离级别下,不存在间隙锁,其他事务可以插入数据。

2、在 可重复读 隔离级别下,条件列未命中索引会锁表,而在 读已提交 隔离级别下,只锁行

执行更新语句:

update test set color = ‘blue’ where color = ‘white’;

在 读已提交 隔离级别下,其先走聚簇索引,进行全表扫描,但实际中MySQL做了优化,在MySQL Server过滤条件,发现不满足后,会调用unlock_row方法,把不满足条件的记录放锁。

而在 可重复读 隔离级别下,走聚簇索引,进行全部扫描,最后会将整个表锁上,如下所示:

3、在 读已提交 隔离级别下,半一致性读(semi-consistent)特性增加了update操作的并发性,innodb引入了一个概念叫做半一致性 “semi-consistent”,减少了更新同一行记录时的冲突,减少锁等待。

半一致性:一个update语句,如果读到一行已经加锁的记录,此时InnoDB返回记录最近提交的版本,由MySQL上层判断此版本是否满足update的where条件。若满足(需要更新),则MySQL会重新发起一次读操作,此时会读取行的最新版本(并加锁)。
具体情况:此时有两个Session,Session1和Session2

Session1执行 update test set color = ‘blue’ where color = ‘red’;
不提交事务,uncommitted

Ssession2执行 update test set color = ‘blue’ where color = ‘white’;

session 2尝试加锁的时候,发现行上已经存在锁,InnoDB会开启semi-consistent read,返回最新的committed版本(1,red),(2,white),(5,red),(7,white)。MySQL会重新发起一次读操作,此时会读取行的最新版本(并加锁)
在 可重复读 隔离级别下,Session2只能等待

在 读已提交 级别下,不可重复读问题需要解决吗?

  • 不用解决,这个问题是可以接受的,因为你数据已经提交,读出来的数据本身就没有太大问题,Oracle的默认隔离级别就是 读已提交

在 读已提交 级别下,主从复制用什么binlog格式?

  • 在 读已提交 隔离级别下,用的binlog为row格式,是基于行的复制

参考链接:https://www.cnblogs.com/rjzheng/p/10510174.html 如有侵权,联系删除

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: