事务日志表undo_log
在数据源代理DataSourceProxy拦截业务SQL之后,会生成包含before-image和<br>after-image信息的事务日志,并保存在事务日志标 (表名:undo_log、事务日志在Seata中称为undoLog)<br>核心重点字段:rollback_info字段,记录了回滚的数据信息,内部有前置镜像和后置镜像。<br>
afterImage中的字段数据
org.apache.seata.rm.datasource.sql.struct.TableRecords<br>表元数据、表名称、行集合<br>
org.apache.seata.sqlparser.struct.TableMeta<br>表名称、列元素映射、索引元数据映射<br>
org.apache.seata.sqlparser.struct.ColumnMeta<br>列名、数据类型、是否为空等.......<br>
org.apache.seata.sqlparser.struct.IndexMeta<br>索引名称、索引类型、是否唯一索引、包含的列<br>
元数据加载
1. 定义一个 缓存 大小100kb 失效时间900s【基于咖啡因 Caffeine】
<br>2. org.apache.seata.rm.datasource.sql.struct.cache.AbstractTableMetaCache#getTableMeta<br>方法获取元数据,如果cache中没有,则从数据库获取<br>
3. fetchSchema是一个抽象方法,从数据库获取元数据
org.apache.seata.rm.datasource.sql.struct.cache.MysqlTableMetaCache#resultSetMetaToSchema<br>在数据表变动不是很频繁的情况下,seata遵循读多写少用缓存的原则,<br>并通过定时任务的方式来保持拿到的数据表元数据是最新的。<br>
数据类型定义Class<br>java.sql.Types<br>
事务日志的处理逻辑一般都在UndoLogManager中
org.apache.seata.rm.datasource.undo.AbstractUndoLogManager#flushUndoLogs<br>保存事务日志方法<br>
org.apache.seata.rm.datasource.undo.AbstractUndoLogManager#undo<br>二阶段回滚方法<br>
org.apache.seata.rm.datasource.undo.AbstractUndoLogManager#deleteUndoLog<br>二阶段回滚处理的删除事务日志方法<br>
org.apache.seata.rm.datasource.undo.AbstractUndoLogManager#batchDeleteUndoLog<br>二阶段提交处理的批量删除事务日志方法<br>
Seata数据源代理
Seata对java.sql中的DataSource、Connection、Statement、PreparedStatement<br>四个接口进行了包装<br>DataSourceProxy、ConnectionProxy、StatementProxy、PreparedStatementProxy<br>
数据源代理类
具体实现org.apache.seata.rm.datasource.DataSourceProxy#DataSourceProxy(javax.sql.DataSource, java.lang.String)<br>init方法作用:<br>1. 判断使用的是什么数据库,比如mysql<br>2. 向资源管理器注册DataSourceProxy,之所以可以注册,是因为DataSourceProxy实现了Resource接口,注册的主要工作是找到与事务分组对应的TC集群,并与集群中的每台机器建立连接<br>3. 判断是否启动定时任务,定时任务的作用是缓存数据库表结构,表结构在RM保存数据快照的时候使用 org.apache.seata.rm.datasource.sql.struct.TableMetaCacheFactory.TableMetaRefreshHolder#TableMetaRefreshHolder<br>
资源管理器
ResourceManager
org.apache.seata.core.model.ResourceManager
ResourceManagerInbound
org.apache.seata.core.model.ResourceManagerInbound<br>主要是对内操作,接收TC发来的请求【提交分支事务、回滚分支事务】<br>
ResourceManagerOutbound
主要是对外的操作,发送请求到TC【注册分支事务、上报分支状态、查询全局锁】
资源注册
1. 获取分支事务类型<br>2. 获取资源管理器<br>3. 注册资源,通过RPC客户端注册资源,将当前的资源组ID和资源ID发给TC<br>
数据库连接代理
创建数据库代理
org.apache.seata.rm.datasource.DataSourceProxy#getConnection()<br>通过目标数据源创建连接代理<br>
本地事务提交
org.apache.seata.rm.datasource.ConnectionProxy#commit<br>1、锁冲突重试机制:AT模式中RM发送创建分支事务请求到服务端,服务端会对分支涉及的行进行加锁<br>为了防止多个分布式事务兵法的修改相同行而造成数据冲突。如果发生冲突,则RM会通过LockRetryPolicy的execute()方法进行重试<br>2、分支事务提交<br> a.注册分支事务 org.apache.seata.rm.datasource.ConnectionProxy#register<br> b.保存事务的日志 org.apache.seata.rm.datasource.undo.AbstractUndoLogManager#flushUndoLogs, 把全局事务ID、分支事务ID、rollback_info(BLOB形式)、状态等字段存入undo_log表中<br> c.本地事务提交<br> d.上报分支事务状态【TC需要根据上报的事务状态来决定二阶段的处理】<br>3、查询Seata全局锁【for支持“读未提交”以上的隔离级别】<br>AT模式中,如果两个操作,创建订单&占用库存。如果一个分支事务提交之后创建订单成功,正在执行占用库存操作。那么此时另一个分支事务直接能看到这个订单,如果另一个分支事务是轮训订单执行推送,那么此时就会出现第一个分支占用库存失败-事务回滚,但是仍然被推送的情况。<br>所以,不断扫描的那个推送事务就应该利用Seata的全局锁,来实现“读已提交”的隔离级别<br>private void processLocalCommitWithGlobalLocks() throws SQLException {<br> checkLock(context.buildLockKeys());<br> try {<br> targetConnection.commit();<br> } catch (Throwable ex) {<br> throw new SQLException(ex);<br> }<br> context.reset();<br> }<br>先进性检查seata的全局锁,在进行本地事务的提交 org.apache.seata.rm.datasource.DataSourceManager#lockQuery<br>
StatementProxy & PreparedStatementProxy
Statement和PreparedStatement的区别<br>
1. Statement用于执行静态的SQL语句,需要实现准备<br>2. PreparedStatement支持动态参数,比如 ?<br>3. PreparedStatement执行SQL,SQL会先被数据库解析和编译,然后放入命令缓冲区。执行同一个PreparedStatement对象,都会再次解析,但是不会再次编译,因为缓冲区有预编译的命令,并且可以重用<br>4. PreparedStatement减少编译次数,提高数据库性能<br>5. PreparedStatement有更好的安全性<br>
PreparedStatement相关关系图
执行模板类 ExcuteTemplete的execute方法<br>org.apache.seata.rm.datasource.exec.ExecuteTemplate<br>如果sql语句不在分布式事务中,并且也没有Seata全局锁的要求,则不需要将其纳入Seata框架下进行处理,用原始的Statement方法处理即可;<br>如果这个SQL语句在分布式事务中,则将其纳入Seata框架进行处理,并根据不同SQL语句类型选用不同的执行器来执行<br>
1.SQL识别器 SQL识别器是通过Druid SQL识别器工厂创建的<br>org.apache.seata.sqlparser.druid.DruidSQLRecognizerFactoryImpl<br>
2.SQL执行器org.apache.seata.rm.datasource.exec.BaseTransactionalExecutor#execute<br>
a、生成前镜像<br>org.apache.seata.rm.datasource.exec.UpdateExecutor#beforeImage<br>
b、执行原始SQL语句
c、生成后镜像<br>org.apache.seata.rm.datasource.exec.BaseInsertExecutor#afterImage<br>
d、准备事务日志<br>org.apache.seata.rm.datasource.exec.BaseTransactionalExecutor#prepareUndoLog<br>