对于生产服务器,我们希望设置数据库集群,以便所有读取查询都指向一个服务器,而所有写入查询都指向另一个服务器。
显然,这将需要在DAO构建方式上进行一些更改。
如果迄今为止一直在使用Spring-Data/JPA创建cookbook式DAO实现,其中一个DAO实现负责读取和写入操作,那么有谁知道如何实现这一点呢?需要对体系结构进行哪些更改才能分离这两种类型的调用呢?
在使用MySQL时,Java开发人员通常会使用Connector/J作为JDBC驱动程序。开发人员通常使用Connector/J的com.mysql.jdbc.Driver
类,并使用类似于jdbc:mysql://host[:port]/database
的URL连接到MySQL数据库。
Connector/J提供了另一个名为ReplicationDriver
的驱动程序,允许应用程序在多个MySQL主机之间进行负载平衡。当使用ReplicationDriver
时,JDBC URL将更改为jdbc:mysql:replication://master-host[:master-port][,slave-1-host[:slave-1-port]][,slave-2-host[:slave-2-port]]/database
。这使得应用程序可以连接到多个服务器中的一个,具体取决于任何给定时间可用的服务器。
在使用ReplicationDriver
时,如果将JDBC连接设置为read-only
,则驱动程序将把URL中声明的第一个主机视为read-write
主机,而将所有其他主机视为read-only
主机。开发人员可以通过以下方式构建他们的Spring应用程序来利用这一点:
@Service
@Transactional(readOnly = true)
public class SomeServiceImpl implements SomeService {
public SomeDataType readSomething(...) { ... }
@Transactional(readOnly = false)
public void writeSomething(...) { ... }
}
有了这样的代码,每当调用方法readSomething
时,Spring事务管理代码将获取一个JDBC Connection
并在其上调用setReadOnly(true)
,因为服务方法默认情况下带有注释@Transactional(readOnly = true)
。这将使从readSomething
方法中的所有数据库查询都转到非主MySQL主机之一,以轮询方式进行负载平衡。同样,在调用writeSomething
时,Spring将在底层JDBC Connection
上调用setReadOnly(false)
,强制数据库查询到主服务器。
这种策略允许应用程序将所有只读流量定向到一组MySQL服务器,并将所有读写流量定向到不同的服务器,而不必更改应用程序的逻辑架构或开发人员担心不同的数据库主机和角色。
您所讲的实际上是CQRS (http://martinfowler.com/bliki/CQRS.html)。
在尝试实现之前,建议先阅读一些概念指南。
至于您的问题,为了取得简短的第一胜利,建议将DAL的服务分成Finder类和Repository类,这些类将被更高级别的面向业务的服务使用。
Finders适合只读访问,仅公开getBy...()方法和查找返回自定义结果对象(如报告)的方法,并且它们的底层实现是针对只读数据库进行的。
另一方面,Repositories适合写入/通过getById()方法进行访问,它们的底层实现是针对写入数据库进行的。
唯一剩下的就是这些数据库之间的同步。这可以通过技术解决方案来实现,例如:数据库复制、在对写入数据库进行更改后延迟更新只读数据库(最终一致性)。
ReplicationDriver
类就像常规的 MySQLDriver
类一样,只是它允许您在 JDBC URL 中传递多个服务器主机。如果您在代码中使用@Transactional(readOnly = true)
注释只读方法,ReplicationDriver
将始终将 JDBC URL 中的第一个主机视为写入主机,其他主机视为只读。 - manishjdbc:mysql:replication://master.example.com,local.example.com
,其中master.example.com
解析为主服务器,local.example.com
解析为本地服务器。因此,尽管有许多从服务器,但由于JDBC URL的构造方式,每个应用服务器只会意识到一个(自己)。 - manish