前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >mysql-选择使用Repeatable read的原因

mysql-选择使用Repeatable read的原因

作者头像
用户7353950
发布2022-06-23 16:28:04
5830
发布2022-06-23 16:28:04
举报
文章被收录于专栏:IT技术订阅IT技术订阅

问题背景

在mysql调优的过程中发现,mysql的默认隔离级别是可重复读(repeatable read),其他几类关系型数据库pg,以及sybase,oracle,sqlserver的默认的隔离级别都是读已提交(read committed)。

我们都知道隔离级别一共有四种,读未提交,读已提交,可重复读,序列化。隔离级别越高,并发性能也就越低。

疑问

1、那么mysql为什么要选择使用可重复读来作为默认的隔离级别呢?

2、可重复读,会带来哪些问题?

3、我们在开发过程中是否要修改默认值,将其改为我们常见的读已提交呢?

  • 可重复读(Repeatable Read),简称为RR;
  • 读已提交(Read Commited),简称为RC;

四种隔离级别

首先我们了解下四种隔离级别的区别。

  • READ UNCOMMITTED :未提交读
    • 读取未提交内容,所有事务可看到其他未提交事务的结果,很少实际使用
    • 读取未提交的数据称为脏读(Dirty Read)
  • READ COMMITTED :提交读
    • 多数数据库的默认隔离级别(MySQL默认不是,默认为REPEATABLE-READ)
    • 满足隔离的简单定义:一个事务只能看到已提交事务所做的改变
    • 这种隔离级别,支持所谓的不可重读(Non-repeatable Read),同一事务的其他实例在该实例过程中可能有新commit,所以同一个select可能返回不同结果(同一个事务如何做到其他实例?
  • REPEATABLE READ :重复读
    • 可重复读(MySQL默认事务隔离),但可能出现幻读(Phantom Read)
    • 幻读(Phantom Read):当用户读取某范围数据行时,另一事务在此范围内插入新行,当用户再次读取此范围数据行时,读取到新的幻影行
    • InnoDB通过多版本并发控制MVCC机制解决该问题
    • PS:新版MySQL采用Next-Key锁来解决幻读问题
  • SERIALIZABLE :串行化
    • 最高隔离级别,强制事务排序(串行化),不会互相冲突
    • 每个读数据航增加共享锁
    • 此级别,可能导致大量超时现象和锁竞争

按事务隔离级别来说,级别越低数据一致性保障效果越差,而并发能力则越强。

为什么选择REPEATABLE READ?

mysql为什么选择使用可重复读来作为默认的隔离级别呢?

查了下文档,发现是有历史原因的,这和mysql的复制有关系,mysql的复制基于binlog,在配置文件中我们可以发现有一个参数binlog_format,binlog有三种格式

代码语言:javascript
复制
# binary logging format - mixed recommended
binlog_format=rowstatement:记录的是修改SQL语句row:记录的是每行实际数据的变更    
mixed:statement和row模式的混合  

在mysql5.0以前binlog只支持statement这种格式,这种格式在读已提交(read commited)这个隔离级别下主从复制是有bug的。

产生bug的原因如下:在主库上面执行先删除后插入,但是在从库如果binlog为statement格式,记录的顺序就是先插入后删除,从库执行的顺序和主库不一致,最后主库有数据,从库的数据被删掉了。

因此mysql将可重复读(repeatable read)作为默认的隔离级别!

当然在可重复读上面也有解决方案

一是使用间隙锁,当session 1执行delete语句的时候,锁住间隙,session 2就会被阻塞

二是将binlog_format设置为row格式,基于行的复制,就不会出现sql执行顺序不一样的问题。但是这个格式是mysql5.1以后才有的。由于历史的原因,mysql将默认的隔离级别设置为可重复读,并一直延续了下来,保证主从复制不出问题。

可重复读,会带来哪些问题?

1、隔离级别越高,并发能力越低。

2、在可重复读级别下,如果使用间隙锁的方式,那么导致死锁的几率比读已提交大的多。

代码语言:javascript
复制
select *from test where n_id <  for update在可重复读级别下,可以锁住间隙,防止其他事务插入数据。
在读已提交级别下,不会影响插入,其他事务任然可以插入数据。

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

代码语言:javascript
复制
INSERT INTO order_record SELECT
    * FROM
    order_today 
WHERE
    pay_success_time < '2020-03-08 00:00:00';
    
在可重复读的隔离级别下,执行上面的sql,会对order_record加表锁,order_today逐步锁(扫描一个锁一个)
当pay_success_time没有索引,或者因为其它原因导致没有走锁定的时候order_today就会被锁住。

测试:

代码语言:javascript
复制
mysql> show profiles;Empty set (0.00 sec)--默认隔离级别 REPEATABLE-READ  mysql> select @@global.tx_isolation,@@session.tx_isolation;+-----------------------+------------------------+| @@global.tx_isolation | @@session.tx_isolation |+-----------------------+------------------------+| REPEATABLE-READ       | REPEATABLE-READ        |+-----------------------+------------------------+ row in set (0.00 sec)--测试1 使用默认的binlog_formatmysql> select @@binlog_format;+-----------------+| @@binlog_format |+-----------------+| STATEMENT       |+-----------------+ row in set (0.00 sec)--session 1 mysql> insert into test2 select * from test limit ;
Query OK,  rows affected,  warning (31.10 sec)
Records:   Duplicates:   Warnings: --session 2 可以看到插入test的数据会一直等待session 1执行完成才能插入mysql> insert into test values('4028e481513e66bc015156c3e359001a','1','1');
Query OK,  row affected (29.48 sec)--测试2 将binlog_format设置为rowmysql> set global binlog_format='row';
Query OK,  rows affected (0.00 sec)

mysql> select @@binlog_format;+-----------------+| @@binlog_format |+-----------------+| ROW             |+-----------------+ row in set (0.00 sec)-- session 1mysql>  insert into test2 select * from test limit ;
Query OK,  rows affected (32.09 sec)
Records:   Duplicates:   Warnings: --session 2 可以看到session 1不会阻塞session 2 的插入mysql> insert into test values('4028e481513e66bc015156c3e359001a','1','1');
Query OK,  row affected (0.00 sec)--测试3 将binlog_format设置为mixedmysql> set global binlog_format='mixed';
Query OK,  rows affected (0.00 sec)

mysql> select  @@binlog_format;+-----------------+| @@binlog_format |+-----------------+| MIXED           |+-----------------+ row in set (0.00 sec)--session 1mysql>  insert into test2 select * from test limit ;
Query OK,  rows affected (32.16 sec)
Records:   Duplicates:   Warnings: --session 2 ,session 1会阻塞session2的插入mysql> insert into test values('4028e481513e66bc015156c3e359001a','1','1');
Query OK,  row affected (30.71 sec)


mysql在默认隔离级别可重复读(REPEATABLE-READ)时,binlog_format设置为statement和mixed都会阻塞,设置为row模式时不会阻塞。

insert into select 有两种阻塞方式,一种是逐行锁住,一种是全表锁住。详情

insert into select引起的问题

解决这个阻塞的方式有两种:一是可以先把查询出来的数据落地,然后在还原到另外一张表。或者将binlog_format改为row

最好的方式是使用读已提交的模式,并且将binlog_format设置为row

另外。在mysql中设置隔离级别为读已提交时,binlog_format如果设置为statement插入数据的时候会报错:

代码语言:javascript
复制
--默认binlog_format=statement会报错mysql> create table test3(id int)engine=innodb;
Query OK,  rows affected (0.16 sec)

mysql> insert into test3 values();
ERROR  (HY000): Binary logging not possible. Message: Transaction level 'READ-COMMITTED' in InnoDB is not safe for binlog mode 'STATEMENT'--设置binlog_format=row无问题mysql> set session binlog_format=row;
Query OK,  rows affected (0.00 sec)

mysql> insert into test3 values();
Query OK,  row affected (0.06 sec)--设置binlog_format=mixed无问题mysql> set session binlog_format=mixed;
Query OK,  rows affected (0.00 sec)

mysql> insert into test3 values();
Query OK,  row affected (0.07 sec)
报错的原因是因为read committed可能会导致不可重复读,也就是说可以读取到后面进入并提交的数据,如果基于STATEMENT格式的话,会导致主从数据不一样,因为STATEMENT是基于SQL语句的复制模式。

使用读已提交的时候,binlog_format只能设置为row或者mixed。建议使用row

总结

1、mysql为什么选择使用可重复读来作为默认的隔离级别?

原因是在mysql5.0以前binlog只支持statement这种格式,这种格式在读已提交(read commited)这个隔离级别下主从复制是有bug的,因此mysql将可重复读(repeatable read)作为默认的隔离级别!

2、可重复读会带来那些问题?

1)、隔离级别越高,并发能力越低。

2)、在可重复读级别下,如果使用间隙锁的方式,那么导致死锁的几率比读已提交大的多。

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

3、是否可以将mysql的默认隔离级别改为读已提交(read commited)

这个是可以的,在修改隔离级别为读已提交的同时,将binlog_format修改为row,可以提高并发能力。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-06-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 IT技术订阅 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 问题背景
  • 疑问
  • 四种隔离级别
  • 为什么选择REPEATABLE READ?
  • 可重复读,会带来哪些问题?
  • 总结
相关产品与服务
云数据库 SQL Server
腾讯云数据库 SQL Server (TencentDB for SQL Server)是业界最常用的商用数据库之一,对基于 Windows 架构的应用程序具有完美的支持。TencentDB for SQL Server 拥有微软正版授权,可持续为用户提供最新的功能,避免未授权使用软件的风险。具有即开即用、稳定可靠、安全运行、弹性扩缩等特点。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档