数据库读写分离(动态数据源切换)介绍
数据库读写分离
方案介绍
读写分离要做的事情就是决定一条 SQL 该到哪个数据库去执行,至于谁来做决定数据库这件事,要么数据库中间件去做,要么应用程序自己去做。首先针对应用程序自己去做的场景,读写分离的职责应该属于数据访问层而不是业务层,其次读写分离不应该入侵到代码中。因此在 Service—DAO—ORM— 数据库驱动的调用链中,要想做到代码弱入侵性或者零入侵性,只能将读写分离写在 ORM 层或者数据库驱动层,写在 ORM 层就和具体 ORM 框架耦合,写在数据库驱动层,就和具体数据库耦合。至于在 ORM 层还是在数据库驱动层实现读写分离,主要看更换 ORM 框架和数据库哪个成本更高和实现的难易程度。一般来讲,读写分离(动态数据源切换)的核心方案主要有以下几种:
- 第一种构建多套环境,优势是方便控制也容易集成一些简单的分布式事务,缺点是非动态同时代码量较多,配置难度大;
- 第二种是依靠数据库中间件(例如:MyCat),由中间件做读写分离,优势是对整个应用程序都是透明的,缺点是降低性能,不支持多数据源事务;
- 第三种是应用程序自己去做,例如使用支持读写分离的数据库驱动、使用 Spring 原生提供的 AbstractRoutingDataSource。后者需要控制只读事务和读写事务切换到主库,写操作切换到主库,读操作切换到从库;同时保证单个事务里面所有的 SQL 都是在同一个数据源里执行。缺点是多数据源的配置不灵活,不支持多数据源事务。具体实现方式可参考 基于 Service 层的 Spring 路由数据源 + AOP / Annotation、基于 ORM 层的 Spring 路由数据源 + Mybatis 插件 / Annotation。
最佳实践
Dynamic-Datasource
Dynamic-Datasource 是 MyBatis-Plus 官方的读写分离框架。如果数据源较少,场景不复杂,不需要使用多数据源事务,可以选择上述任意一种读写分离方案。如果需要更多特性,又不想引入数据库中间件,可尝试 Dynamic-Datasource,具体的使用方式建议阅读 官方文档一、官方文档二、开源中国介绍。
优势:
- 项目启动后支持动态增减数据源
- 简化 Druid 和 HikariCp 配置,提供全局参数配置
- 提供自定义数据源来源(默认使用 yml 或 properties 配置)
- 数据源分组,适用于多种场景,包括纯粹多库、读写分离、一主多从、多主多从、混合模式
- 使用 Spel 动态参数解析数据源,如从 session,header 和参数中获取数据源。(多租户架构神器)
- 简单集成 Druid 数据源监控多数据源,简单集成 Mybatis-Plus 简化单表,简单集成 P6sy 格式化 SQL,简单集成 Jndi 数据源
- 使用正则匹配或 Spel 表达式来切换数据源(实验性功能)
- 默认支持通过 @DS 注解来动态选择数据源(代码入侵性强),额外支持使用 MyBatis 插件在 ORM 层实现纯读写分离(代码零入侵性),但两者不能同时使用
- 使用 @DS 注解的时候,支持多层数据源嵌套切换。(一个业务 ServiceA 调用 ServiceB,ServiceB 调用 ServiceC,每个 Service 都是不同的数据源)
劣势
- 不支持多数据源事务(同一个数据源下支持事务),网上绝大多数普通方案(基于 Spring 路由数据源 )也都不能支持
- 如果需要使用到分布式事务,那么架构应该到了微服务化的时候
- 提示:如果项目中只有几个数据库,但是有强烈使用分布式事务的需求,建议还是使用传统方式自己构建多套环境集成 Atomic 这类方案
约定
- 只做切换数据源这件核心的事情,并不限制具体操作,切换了数据源可以做任何 CRUD 操作
- 配置文件中所有以下划线 _ 分割的数据源,首部即为组的名称,相同组名称的数据源会放在一个组下
- 切换数据源即可以是组名,也可以是具体数据源名称,切换时默认采用负载均衡机制切换
- 默认的数据源名称为 master,可以通过 spring.datasource.dynamic.primary 修改
- 方法上的注解优先于类上注解
Sharding-Jdbc
Sharding-Jdbc 是 Apache 的分布式数据库中间件。