Let’s start with a piece of code from a recent code review to discuss cache and database consistency.

Explore the pre –

Generally speaking, cache is mainly used to improve application performance and reduce the direct load on DB. The final consistency scheme is acceptable in scenarios. If a business scenario requires strong consistency between cache and database, a synchronization scheme is required. For example, if exclusive locking or queuing + database transaction processing affects system availability, simply use…. Let’s do something else

The business scenario

  1. Commodity system Cache and DB data (or RPC operation inventory application), which roughly includes commodity basic attributes, inventory, price,
  2. Business features: read more and write less, the same commodity ID, less concurrent modification of basic attributes, more concurrent modification of inventory (inventory operation is more frequent when the order quantity is large)
  3. For Cache operation mode, use Cache Aside Pattern (photo)
  4. Pseudocode for specific use cases
public Ware getById(long id) {
        Ware ware = Cache.get(id);
        if(ware ! =null) {
            return ware;
        }
        ware = Db.get(id);
        if(ware ! =null) {// The cache duration is 12 hours, which can be adjusted based on service requirements
            Cache.put(ware,60*60*12);
        }
        return ware;
}

public void update(Param param) {
        Db.update(param);
        Cache.del(param.getId());
}
//Cache
public void del(long id) {
		// Exception silence
        CacheClient.expire("key1"+id, 0);
        CacheClient.expire("key2"+ id, 0);
}
Copy the code

The problem

The benefits of using Cache Aside Pattern include

  1. Q:Update DB: delete cache from DB; update DB: delete cache from DB; update DB: delete cache from DB.A:Given that getById and update are executed concurrently, updates are deleted first, but queries are placed concurrently, resulting in dirty data in the cache (too many reads and too few writes makes this situation more likely).
  2. Q:Update updates the DATABASE first and then deletes the cache. Why not update the cache directly to avoid cache breakdown caused by hot data?

A: 1. Considering that the update method itself is executed concurrently, db. update and cache. update are not atomic operations.

A: 2. The cache data of goods may contain multidimensional dimensions, such as inventory and price. A field of inventory is updated here

A: 3. The commodities updated this time may not be queried and used, such as cold data, so the cache will play A lazy loading effect during the search

A: 4. Cache breakdown problem, the update here is based on A single item, generally can be ignored, if the request volume is very high, for example, the second kill item needs to change the cache structure and special processing mode (such as version replacement mechanism, queue deduction mechanism, etc., later Bob will have A chance to explain…)

Problems not considered

  1. Db.update and cache.update are not atomic operations. If abnormal silence occurs, dirty data will appear in the Cache. If there is dirty data, what else can be done?
  2. If one of them works and the other one fails, you can see partial failures… How to do?
  3. When the UPDATE method is executed concurrently, multiple requests can still suffer from timing inconsistencies, such as updating first and placing caches later.
  4. In the case of parallel update and query, the query interface retrieves data from the DB to be placed in the cache, but the GC pauses, the update removes the cache, the query resumes the placed cache, and in extreme cases, dirty data can occur

solution

  1. If the cache expiration time is inconsistent, it is only within the cache expiration time. The shorter the expiration time is, the higher the data consistency is, but the more frequently the database is checked
  2. Versioning or locking can be used to maintain sequential consistency (throughput impact)
  3. To achieve final consistency we can compensate by introducing message queues, and instead of deleting the cache after the update we send messages asynchronously (technical complexity increases)
  4. Using binlog+ message queue (the project’s current solution), the binlog is parsed in sequence, sent to the message queue (using sequential queue, delayed consumption for a certain amount of time), and then the business system sequential consumption deletes the cache, which can achieve final consistency. Sequential consistency because the binlog is parsed sequentially and sent to the sequential queue. Can guarantee the sequential consistency, so our business can continue to retry if you remove the cache failure, why want to delay some time consumption, this is to ensure that the query and delete cache concurrency will be dirty data, because the delay some time, and query methods should finish this period of time, and then deleted, could improve the consistency of the
  5. Business cache dynamic isolation (for example, inventory is treated separately as a separate cache key and the underlying attributes), hot isolation (for example, kill items are treated in a special way)

subsequent

At the end of the day, we can see that rather than ensuring consistency, we are improving cache consistency

Problem analysis from the above business use scene, we can also see that under different scenarios in order to achieve the effect of different consistency requirements, throughput and concurrency) we have a different plan, the choice of these solutions is dependent on the scene, but at the same time we also want to combine technical complexity and team level, development maintenance cost consider to choose suitable solutions team.

reference

Summary of Redis use (1, some experience)

High Concurrency architecture series: Redis cache and MySQL data consistency solution

Facebook use delete to remove the key

Improving cache consistency