Cache optimization and practice of ten million shopping cart system

Posted by mitwess on Fri, 18 Feb 2022 10:21:21 +0100

1, Analysis of shopping cart data model in E-commerce

Functions realized in shopping cart: add shopping cart, view shopping cart, edit shopping cart sku, edit commodity data, select multiple commodities in shopping cart to settle, view the total number of commodities in shopping cart, delete shopping cart, share shopping cart, etc

Current shopping cart implementation, synchronous write library + synchronous (asynchronous) write cache

For shopping carts, if you are engaged in some activities (spelling out orders, full reduction and other non single goods must be placed in the shopping cart) - write more and read more in the shopping cart. High concurrent writes may occur in the shopping cart, and the write operations of the shopping cart fall into the library synchronously, which may lead to great pressure on the library (slow sql occasionally occurs when the traffic is high at 10 o'clock every day).

Solution: the shopping cart can write more and read more, write a lot of high concurrency, and read a lot of high concurrency. The main data storage can be transformed into redis cache. Based on redis as the main storage, high-performance read and write can synchronize the data to mysql persistence asynchronously.

2, Practice of multiple redis data structures of shopping cart (practice of shopping cart complex cache and asynchronous drop)

The shopping cart has multiple write and read scenarios. The cache is used as the main data storage, which is resistant to high concurrent write and read, and the storage is asynchronous. Generally speaking, the commodity system will also use the cache architecture to provide the implementation of the read interface.

At the same time, the shopping cart needs to contain a lot of data. It is divided into multiple cache key s for cache operation.

// Select the hash structure in redis to update the commodity extension information in the cache
cart_hash {
        {skuid1}:cartSkuInfo;
        {skuid2}:cartSkuInfo;
}

// Update number in cache
cart_hash { cartType_userId
        {skuid1}:2;
        {skuid2}:1;
}

// Update sorted set
// Write each skuid and the time to add to the shopping cart to the sorted set
sorted set [{skuId -> score(time)}]

// Shopping cart cache model:
hash:{skuId -> count} ,hash :{skuId -> cartSkuInfo}, zset : [{skuId -> timestamp}]

3, The message of asynchronous drop of shopping cart is lost and inconsistent

The main data storage of shopping cart is realized through redis. There is no inconsistency between writing and reading redis (without collapse)

Using redis data storage, if the whole redis cluster crashes, it will lead to the loss of the main data of our shopping cart. The mysql database will be degraded to provide the write and read of the shopping cart. After the cache is restored, the cache will be pre warmed and the data of the database will be loaded into the cache.

case1: mq system or mysql crashes

If we just finished writing the cache and haven't had time to send the message to mq, the mq system crashes, resulting in the successful writing of the cache, but the asynchronous message doesn't pass, mq has some faults, resulting in the unsuccessful sending of the message, there is data in redis, and there is no message and data in mq and database, resulting in the inconsistency between redis and mysql data, As long as there is data in redis, because reading and writing redis, mysql only makes backups, and mysql needs incremental backups

case2: redis crashes

redis crashes, but mysql fails to synchronize. As a result, a piece of data is lost (temporary storage space, in extreme cases, does not affect the use of the main business). In terms of business, the shopping cart is temporary data. It is just to temporarily store some goods in the shopping cart. Sooner or later, you will buy or forget them. Otherwise, you will directly initiate the purchase, and these data will have to be deleted for a long time, The user doesn't know what to add. In extreme cases, there are fewer goods and users need to add shopping carts again.

4, Solving the multithreading concurrency problem of adding goods to shopping cart

Using distributed locks provided by redis:

String updateCartLockKey = RedisLockKeyConstants.UPDATE_CART_LOCK_KEY +userId+skuId;
boolean locked = redisLock.blockedlock(updateCartLockKey);
if(!locked) {
    throw new BaseBizException("Failed to add the item to the shopping cart. The item already exists in the shopping cart");|
}

try{

} catch(){
} finally {
      redisLock. unlock (RedisLockKeyConstants.UPDATE_CART_LOCK_ KEY);
}

5, Application of new cache data structure, shopping cart query function practice

Query cache operation:

// Get an ordered list of product ID s from the cache
// Find out all the data in the sorted set. When writing data, it has been sorted by default
Set<String> orderSkuIds = redisCache.zrevrange(RedisKeyConstant.SHOPPING_CART_ZSET+userId,INDEX,END);

// Get shopping cart information
Map<String, String> cartInfo = redisCache.hGetAll(RedisKeyConstant.SHOPPING_CART_EXT+userId;

// sorted set + hash
// First query the skuId collection of goods sorted by time, and each skuId corresponds to the commodity information
// Reordering into objects
...

// redisLock is required for all operations except query

Topics: Java Redis Cache