AD_TRY_SEMI_CONSISTENT;
// 至此,InnoDB的fetch next针对semi-consistent read的处理完毕
上面提到的是semi-consistent read的功能实现,除此之外,MySQL针对semi-consistent read,还做了优化措施:对于update scan返回的不满足条件的记录,提前放锁。
MySQL Server层流程:
sql_update.cc::mysql_update()
// 判断当前scan返回的记录,是否满足update的where条件
? ? // 若满足,则进行update操作
if (!(select && select->skip_record())
…
// 若不满足update的where条件,则选择将当前记录上的行锁提前释放
else
table->file->unlock_row();
InnoDB Engine层流程:
ha_innobd.cc::unlock_row();
switch (prebuilt->row_read_type)
// 若系统未设置参数innodb_locks_unsafe_for_binlog,同时隔离级别大于
? ? // TRX_ISO_READ_COMMITTED,则不可提前释放不满足条件的行锁
? ? // 否则可以提前释放不满足条件的行锁
case ROW_READ_WITH_LOCKS:
if (!srv_locks_unsafe_for_binlog &&
prebuilt->trx->isolation_level > TRX_ISO_READ_COMMITTED)
break;
// 若当前系统已采用SEMI_CONSISTENT read,但是没有锁等待,加锁直接成功
? ? // 那么此时直接释放不满足条件的行锁
case ROW_READ_TRY_SEMI_CONSISTENT:
row_unlock_for_mysql();
// 若当前系统已采用SEMI_CONSISTENT read,并且有锁等待,构造了commit版本
? ? // 没有在commit版本上加锁,因此也无锁可放,直接返回即可
case ROW_READ_DID_SEMI_CONSISTENT:
prebuilt->row_read_type = ROW_READ_TRY_SEMI_CONSISTENT;
break;
set binlog_format=mixed;
set session transaction isolation level repeatable read;
create table t1(a int not null) engine=innodb DEFAULT CHARSET=latin1;
insert into t1 values (1),(2),(3),(4),(5),(6),(7);
session 1: ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?session 2:
set autocommit=0;
update t1 set a = a + 10;
set binlog_format=mixed;
set session transaction isolation level read committed;
update t1 set a = a + 100 where a > 10;
此时,session 2不需要等待session 1,虽然session 1的更新后项满足session 2的条件,但是由于session 2进行了semi-consistent read,读取到的记录的前项为(1-7),不满足session 2的更新where条件,因此session 2直接返回。
session 2直接返回,0 rows affected。
set binlog_format=mixed;
set session transaction isolation level repeatable read;
create table t1(a int not null) engine=innodb DEFAULT CHARSET=latin1;
insert into t1 values (1),(2),(3),(4),(5),(6),(7);
session 1: ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?session 2:
set autocommit=0;
update t1 set a = a + 10;
commit;
set binlog_format=mixed;
set session transaction isolation level repeatable read;
set autocommit = 0;
update t1 set a = a + 100 where a < 10;
select * from t1 lock in share mode;
session 1在session 2开始前已经提交,session 2可以进行semi-consistent read。并且读到的都是session 1的更新后项,完成加锁。但是由于更新后项均不满足session 2的where条件,session 2会释放所有行上的锁(由MySQL Server层判断并调用unlock_row方法释放行锁)。
此时,session 1再次执行select * from t1 lock in share mode语句,直接成功。因为session 2已经将所有的行锁提前释放。
朋友们可以试试将session 2的隔离级别改为repeatable read,那么此时session 1就会等待session 2提交。