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

我们在项目中使用 Spring、Spring-Data 和 JPA.

We using Spring, Spring-Data and JPA in our project.

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

For production servers, we would like to setup database cluster such that all read queries are directed to one server and all write queries are directed to another server.

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

This obviously will require some changes in the way the DAOs are built.

如果到目前为止,有人一直在使用 Spring-Data/JPA 遵循食谱风格的 DAO 创建,其中一个 DAO 实现负责读取和写入,那么有谁知道如何实现这一点?需要对架构进行哪些更改来隔离这两种类型的调用?

Does anyone know how to achieve this if one has, so far, been following cook-book style DAO creations using Spring-Data/JPA where a DAO implementation is responsible for both reads and writes? What kind of changes in architecture will be needed to segregate the two types of calls?

推荐答案

在使用 MySQL 时,Java 开发者常用 Connector/J 作为 JDBC 驱动程序.开发人员通常使用 Connector/J com.mysql.jdbc.Driver 类,使用诸如 jdbc:mysql://host[:port]/database 之类的 URL 进行连接到 MySQL 数据库.

When using MySQL, it is common for Java developers to use Connector/J as the JDBC driver. Developers typically use the Connector/J com.mysql.jdbc.Driver class, with a URL such as jdbc:mysql://host[:port]/database to connect to MySQL databases.

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]]/数据库.这允许应用程序连接到多个服务器之一,具体取决于在任何给定时间点可用的服务器.

Connector/J offers another driver called ReplicationDriver that allows an application to load-balance between multiple MySQL hosts. When using ReplicationDriver, the JDBC URL changes to jdbc:mysql:replication://master-host[:master-port][,slave-1-host[:slave-1-port]][,slave-2-host[:slave-2-port]]/database. This allows the application to connect to one of multiple servers depending on which one is available at any given point in time.

使用 ReplicationDriver 时,如果 JDBC 连接设置为 read-only,驱动程序会将 URL 中声明的第一个主机视为 read-write 主机和所有其他主机为 read-only 主机.开发人员可以在 Spring 应用程序中通过如下结构来利用这一点:

When using the ReplicationDriver, if a JDBC connection is set to read-only, the driver treats the first host declared in the URL as a read-write host and all others as read-only hosts. Developers can take advantage of this in a Spring application by structuring their code as follows:

@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),强制数据库查询转到 master服务器.

With code like this, whenever the method readSomething is called, the Spring transaction management code will obtain a JDBC Connection and call setReadOnly(true) on it because the service methods are annotated with @Transactional(readOnly = true) by default. This will make all database queries from the readSomething method to go to one of the non-master MySQL hosts, load-balanced in a round-robin fashion. Similarly, whenever writeSomething is called, Spring will call setReadOnly(false) on the underlying JDBC Connection, forcing the database queries to go to the master server.

此策略允许应用程序将所有只读流量引导至一组 MySQL 服务器,并将所有读写流量引导至另一台服务器,而无需更改应用程序的逻辑架构或开发人员不必担心不同的数据库主机和角色.

This strategy allows the application to direct all read-only traffic to one set of MySQL servers and all read-write traffic to a different server, without changing the application's logical architecture or the developers having to worry about different database hosts and roles.

相关文章