AT模式写隔离
当我们看到上面的图的时候可能不太明白这个具体是怎么做的,所以今天就来源码看下。
首先是获取全局锁,那这个到底是什么锁呢,我们来看TA
模式的ConnectionProxy
的processGlobalTransactionCommit
方法:
注册的时候会去注册锁,其实有点像分布式锁,谁先注册了谁就占着了,然后其他的就要重试:
其实这把锁哪里来的,就是在准备回滚日志的时候生成的,表名_主键ID
:
如果发现注册有锁冲突,说明已经被别人抢到锁了,回头就抛recognizeLockKeyConflictException
异常:
这个异常一直抛出去,直到外面有捕获的,睡眠一定时间重试,直到超时抛出LockWaitTimeoutException
异常:
然后服务端提交后会释放锁,具体可以看服务端代码,这个就不细说了:
现在应该能大致看明白那个图了把,其实就是谁先注册谁就获得锁,然后做事务,提交了才会释放锁,如果注册发现有锁了,就只能不断的尝试,直到能注册位置,这样就进行了写隔离,同一时刻就只能有一个事务对同一个数据进行修改。
AT模式读隔离
这张图说的是读隔离,默认的是读未提交,如果要进行读已提交的话也可以用全局锁:
在sql
中有SELECT FOR UPDATE
语句,执行器就是SelectForUpdateExecutor
,可以看到查询的时候会检查全局锁,不能获得就会重试:
while (true) {
try {
// #870
// execute return Boolean
// executeQuery return ResultSet
rs = statementCallback.execute(statementProxy.getTargetStatement(), args);
// Try to get global lock of those rows selected
TableRecords selectPKRows = buildTableRecords(getTableMeta(), selectPKSQL, paramAppenderList);
String lockKeys = buildLockKey(selectPKRows);//构建全局锁
if (StringUtils.isNullOrEmpty(lockKeys)) {
break;
}
if (RootContext.inGlobalTransaction() || RootContext.requireGlobalLock()) {
// Do the same thing under either @GlobalTransactional or @GlobalLock,
// that only check the global lock here.
statementProxy.getConnectionProxy().checkLock(lockKeys);//检查全局锁
} else {
throw new RuntimeException("Unknown situation!");
}
break;
} catch (LockConflictException lce) {
if (sp != null) {
conn.rollback(sp);
} else {
conn.rollback();
}
// trigger retry
lockRetryController.sleep(lce);//重试
}
}
像TC
查询锁是否存在:
其实就一个简单的例子,比如你想查询一个数据,但是你没在任何全局事务中,但是怕有其他全局事务在修改,你查出来的数据有问题,那怎么办呢,你就可以在查询的方法上用@GlobalLock
,其实就是一个读隔离。
好了,今天就到这里了,大致的说下AT
的读写隔离,细节的还要自己去看,希望对学习理解有帮助,大神看见勿喷,仅为自己的学习理解,能力有限,请多包涵。