Mysql 在Spring/J2EE应用程序中分离只读和读写

Mysql 在Spring/J2EE应用程序中分离只读和读写,mysql,jpa,design-patterns,architecture,spring-data,Mysql,Jpa,Design Patterns,Architecture,Spring Data,我们在项目中使用Spring、Spring数据和JPA 对于生产服务器,我们希望设置数据库集群,以便所有读取查询都指向一台服务器,所有写入查询都指向另一台服务器 这显然需要对DAO的构建方式进行一些更改 如果到目前为止,有人一直在使用SpringData/JPA跟踪CookBook风格的DAO创建,DAO实现负责读写,那么有人知道如何实现这一点吗?要将这两种类型的调用分离开来,需要对体系结构进行什么样的更改?好吧,您所说的实际上就是CQR()。 我建议在尝试实施之前先阅读一些概念指南 至于你的问

我们在项目中使用Spring、Spring数据和JPA

对于生产服务器,我们希望设置数据库集群,以便所有读取查询都指向一台服务器,所有写入查询都指向另一台服务器

这显然需要对DAO的构建方式进行一些更改


如果到目前为止,有人一直在使用SpringData/JPA跟踪CookBook风格的DAO创建,DAO实现负责读写,那么有人知道如何实现这一点吗?要将这两种类型的调用分离开来,需要对体系结构进行什么样的更改?

好吧,您所说的实际上就是CQR()。 我建议在尝试实施之前先阅读一些概念指南

至于你的问题,作为第一个胜利,我建议开始将DAL的服务划分为Finder类和Repository类,这两个类将由更高的、面向业务的服务使用

查找程序适合于只读访问,只公开返回自定义结果对象(如报表)的getBy…()方法和查找,并且它们的底层实现是针对只读数据库定制的

另一方面,存储库将适合于只写/getById()方法,它们的底层实现是针对只写数据库定制的

唯一剩下的就是这些数据库之间的同步。
这可以非常简单地通过以下技术解决方案实现:数据库复制、更改只读数据库后推迟对只读数据库的更新(最终一致性)。

使用MySQL时,Java开发人员通常将其用作JDBC驱动程序。开发人员通常使用Connector/J
com.mysql.jdbc.Driver
类,其URL如
jdbc:mysql://host[:port]/database
连接到MySQL数据库

Connector/J提供了另一个名为的驱动程序,它允许应用程序在多个MySQL主机之间实现负载平衡。使用
ReplicationDriver
时,JDBC URL将更改为
JDBC:mysql:replication://master-host[:主端口][,从-1-主机[:从-1-端口][,从-2-主机[:从-2-端口]]]/数据库
。这允许应用程序连接到多个服务器中的一个,具体取决于在任何给定时间点哪个服务器可用

使用
ReplicationDriver
时,如果JDBC连接设置为
只读
,则驱动程序将URL中声明的第一个主机视为
读写
主机,将所有其他主机视为
只读
主机。开发人员可以在Spring应用程序中利用这一点,其代码结构如下:

@Service
@Transactional(readOnly = true)
public class SomeServiceImpl implements SomeService {
   public SomeDataType readSomething(...) { ... }

   @Transactional(readOnly = false)
   public void writeSomething(...) { ... }
}
对于这样的代码,每当调用方法
readSomething
时,Spring事务管理代码将获得一个JDBC
连接
,并在其上调用
setReadOnly(true)
,因为服务方法在默认情况下被注释为
@Transactional(readOnly=true)
。这将使来自
readSomething
方法的所有数据库查询转到一个非主MySQL主机,以循环方式进行负载平衡。类似地,无论何时调用
writeMething
,Spring都会在底层JDBC
连接上调用
setReadOnly(false)
,强制数据库查询转到主服务器


此策略允许应用程序将所有只读流量定向到一组MySQL服务器,并将所有读写流量定向到不同的服务器,而无需更改应用程序的逻辑体系结构,也无需开发人员担心不同的数据库主机和角色。

您使用的是什么数据库?在这种情况下,我使用的是MySQL 5.6,对你有用吗?我以前试过,效果很好。
ReplicationDriver
类与常规的MySQL
驱动程序类似,只是它允许您在JDBC URL中传递多个服务器主机。如果在代码中使用
@Transactional(readOnly=true)
注释只读方法,则
ReplicationDriver
将始终将JDBC URL中的第一个主机视为写入主机,其他主机视为只读。谢谢。这看起来很有希望。我们有一个多主设置,我们是否需要移动到主从式的设置,它才能工作不,不是必需的。例如,我曾经用一个MySQL主应用程序构建了一个示例应用程序,每个应用程序服务器运行一个从应用程序副本。所有这些都在同一机架中,因此复制不会经常失败。然后,我们使用JDBCURL
JDBC:mysql:replication://master.example.com,local.example.com
,其中
master.example.com
解析为master,而
local.example.com
解析为本地服务器。因此,尽管有很多从服务器,但由于JDBC URL的构建方式,每个应用服务器只会知道一个(自身)。我知道这是一篇较老的文章,但我使用Spring(4.2.x)和hibernate(5.1.x)以及mysql复制驱动程序,但即使使用@Transactional(readOnly=true),所有查询似乎都会转到主服务器。似乎底层连接没有被设置为只读,还有其他人遇到/解决了这个问题吗?我最终将我的代码重构为标准JPA(ie:EntityManager)的hibernate特定API(ie:SessionFactory),解决了我的问题。