consistent non-locking read,经过行多版本控制的方式读取当前执行时间点的记录mysql
默认状况下innodb select没有任何锁,读到的记录在更新就经过undo读以前版本,serializable时候读会被阻塞,由于它默认加一个lock in share mode
--->like oraclegit
原理
undo && read_view
经过read_view判断一条记录是否可见,不可见(在更新被锁住)就经过undo回滚到以前版本,以前的版本再读trx_id,还不可见再回滚,rc只要回滚一个版本,rr可能要回滚不少版本,最大trx_id是持久化的,保存在共享表空间中github
其实理解下就是事务在不在活跃列表中,在的话这个事务对记录作的动做就不可见须要找记录的前镜像(rc老是读最新的非锁定版本,rr老是读最老的非锁定版本,后续会有专门的文章说明)sql
举个栗子数据库
session1: (root@localhost) [test]> select * from t; +------+ | a | +------+ | 1 | +------+ 1 row in set (0.00 sec) (root@localhost) [test]> begin; Query OK, 0 rows affected (0.00 sec) (root@localhost) [test]> update t set a = a + 1 where a = 1; Query OK, 1 row affected (0.01 sec) Rows matched: 1 Changed: 1 Warnings: 0 (root@localhost) [test]> select * from t; +------+ | a | +------+ | 2 | +------+ 1 row in set (0.00 sec) session2: (root@localhost) [test]> begin; Query OK, 0 rows affected (0.00 sec) (root@localhost) [test]> select * from t; +------+ | a | +------+ | 1 | +------+ 1 row in set (0.00 sec)
第一个会话开启事务更新记录,不提交,此时记录是被锁住的编程
新开一个会话去select 这条记录,并不会由于有锁而阻塞,读到的是原来的记录session
此时commit以后,以前版本的undo是不能被立刻回收的,由于其余线程可能还在引用以前版本的undo,真正的回收undo是purge线程作的mvc
purge的做用是删除undo,真正删除一条记录(完成update和delete)oracle
delete from table where pk=1; 在page中只是标记为删除,page上并无真正的删除 相关参数:innodb_purge_threads 默认是1,5.7中设大一点,4或者8,都是ssd性能比较好
5.5以前全部的purge操做都是master thread作的分布式
默认只有一个purge thread
innodb_purge_threads={0|1}5.6
N purge thread
innodb_purge_threads={4}
1024个槽------1024个undo回滚段,每一个槽对应不一样的undo日志
一旦事务提交,undo就放到hitory list中
tips:
由于记录不是有序的,因此purge操做须要大量离散读取操做
undo不断增大,不能有效回收,致使系统空间不断增大,
最主要的缘由有两个:
索引没有添加
检查slow log存在大事务
拆大为小
其实就一点,一个事务执行时间很长,那对应的undo就不能回收,至少要commit完成后才能回收
另外回滚比提交慢很是多,commit很快,rollback须要的时间就是事务执行的时间,逻辑回滚
tips:
目前MySQL已经支持在线回收undo,详见阿里数据库内核月报
以前咱们谈到binlog和redo的一致性是经过一个内部的xa事务保证的,这里简单聊下外部的分布式事务
(root@localhost) [test]> xa start 'a'; -- 开启一个分布式事务 Query OK, 0 rows affected (0.00 sec) (root@localhost) [test]> insert into t values(2000); Query OK, 1 row affected (0.09 sec) (root@localhost) [test]> insert into t values(3000); Query OK, 1 row affected (0.00 sec) (root@localhost) [test]> xa end 'a'; -- 结束 Query OK, 0 rows affected (0.00 sec) (root@localhost) [test]> xa prepare 'a'; -- 写prepare Query OK, 0 rows affected (0.03 sec) (root@localhost) [test]> xa recover; -- 看一眼,有一个分布式事务 +----------+--------------+--------------+------+ | formatID | gtrid_length | bqual_length | data | +----------+--------------+--------------+------+ | 1 | 1 | 0 | a | +----------+--------------+--------------+------+ 1 row in set (0.00 sec) (root@localhost) [test]> xa rollback 'a'; -- 回滚 Query OK, 0 rows affected (0.01 sec) (root@localhost) [test]> xa recover; -- 再看下,没了 Empty set (0.00 sec)
这是再单实例上模拟的,意义不大
真正应用程序中两个实例作分布式事务,须要两边的prepare都成功才能最终提交
create procedure load1(count int unsigned) begin declare s int unsigned default 1; declare c char(80) default repeat('a',80); while s <= count do insert into t1 select NULL,c; set s = s+1; end while; end call load1(1000)
上面这个存储过程的调用,auto commit致使了insert会处罚一千次fsync
正确姿式:
begin; call load1(1000) commit;
事务拆大为小,缘由就是binlog在搞鬼,其实不必定是大事务,大的操做都要拆吧
计算利息,拆了批量执行
update account set account_total = account_total + (1 + interest_rate)
为何要拆?老生常谈的、
题外话:
binlog是有点讨厌不像oracle用redo,历史缘由,很差说
也有好处,作大数据平台集成很是简单,把MySQL的的数据实时推到大数据平台上太简单,github上一搜一大把项目直接用