文档章节

使用ThreadLocal实现的读写分离在迁移后的偶发错误

drv
 drv
发布于 2017/05/26 19:54
字数 1046
阅读 14
收藏 0

  最近莫名的会有错误日志,说有写操作因为走了读库而报了read only的异常,由于并没有造成应用使用的问题,开始我以为哪的配置错误就没当回事让程序员自己去查了,然而。。。

  背景:之前的博客里提到过,读写分离功能是直接从老系统迁移过来的,老系统可能是因为主从库之间同步延迟而使用了基于请求层面的读写分离,迁过来的时候降成了基于Service,新系统用了dubbox,这些Service直接充当Provider。读写分离的实现是重写了了DataSource,请求到Service的时候会先经过拦截器判断方法是走读库还是写库,默认是写库,如果是要走读库的会用ThreadLocal标记为读,否则标记为写。由于读压力远高于写,所以并不是所有读都走写库,避免两个数据库压力不均,通过人为指定来平衡两个库的压力,也就是说是以标记为准而不是以请求类型为准。

  从第一截图可以看到,当遇到没有被标记的事务获取数据源的时候,默认会设置为写。原本这其实是没有什么问题的,因为需要走到写库的都会先被拦截器拦截标记为写:

  而这个determineCurrentLookupKey就是我们重写的方法,也就是上面的第一个截图,所以貌似看上去没什么问题。因为一个事务被设置一个数据源,即使请求因为线程池被复用的关系,上一次请求的ThreadLocal没有专门清理,在新的请求进来的时候这个值也会被拦截器更新。而且,嵌套事务中每个子事务都会保存一个自己的状态,子事务执行完回将状态重置回它开始时候得状态(这个状态也是通过ThreadLocal保存的): 

  数据源与连接的绑定关系也是通过ThreadLocal保存的,因为一个事务也只能使用一个连接,所以将唯一的数据源(即使可能不确定)与连接绑定也没什么关系,反正事务结束后会清理掉。

  本来如果维持这个事务体系的闭环就好了,然而依然是因为determineCurrentLookupKey被重写的原因,begin时候数据源的状态对Spring的事务体系就不可见了,是运行时动态选择的,而这个状态的选择在一般情况下依赖于拦截器。为什么说是一般情况下呢,因为有特殊情况会出现,比如第一个图中的=null,只要拦截器先执行了就不会出现=null的情况,问题就出现在先执行上。如果TransactionInterceptor先执行,这里就没有依赖于拦截器,刚才一直都在重点说ThreadLocal,如果这个线程刚巧在上一个事务中把第二个图中的holder设为了读,一连串的问题就来了,首先外层事务的状态就不对了,又因为各个嵌套的子事务的的状态都是隔离的,即使后续子事务的启动是在外层方法被拦截之后得到了正确的连接,但是由于一个事务只有有一个连接,ThreadLocal中已经保存的有数据源和连接了,所以每个事务的状态可能是对的,但提交时用的也就是上图中的连接时错的,本来应该是指向写库的就变成读库了,提交就是用的读库的连接进行的。

  找到问题,解决就容易了,只要设置一下拦截器的执行顺序就好了;也可以通过在提交事务后清理我们自己的ThreadLocal来解决,只需要继承DataSourceTransactionManager重写其中比如清理方法就可以了。

==========================================================

咱最近用的github:https://github.com/saaavsaaa

微信公众号:

                      

© 著作权归作者所有

drv

drv

粉丝 2
博文 57
码字总数 75382
作品 0
东城
架构师
私信 提问
轻量级的关系型数据库中间件 - Sharding-JDBC

Sharding-JDBC是一个开源的适用于微服务的分布式数据访问基础类库,它始终以云原生的基础开发套件为目标。 Sharding-JDBC定位为轻量级java框架,使用客户端直连数据库,以jar包形式提供服务,...

亮_dangdang
2016/01/27
51.2K
41
Sharding-JDBC 1.3.1 发布,当当 JDBC 增强驱动

Sharding-JDBC 1.3.1 发布了,Sharding-JDBC是当当应用框架ddframe中,从关系型数据库模块dd-rdb中分离出来的数据库水平分片框架,实现透明化数据库分库分表 访问。Sharding-JDBC是继dubbox和...

淡漠悠然
2016/06/28
3.1K
6
amoeba-mysql 的安装使用和读写分离

amoeba真的是不错的稳定而灵活的数据库解决方案,阿里巴巴的技术陈思儒开始的一个开源项目,它是分布式数据库Proxy解决方案。 Amoeba(变形虫)项目,专注 分布式数据库 proxy 开发。座落与Cli...

红薯
2010/08/11
2.2K
1
Sharding-JDBC 1.5.3 发布,读写分离全面提升

Sharding-JDBC 1.5.3 正式发布。主要更新是对读写分离的全面提升。 Sharding-JDBC 之前的读写分离必须配合分库分表使用,无法仅使用读写分离。由于独立使用读写分离的呼声非常高,因此我们于...

亮_ShardingSphere
2017/09/05
2.3K
10
Sharding-JDBC 1.5.4 发布,1.x 系列的最终版本

Sharding-JDBC 1.5.4 正式发布。作为分布式数据库中间件,它关注如何简化分布式数据库带来的复杂度,让工程师象使用单一数据库一样使用分布式的数据库。它完整的实现了分库分表、读写分离、分...

亮_ShardingSphere
2017/09/19
1K
6

没有更多内容

加载失败,请刷新页面

加载更多

02.日志系统:一条SQL更新语句是如何执行的?

我们还是从一个表的一条更新语句说起,我们创建下面一张表: create table T(ID int primary key, c int); 如果要将ID=2这一行c的值加1,SQL可以这么写: update T set c=c+1 where ID=2; 前...

scgaopan
44分钟前
7
0
【五分钟系列】掌握vscode调试技巧

调试前端js 准备一个前端项目 index.html <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1......

aoping
44分钟前
4
0
PhotoShop 高级应用:USM锐化/S锐化/防抖

、 高反差锐化+混合模式:叠加模式 【将更多的边缘细节添加到图像中】

东方墨天
55分钟前
7
0
Python数据可视化之matplotlib

常用模块导入 import numpy as npimport matplotlibimport matplotlib.mlab as mlabimport matplotlib.pyplot as pltimport matplotlib.font_manager as fmfrom mpl_toolkits.mplot3d i......

松鼠大帝
昨天
5
0
我用Bash编写了一个扫雷游戏

我在编程教学方面不是专家,但当我想更好掌握某一样东西时,会试着找出让自己乐在其中的方法。比方说,当我想在 shell 编程方面更进一步时,我决定用 Bash 编写一个扫雷游戏来加以练习。 我在...

老孟的Linux私房菜
昨天
9
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部