Generally speaking, a business goes through several phases

A single library



At the beginning of the traffic is small, read and write requests fall into the same library without pressure

Many libraries

With the development of business, the QPS of single library gradually becomes higher, and even appears the bottleneck of reading. At this point, you can distribute database read and write traffic to multiple instances

Depots table

Puts read and write requests into a specified library based on routing policies, such as hashing primary keys. Businesses can reduce complexity by integrating more mature middleware: ShardJdbc(recommended),Mycat, etc

However, this is not a perfect silver bullet, and you need to consider how to design the branch library first, so that hot data does not fall into the same instance. In addition, DML logic becomes more complex, such as sorting, distributed transactions, etc

Reading and writing separation

If the number of reads and writes is high and the number of writes is low, you can use this mode: The database is deployed in the master and slave mode. The write traffic falls into the master database and the read traffic falls into the slave database.

Then comes the master-slave delay problem. For consistency, see address

Database + cache

First, separate the read and write requests

Read requests

Read cache first, cache cannot read database, and set cache

Write requests

Cache writes are divided into two types: delete cache and update cache. Since the operation database and the operation cache are not atomic, you also need to consider the order in which the operations are performed. So there are four cases to consider:

  • Update the cache

– 1. Write data to the database and then update the cache

Counterexample: Database write succeeds but cache update fails

– 2. Update the cache before writing to the database

Counterexample: The cache update succeeds but the database write fails. Dirty data will be queried in the read request. Even if a cache restore operation is appended after a database write failure, the cache update fails.

  • Delete the cache

– 3. Write to the database before deleting the cache

Counterexample: The database write succeeds and the cache deletion fails

– 4. Delete the cache before writing to the database

Avoids the dirty data problem introduced by scenario (2) due to database write failures but successful cache updates. However, since cache deletion and database write operations are not atomic, there may be inconsistencies caused by parallel read and write requests


  1. A Deleting cache
  2. B reads the cache, since the cache has been deleted by A, reads the database and sets the cache key=X
  3. A Write database key=Y

To solve this problem, distributed locks can be introduced

  • Write requests

The lock is acquired, the cache is deleted and the database is updated, and the lock is released.

  • Read requests

Read cache data directly return; Unable to read cache data, try to acquire lock first

If the lock fails to be obtained, concurrent writes exist. After reading the lock, the database returns data directly. If the lock is obtained successfully, it indicates that no concurrent write exists. The database is read first, then the cache is set, and the lock is released.

Of course, some people might say, what if the id expires but the database hasn’t been updated yet, so the lock needs to support automatic renewal (see redisson’s implementation).

Make database operations and cache operations atomic

Based on the database binlog and update or delete the cache, even if the cache operation fails, can continue to replay. Common consumption such as Kafka, Ali Canal, etc. I won’t go into detail here.

However, if there is a multi-instance deployment on the consumer side, there will be out-of-order conditions when modifying multi-row records of a table or records of multiple tables.