Spring/J2EE Apps 中的只读和读写分离

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

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-将 主机和所有其他主机写入只读 主机.开发人员可以通过如下构造他们的代码在 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事务管理代码都会获取一个JDBCConnection并调用setReadOnly(true)code> 在它上面,因为服务方法默认使用 @Transactional(readOnly = true) 注释.这将使来自 readSomething 方法的所有数据库查询转到非主 MySQL 主机之一,以循环方式进行负载平衡.同样,每当writeSomething被调用时,Spring都会在底层的JDBCConnection上调用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.

相关文章