Mysql事务的串行化

串行读特别简单,就当成是对同一个批数据不允许并发操作,按顺序更新,插入,或者删除。

那么问题来了,同一批数据该如何界定?这是个问题。之前的看图说话中,似乎还没提到这个问题。

先看串行读,事务开启,先事务A select,然后事务B进行 update,注意,此时事务B是不可能马上提交成功的,要等事务A 提交后,事务B才会提交,这就是串行。(如果不start transaction,意味着未开启事务,那么就是自动提交,自动提交也是事务)

先看图说话:

事务A (下图B更新+1,A也更新+1)

下图为事务B 因为等待事务A的提交而超时。

基本上三种隔离级别都实验了。

对于开篇提出的疑问,“一批数据”指的到底是什么,经过实验,三种隔离级别里,都是对整张被查询的表进行隔离,而不管有没有where条件。这个隔离级别跟表锁和行锁不是一回事,莫混淆。

此时此刻应该再搞搞MVCC了。

Mysql事务的可重复读

Repeatable Read 即 可重复读。Mysql默认的隔离级别。

理解了不可重复读,再来看可重复读就简单的多,也就是不管其它事务B是否修改了该数据,事务A内读多次该数据都是一致的,当然有个前提就是事物A在多次读之前都没有对数据进行修改,插入,删除的操作,否则怎么会一致呢。

还有个小提示就是:事务A开启后,如果还没有执行任何语句,事务B进行了更新操作,那么事务A的第一次select结果一定是事物B操作后的。

看图说话:

隔离级别

下图事务A读两次,第一次读bill有1000块钱,红框表示事务B进行了update操作。

事务A

事务B更新

事务A第二次读的结果bill的钱仍是1000,与事务B更新后读出的结果不同。

事务A里读了两次都是1000,即可重复读。

可是,如果事务B更新为999块钱时,事务A进行更新操作把bill的钱减1,然后事务A再次读取,会显示Bill有多少钱呢,999还是998?在事务A执行中间,事务B进行了插入或者删除操作呢?

看图继续:

事务B

事务A

从上图可知,事务B的插入和删除,对事务A依然是不可见的,即不影响事务A的多次读的结果,也就是说仍然是可重复读即数据一致。

然而,一旦事务A有修改操作,就会在事务B更新的结果上进行修改,再次读,就会读出更新后的结果。

这么看,多次读的结果是否是一致的,非很多场景,依赖于其它事务干了什么,依赖于本事务干了什么,不可笼统的讲。

这个场景,如果拿出来面试,血流成河。

Mysql事务的不可重复读

事务的ACID属性中的I是Isolation,言外之意就是多个事务之间的数据隔离问题,所以在谈论事务时一定是在多个事务即一般意义上是多个数据库连接同时操作的数据库的情况下。单个事务内是没有isolation概念的,即单个事务内的数据无隔离性,全可视。

四种隔离级别中的

第一种:Read Uncommitted,几乎无实际用处,请忽略。

第二种:Read Committed也叫做Non Repeatable Read,即不可重复读。

这个级别是多数数据库的默认的隔离级别,但是MYSQL不是。

不可重复读?指的是什么?

首先要搞清楚,是谁第一次读、第二次读?是指在一个事务内的两次select,也就是在一个数据库链接中操作。

为何叫不可?是因为这个级别的事务A开启后,第一次select后,另一个事务B有update这条记录的操作,并且commit了,再之后这个事务A再次select,得到的结果和第一次select的结果不同,这就是不可重复读。

下面我们看下实验(mysql版本5.6.17):

表结构

表数据

设置隔离级别

事务A先select(红框上面),然后事务B(另一个数据库连接)update了记录为999(红框表示有事务B执行).

事务A:

事务B:

然后事务A再次执行select(红框下面),读出的结果与第一次不同(bill第一次有1000,第二次有999,因为两次读中间,事务B修改了bill的钱)。

这就是不可重复读,但是只是update情况,那么insert和delete情况如何?继续看下。

事务B中进行insert操作,然后事务A读一下,然后事务B再次delete,然后事务A再读。

事务B

事务A

可见,这个级别是彻底的不可重复读。

如果事务A中,先读,事务B更新,事物A再更新同一条记录的同一个字段,然后事务A再读该字段,会出现什么情况?这个字段的值该如何变化?

这个情形,不可重复读和可重复读是一致的,见下一篇《Mysql事务的可重复读》。

mysql 存储过程批量建表

BEGIN 
	DECLARE `@i` int(11);     
	DECLARE `@createSql` VARCHAR(2560); 
	DECLARE `@createIndexSql` VARCHAR(2560);     
		DECLARE `@tableName` varchar(32);

	set @i=0; 
	WHILE  @i< 16 DO
		IF @i<10 THEN
			SET @tableName=CONCAT('msg_message_0',@i);
		ELSE
			SET @tableName=CONCAT('msg_message_',@i);
		end IF;

		SET @createSql = CONCAT('CREATE TABLE IF NOT EXISTS ',@tableName,'(
			`ID` int  AUTO_INCREMENT PRIMARY KEY ,
			`OWNER` varchar(32) NOT NULL COMMENT \'ERP or VenderId\',
				`CONTENT` varchar(255) NOT NULL COMMENT \'消息内容\',
				`OWNER_TYPE` tinyint(4) NOT NULL COMMENT \'1 for ERP,2 for VenderId\',
				`CHANNEL` tinyint(4) default 0 COMMENT \'综合,频道,盘货,无线等\',
				`TYPE` tinyint(4) default 0 COMMENT \'1 for 驳回,2 for 通过等\',
				`CREATED` datetime COMMENT \'创建时间\',
				`MODIFIED` datetime COMMENT \'修改时间\',
				`STATUS` tinyint(4) DEFAULT 0 COMMENT \'0未读,1已读\' );'
			 ); 
				prepare stmt from @createSql; 
				execute stmt;                             
		
				-- 创建索引    
				set @createIndexSql  = CONCAT('create index `index_owner_owner_type` on ',@tableName,'(`OWNER`,`OWNER_TYPE`);');
				prepare stmt from @createIndexSql; 
				execute stmt; 
		SET @i= @i+1; 
    END WHILE;
END