表a中有4条记录1,3,5,7,开两个sessionmysql
session1:begin; delete <= 7 ; session2:begin; insert a values(2); commit; session1:commit;
这个模型在rc的状况下,这是没问题的,只是加记录锁,不会锁范围,插入2是能够的。最后a上就剩2这个记录sql
那binlog里面记录的内容就有讲究了,假设是statment格式的binlog数据库
insert 2; delete <= 7;
这时候数据库作同步,从库上就gg了,一个事务所作的修改对另外一个事务不可见,好似串行session
这就是不符合隔离性的要求,并行执行和串行执行的结果不同并发
row格式的binlog是下面这样记录的:ide
insert 2; delete 1,delete 3, delete 5, delete 7
这样2就还在性能
因此,必定要用row,row记录的是一条一条记录,而不是简单的sql.因此说rc的状况下设置为row,主从仍是能够保持一致的测试
5.1版本才支持row,主从复制从3.23就开始支持,中间差了4.0,4.1,5.0三个版本,最先为何innodb要支持这样的锁,也有部分缘由是当时的MySQL的复制不用Next-key Lock,就是根本不可用的,由于innodb能够并行的,有并发问题,不像其余存储引擎有表锁优化
tips:code
先讲两句题外话,这个判断的是根据事务id来作的,每条记录有rowid、txid、rowpointer,后面才接用户的列
txid是6个字节的,其实每一个事务分配的,并且是全局自增的,在共享表空间的某个位置存放着当前max的txid,因此每开启一个事务都会分配一个txid
①当前活跃事务列表,里面记录着当前正在执行未提交的事务
begin; xxx 随便什么语句
这时候产生一个read_view的内存对象,如今在mysql里面彻底看不到,能看到的就是下面这个,有多少个read_view开着,咱们经过这个read_view来判断记录的可见性
-------------- ROW OPERATIONS -------------- 0 queries inside InnoDB, 0 queries in queue 0 read views open inside InnoDB Process ID=23137, Main thread ID=139830599059200, state: sleeping Number of rows inserted 116, updated 27, deleted 0, read 130 0.00 inserts/s, 0.00 updates/s, 0.00 deletes/s, 0.00 reads/s
这个read_view就是把事务开始的时候事务活跃列表拷贝一份出来,这时候会拿到不少个事务id,只要你的记录对应的事务id在这个活跃列表中,意味着这个记录不可见,由于这条记录在select这个事务开启的时候还没提交,因此该记录的事务id对新产生的事务是不可见的
②read_view在rc和rr中的区别
rr中read_view只产生一次,rc中每执行一条sql语句就会建立一个read_view
缘由:rc能够读到已经提交的事务的记录,第二次执行还要检查事务活跃列表,若是提交了这条记录就可见,而rr实现了可重复读
若是一个事务执行时间很长,它插入了一条记录,是否可见?就看建立read_view的时候这个tx_id有没有在活跃列表中,若是不在就意味着不可见,那就不会删掉了。
rr的好处,read_view只建立一次,rc要建立n次
若是事务活跃列表很长的话,每次拷贝的时候要锁住全部活跃事务(latch,5.5版本叫kernel_mutex,这个锁很大),须要时间仍是挺长的,但5.6,5.7都开始作优化了(把大锁拆分了)
因此,一个事务中有不少条操做,并且全是select操做(随机主键值查询),这时候rr性能更好,没有insert因此不会有gap锁致使的并发插入影响,这种状况太少了,因此咱们仍是选择rc
begin的时候仍是执行第一条sql的时候?
测一把便知
mysql> select @@tx_isolation; +-----------------+ | @@tx_isolation | +-----------------+ | REPEATABLE-READ | +-----------------+ 1 row in set (0.00 sec) mysql> desc l; +-------+---------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+---------+------+-----+---------+-------+ | a | int(11) | NO | PRI | NULL | | +-------+---------+------+-----+---------+-------+ 1 row in set (0.00 sec) mysql> select * from l; +---+ | a | +---+ | 1 | | 2 | | 3 | +---+ 3 rows in set (0.00 sec)
测试一:
session1: mysql> begin; Query OK, 0 rows affected (0.00 sec) session2: mysql> begin; Query OK, 0 rows affected (0.00 sec) mysql> insert into l values (4); Query OK, 1 row affected (0.00 sec) mysql> commit; Query OK, 0 rows affected (0.00 sec) mysql> select * from l; +---+ | a | +---+ | 1 | | 2 | | 3 | | 4 | +---+ 4 rows in set (0.00 sec) session1: mysql> select * from l; +---+ | a | +---+ | 1 | | 2 | | 3 | | 4 | +---+ 4 rows in set (0.00 sec)
测试二:
session1: mysql> mysql> begin; Query OK, 0 rows affected (0.00 sec) mysql> select * from l; +---+ | a | +---+ | 1 | | 2 | | 3 | +---+ 3 rows in set (0.00 sec) session2: mysql> begin; Query OK, 0 rows affected (0.00 sec) mysql> insert into l values (4); Query OK, 1 row affected (0.00 sec) mysql> commit; Query OK, 0 rows affected (0.00 sec) mysql> select * from l; +---+ | a | +---+ | 1 | | 2 | | 3 | | 4 | +---+ 4 rows in set (0.00 sec) session1: mysql> select * from l; +---+ | a | +---+ | 1 | | 2 | | 3 | +---+ 3 rows in set (0.00 sec)
按道理若是事务隔离级别为rr,那一个事务提交了,对另外一个事务不可见,解决不可重复读,这样看测试二是合理的,
那为何,测试二session1一开始select了一把,session2里面事务提交了,session1就不可见,而测试一session1一开始没有select,后面再select就可见了?这是重复读的体现吗?
缘由:rr隔离级别的时,事务中有select时会建立一个read_view,并且一个事务只建立一次,因此测试一的时候,最后session1 select的时候才建立了read_view,发现session2的事务中相关记录已经commit了,不在事务活跃列表中,因此读到了这条记录,而测试二,session1 开启事务,第一个select的时候就建立了read_view,这时候session2里面的事务还没开启,第二个select的时候用的仍是原来的rv,这样就不可见了
tips:
若是但愿begin的时候就建立read_view
必须用start transaction with consistent snapshot; 结合rr用,由于rc时候,事务中每执行一个sql就会建立read_view
session1: start transaction with consistent snapshot; 建立了rv session2: begin; insert aaa; commit; session1: select aaa;查不到,建立rv的时候,session2中的事务还不存在 若是rc的话能读到aaa,由于第三步session1里执行select又会建立一个rv,会发现aaa这个记录已经提交了,就能看到了