innodb默认隔离级别可重复读,那么可重复读究竟是什么样的?怎么实现的?
可重复读简单理解就是给当前事务开辟了一个新的空间,主数据源备份到该空间,事务没提交时都是读取该空间的数据,当提交后才继续读取主数据源数据
可重复读的实现:
事务启动时生成一个read view ,其中包含了c_trx_id(后面简写ctrxid),m_ids,min_trx_id,max_trx_id
ctrxid是当前事务的id,mids是当前事务开启之后(还未提交)创建的事务id列表,包含当前ctrxid,mintrxid是列表中最小的trxid,maxtrxid是最大的trxid,注意这个最大应该是当前最大的+1,方便下一个事务开启时用。
例子:
事务a开启 trx_id为100,事务b也开启,trxid为101,此时a事务中mids为100和101,mintrx为a 100,maxtrxid为102,(此时b事务中mids为100和101,mintrx为a 100,maxtrxid为102)
a读取卡数据500万,此时b往里面存了100万,b提交,a第二次读还是500万
a读数据的时候发现此时卡的trxid为101,比自身大,并且比a事务中mintrxid大,此时不能读取trxid为101,往卡的历史trxid中读取,找到修改之前卡的trxid 99,此时是500万,达到了可重复读的功能。
(历史trxid,卡数据记录中有两个隐藏列,trxid和上一个记录的指针,b事务修改之后,卡的trxid为101,数据值为600万,指针索引指向记录的trxid为99,数据值为500万)
例如:
开始事务a后(begin开启)
查询部门no为1的名字为3333,
trxid(事务id):
开启新的事务b,修改no1的部门名称为zhangsan
a事务的trxid:
事务b已提交:数据库查询数据已更改
回到事务a再次进行查询,数据还是3333,实现了可重复读的功能
此时a事务的trxid没有变化
commit后再查询,此时为zhangsan
再次查询事务a的trxid,此时trxid为null
SHOW FULL PROCESSLIST
SELECT * FROM information_schema.INNODB_TRX
UPDATE dept set dname = "1231" WHERE dept_no =2
#开启事务 分配TRX_ID
#begin不会立即分配TRX_ID
begin;
#触发分配TRX_ID
select * from dept WHERE dept_no =1
#指定TRX_MYSQL_THREAD_ID=当前CONNECTION_ID,表示查询当前连接
select TRX_ID from INFORMATION_SCHEMA.INNODB_TRX where TRX_MYSQL_THREAD_ID = CONNECTION_ID();
commit;
当出现行锁时(当前事务未提交,同时修改同一条数据 行锁):
日志输出:
com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException: Lock wait timeout exceeded; try restarting transaction
SHOW FULL PROCESSLIST
SELECT * FROM information_schema.INNODB_TRX
CREATE TABLE `dept` (
`dept_no` bigint(20) NOT NULL AUTO_INCREMENT,
`dname` varchar(60) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '主从数据',
`db_source` varchar(60) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`time` datetime(0) DEFAULT CURRENT_TIMESTAMP,
`deleted` tinyint(2) DEFAULT 0,
`sex` varchar(60) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
PRIMARY KEY (`dept_no`) USING BTREE,
FULLTEXT INDEX `1231`(`dname`, `sex`)
) ENGINE = InnoDB AUTO_INCREMENT = 45 CHARACTER SET = latin1 COLLATE = latin1_swedish_ci COMMENT = '部门表' ROW_FORMAT = Compact;