Databases 9 min read

Read‑Write Splitting in Database Applications: Implementation Methods, Data Lag, and Routing Strategies

This article explains the concept of read‑write splitting for databases, compares client‑side and proxy implementations, discusses data lag and forced routing techniques, and provides code examples and custom load‑balancing algorithms using Sharding‑JDBC and Spring.

Full-Stack Internet Architecture
Full-Stack Internet Architecture
Full-Stack Internet Architecture
Read‑Write Splitting in Database Applications: Implementation Methods, Data Lag, and Routing Strategies

Implementation Methods

Read‑write splitting can be implemented in two main ways: a client‑side approach and a proxy approach.

The client‑side method can be built using Spring's AbstractRoutingDataSource or third‑party frameworks such as Sharding‑JDBC.

The proxy method involves writing a proxy service that manages all database nodes, allowing the application to remain unaware of multiple data sources; this can be custom‑built, use open‑source frameworks, or rely on commercial cloud services.

Data Lag

Understanding the master‑slave architecture is essential: write operations occur on the master, while reads are directed to slaves. Immediately after a write, the data may not yet be synchronized to the slaves, leading to stale reads.

For example, after publishing an article, the newly posted entry might not appear on the list page until the page is refreshed, illustrating the effect of data lag in a read‑write split setup.

Forced Routing

If a business scenario requires real‑time data, the only reliable solution is to force reads to the master.

Forced routing can be achieved using hint syntax such as FORCE_MASTER or FORCE_SLAVE supported by many middleware components.

In Sharding‑JDBC, the HintManager provides this capability:

HintManager hintManager = HintManager.getInstance();
hintManager.setMasterRouteOnly();

It is recommended to encapsulate this logic in a custom annotation and apply it to methods that require real‑time queries.

Annotation usage:

@MasterRoute
@Override
public UserBO getUser(Long id) {
    log.info("查询用户 [{}]", id);
    if (id == null) {
        throw new BizException(ResponseCode.PARAM_ERROR_CODE, "id不能为空");
    }
    UserDO userDO = userDao.getById(id);
    if (userDO == null) {
        throw new BizException(ResponseCode.NOT_FOUND_CODE);
    }
    return userBoConvert.convert(userDO);
}

Aspect setting:

@Aspect
public class MasterRouteAspect {
    @Around("@annotation(masterRoute)")
    public Object aroundGetConnection(final ProceedingJoinPoint pjp, MasterRoute masterRoute) throws Throwable {
        HintManager hintManager = HintManager.getInstance();
        hintManager.setMasterRouteOnly();
        try {
            return pjp.proceed();
        } finally {
            hintManager.close();
        }
    }
}

Transaction Operations

Within a transaction, the safest approach is to route all operations to the master to guarantee data consistency, especially when a write is followed by a read before the transaction commits.

Sharding‑JDBC also offers a more nuanced behavior: for the same thread and the same database connection, if a write occurs, subsequent reads are automatically routed to the master, while reads before any write can still use slaves, reducing master load.

Dynamic Forced Routing

Hard‑coding forced routing in annotations requires code changes and redeployment when new endpoints need master reads. A dynamic approach can leverage a configuration center to decide routing at runtime, setting hints in a filter without restarting the application.

Dynamic routing can also be achieved with aspect‑oriented programming to apply routing decisions at the business‑method level.

Traffic Distribution

Scenario 1: With one master and two slave nodes, read traffic may overload the slaves. Adding a third slave can relieve pressure, but if the master’s load is low, it might be unnecessary to add more slaves.

Scenario 2: When slaves have heterogeneous resources (e.g., an 8‑core/64 GB slave versus a 4‑core/32 GB slave), without custom traffic distribution, the weaker slave can become a bottleneck. Proper routing rules are needed to balance load according to each node’s capacity.

Sharding‑JDBC provides a read‑write split routing algorithm that can be customized to manage traffic distribution.

Implementation of a custom load‑balance algorithm:

public class KittyMasterSlaveLoadBalanceAlgorithm implements MasterSlaveLoadBalanceAlgorithm {
    private RoundRobinMasterSlaveLoadBalanceAlgorithm roundRobin = new RoundRobinMasterSlaveLoadBalanceAlgorithm();
    @Override
    public String getDataSource(String name, String masterDataSourceName, List
slaveDataSourceNames) {
        String dataSource = roundRobin.getDataSource(name, masterDataSourceName, slaveDataSourceNames);
        // control logic, e.g., assign different ratios to different slaves
        return dataSource;
    }
    @Override
    public String getType() {
        return "KITTY_ROUND_ROBIN";
    }
    @Override
    public Properties getProperties() {
        return roundRobin.getProperties();
    }
    @Override
    public void setProperties(Properties properties) {
        roundRobin.setProperties(properties);
    }
}

SPI configuration entries:

org.apache.shardingsphere.core.strategy.masterslave.RoundRobinMasterSlaveLoadBalanceAlgorithm
org.apache.shardingsphere.core.strategy.masterslave.RandomMasterSlaveLoadBalanceAlgorithm
com.cxytiandi.kitty.db.shardingjdbc.algorithm.KittyMasterSlaveLoadBalanceAlgorithm

Read‑write splitting configuration in Spring:

spring.shardingsphere.masterslave.load-balance-algorithm-class-name=com.cxytiandi.kitty.db.shardingjdbc.algorithm.KittyMasterSlaveLoadBalanceAlgorithm
spring.shardingsphere.masterslave.load-balance-algorithm-type=KITTY_ROUND_ROBIN

About the author: Yin Jihuan, a technology enthusiast, author of "Spring Cloud Microservices – Full‑Stack Technology and Case Analysis" and "Spring Cloud Microservices: Beginner, Practice and Advanced", and founder of the public account "Yuan Tiandi".

springMaster‑SlaveRead-Write SplittingDatabase ScalingSharding-JDBC
Full-Stack Internet Architecture
Written by

Full-Stack Internet Architecture

Introducing full-stack Internet architecture technologies centered on Java

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.