某项目需要实现多租户,当然这里谈论的资源主要是数据库。 评估的时候主要从数据量上去评估,结论是目前只需要一个库。但是有16张表需要进行水平切分从而容纳56个接入公司。
目标是为了代码尽可能少改动。
评估出来大概是分为 1 表内加字段的区分多租户的表 2 表水平切分的 3 跨多表的我们采用ES来做查询。
这里主要是谈第2块 ,实现是基于ShardingSphere的Hint模式。
大概是读了一下ShardingSphere的starter的源代码,官方给的starter对我们这种场景不可用,因为我们是现存的十多个微服务尽量少改动,所以需要一个工程在背后自动完成。基本上做的事就是自动做表的映射。
单纯只做表的映射就可以直接在ORM框架层去做,mybatis/hibernate/spring data 等,但是这个项目不可以这样做,因为有部分是遗留的资产,所以DAO层什么样的技术都有,各种技术都有,所以选择在JDBC层来进行处理,这样就就不在意各种DAO 框架。
大概思路 网关进来->取出用户所在的公司(当成分片id)->ORM层自动映射到表,同时增加feign调用的时候的拦截器,自动传递tenantId
其它的微服务使用的时候,直接使用一个jar包依赖既可,背后就是做了个auto-configure,和springboot-**-starter原理是一样的。
记录一下碰见的问题:
1 数据源的初始化的事,这有众多微服务,并且有祖传代码,因此连接池也没有统一,所以初始化的时候一定要注意,因为ShardingSphere-jdbc初始化的时候(它老的starter并没有考虑这个),所以得参照spring 的datasource的初始化来进行兼容性初始化,相当于spring配置的池的属性是通用的,但是各连接池并不是,所以需要参照spring中初始化
2 ShardingSphere在jdbc关于日期格式转化的时候有个问题的,这个不细说。分析一下就知道引入一个开源包来转换就好了。主要是mybatis来转换 datetime转LocalDateTime
3 个人发现的问题,如果列带下划线的时候,列名一定要用``进行转义,不然同样解析不出来
发现这祖传代码中有好多SQL太复杂,特别是 case中带子查询的确实是识别不出来。着急上线,先弄一个表的简单映射方案。
主要基于Druid中的filter进行改写表名。
这里特别要说一下,租户ID在不同的线程中传递的时候要明白:
I InheriableThreadLocal是在创建线程的时候会获取父线程的数据,对连接池是无效的(连接池的线程大多是复用的)
2 改为用阿里的 TransmittableThreadLocal
这玩意也踩了个小坑,以前用过,用是好用,小坑也有。因为我们从网关传过来的用户ID/多租户ID等都放在一个对象,但是多租ID涉及到可以切换。这个时候你会发现父子线程看上去不能隔离,后来翻了一下源码,其实也就是因为对象是浅拷贝的原因。