How to implement flash sale system in redis
1. Design Ideas
The flash kill system is characterized by a large amount of concurrency. Tens of thousands of requests may come in in one second. If some means are not used, the system will It collapsed in minutes. Let’s discuss how to design a flash kill system that can be played.
1. Current limiting:
First of all, do not consider the business logic. If there is the following simplest interface:
@GetMapping("/test") public String test() { return "success"; }
Although this interface is very simple and does not have any logic, The server can also crash if there are thousands of requests accessing it simultaneously. Therefore, the first thing that a high-concurrency system should do is to limit the current flow. Springcloud projects can use hystrix for current limiting, and springcloud alibaba can use sentinel for current limiting. What about non-springcloud projects? Guava provides us with a RateLimiter tool class that can limit current. It mainly includes leaky bucket algorithm and token bucket algorithm.
-
Leaky Bucket Algorithm: A bucket with holes is filled with water under the faucet. It will leak a little when it is filled. However, if the water in the faucet is very large, the bucket will leak. Sooner or later, the water will overflow, and the flow will be limited when it overflows. This is suitable for limiting upload and download rates.
-
Token bucket algorithm: Put tokens into the bucket at a constant rate. Every time a request comes in, the token must be taken from the bucket first. If If the token is not obtained, the request is blocked. This is suitable for current limiting, that is, limiting QPS.
The token bucket algorithm should be used here to limit the flow. If the token is not obtained, a prompt of "too many people can't squeeze in" will be returned directly.
2. Check whether the user is logged in:
After the first step of current limiting, incoming requests should check whether the user is logged in. This project uses JWT, that is, first requests the login interface and returns after logging in. Token, request all other interfaces with token in the request header, and then you can get the user information through the token. When the user information is not obtained, the user is prompted to log in again: invalid token.
3. Check whether the product is sold out:
If the first two steps of verification are passed, you need to check whether the product is sold out. If it is sold out, a prompt message "Sorry" will be returned. , the product has been sold out instantly." Note that you cannot check the database to check whether the product is sold out, otherwise it will be very slow. You can use a dictionary to store product IDs, using the product ID as the key. If the item is sold out, set its value to True, otherwise to False.
4. Add the products participating in the flash sale to redis:
First get a key of ISINREDIS
to indicate whether the product has been added to redis to avoid each Repeat this operation for every request that comes in. If the ISINREDIS
value is false, it means that there are no flash sale products in redis. Then query all the products participating in the flash sale, use the product id as the key, and the product inventory as the value, and store them in redis. At the same time, use the product id as the key and false as the value, and put them into the map in the third step, indicating that the product is not sold. over. Finally, set the value of ISINREDIS
to true, which means that all the products participating in the flash sale have been added to redis.
5. Withholding inventory:
Use the decr function of redis to reduce the quantity of goods and judge the reduced value. If the result after self-decrement is less than 0, it means that the product has been sold out, then the value of the corresponding product ID in the map is set to true, and a prompt of "It's late, the product has been sold out" is returned.
6. Determine whether the flash sale is repeated:
If the user's flash sale is successful, after the flash sale order is stored in the database, the user id and product id will be used as the key, and true will be stored in redis as the value, indicating This user has already sold this product instantly. So here we go to redis based on the user ID and product ID to determine whether the flash sale is repeated. If so, the prompt "Do not repeat the flash sale" is returned.
7. Asynchronous processing:
If the above verifications pass, then the flash sale can be processed. If you deduct inventory and create orders for every flash sale request, not only will the speed be very slow, but it may also cause the database to crash. So we can process it asynchronously, that is, after passing the above verification, the user ID and product ID will be sent to MQ as messages, and then a "queuing" prompt will be returned to the user immediately. Then consume the message on the consumer side of MQ, get the user ID and product ID, and query the inventory based on the product ID to ensure sufficient inventory again; then you can also determine whether to repeat the flash sale. After passing the judgment, operate the database, deduct inventory, and create a flash sale order. Note that deducting inventory and creating flash sale orders need to be in the same transaction.
8. Oversold problem:
The oversold problem is a negative inventory of goods. For example, if the inventory is 1, then 10 users sell instantly at the same time. When judging the inventory, they are all 1, so 10 people can place orders successfully, and the final inventory is -9. How to solve? In fact, such a problem will not occur in this system at all, because redis is used to pre-reduce inventory at the beginning, and the redis command core module is single-threaded, so it can be guaranteed not to be oversold. If redis is not used, you can also add a version field to the product. Check the version before deducting inventory each time. Add a condition to the sql for deducting inventory, that is, the version must be equal to the version just found.
二、核心代码
@RestController @RequestMapping("/seckill") public class SeckillController { @Autowired private UserService userService; @Autowired private SeckillService seckillService; @Autowired private RabbitMqSender mqSender; // 用来标记商品是否已经加入到redis中的key private static final String ISINREDIS = "isInRedis"; // 用goodsId作为key,标记该商品是否已经卖完 private Map<integer> seckillOver = new HashMap<integer>(); // 用RateLimiter做限流,create(10),可以理解为QPS阈值为10 private RateLimiter rateLimiter = RateLimiter.create(10); @PostMapping("/{sgId}") public JsonResult> seckillGoods(@PathVariable("sgId") Integer sgId, HttpServletRequest httpServletRequest){ // 1. 如果QPS阈值超过10,即1秒钟内没有拿到令牌,就返回“人太多了,挤不进去”的提示 if (!rateLimiter.tryAcquire(1, TimeUnit.SECONDS)) { return new JsonResult(SeckillGoodsEnum.TRY_AGAIN.getCode(), SeckillGoodsEnum.TRY_AGAIN.getMessage()); } // 2. 检查用户是否登录(用户登录后,访问每个接口都应该在请求头带上token,根据token再去拿user) String token = httpServletRequest.getHeader("token"); String userId = JWT.decode(token).getAudience().get(0); User user = userService.findUserById(Integer.valueOf(userId)); if (user == null) { return new JsonResult(SeckillGoodsEnum.INVALID_TOKEN.getCode(), SeckillGoodsEnum.INVALID_TOKEN.getMessage()); } // 3. 如果商品已经秒杀完了,就不执行下面的逻辑,直接返回商品已秒杀完的提示 if (!seckillOver.isEmpty() && seckillOver.get(sgId)) { return new JsonResult(SeckillGoodsEnum.SECKILL_OVER.getCode(), SeckillGoodsEnum.SECKILL_OVER.getMessage()); } // 4. 将所有参加秒杀的商品信息加入到redis中 if (!RedisUtil.isExist(ISINREDIS)) { List<seckillgoods> goods = seckillService.getAllSeckillGoods(); for (SeckillGoods seckillGoods : goods) { RedisUtil.set(String.valueOf(seckillGoods.getSgId()), seckillGoods.getSgSeckillNum()); seckillOver.put(seckillGoods.getSgId(), false); } RedisUtil.set(ISINREDIS, true); } // 5. 先自减,预扣库存,判断预扣后库存是否小于0,如果是,表示秒杀完了 Long stock = RedisUtil.decr(String.valueOf(sgId)); if (stock (SeckillGoodsEnum.SECKILL_OVER.getCode(), SeckillGoodsEnum.SECKILL_OVER.getMessage()); } // 6. 判断是否重复秒杀(成功秒杀并创建订单后,会将userId和goodsId作为key放到redis中) if (RedisUtil.isExist(userId + sgId)) { return new JsonResult(SeckillGoodsEnum.REPEAT_SECKILL.getCode(), SeckillGoodsEnum.REPEAT_SECKILL.getMessage()); } // 7. 以上校验都通过了,就将当前请求加入到MQ中,然后返回“排队中”的提示 String msg = userId + "," + sgId; mqSender.send(msg); return new JsonResult(SeckillGoodsEnum.LINE_UP.getCode(), SeckillGoodsEnum.LINE_UP.getMessage()); } } </seckillgoods></integer></integer>
三、压测
用jmeter模拟并发请求,测试高并发情况下系统能否扛得住。由于只有一个id为1的商品,所以商品id固定写死1。但是每个用户都要先请求登录接口获取到token才能进行秒杀请求,有点儿麻烦,所以可以先把jwt模块注释掉,把userId当成参数传进去。jmeter配置如下图:


The above is the detailed content of How to implement flash sale system in redis. For more information, please follow other related articles on the PHP Chinese website!

Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Hot Topics











Redis cluster mode deploys Redis instances to multiple servers through sharding, improving scalability and availability. The construction steps are as follows: Create odd Redis instances with different ports; Create 3 sentinel instances, monitor Redis instances and failover; configure sentinel configuration files, add monitoring Redis instance information and failover settings; configure Redis instance configuration files, enable cluster mode and specify the cluster information file path; create nodes.conf file, containing information of each Redis instance; start the cluster, execute the create command to create a cluster and specify the number of replicas; log in to the cluster to execute the CLUSTER INFO command to verify the cluster status; make

How to clear Redis data: Use the FLUSHALL command to clear all key values. Use the FLUSHDB command to clear the key value of the currently selected database. Use SELECT to switch databases, and then use FLUSHDB to clear multiple databases. Use the DEL command to delete a specific key. Use the redis-cli tool to clear the data.

To read a queue from Redis, you need to get the queue name, read the elements using the LPOP command, and process the empty queue. The specific steps are as follows: Get the queue name: name it with the prefix of "queue:" such as "queue:my-queue". Use the LPOP command: Eject the element from the head of the queue and return its value, such as LPOP queue:my-queue. Processing empty queues: If the queue is empty, LPOP returns nil, and you can check whether the queue exists before reading the element.

On CentOS systems, you can limit the execution time of Lua scripts by modifying Redis configuration files or using Redis commands to prevent malicious scripts from consuming too much resources. Method 1: Modify the Redis configuration file and locate the Redis configuration file: The Redis configuration file is usually located in /etc/redis/redis.conf. Edit configuration file: Open the configuration file using a text editor (such as vi or nano): sudovi/etc/redis/redis.conf Set the Lua script execution time limit: Add or modify the following lines in the configuration file to set the maximum execution time of the Lua script (unit: milliseconds)

Use the Redis command line tool (redis-cli) to manage and operate Redis through the following steps: Connect to the server, specify the address and port. Send commands to the server using the command name and parameters. Use the HELP command to view help information for a specific command. Use the QUIT command to exit the command line tool.

Redis counter is a mechanism that uses Redis key-value pair storage to implement counting operations, including the following steps: creating counter keys, increasing counts, decreasing counts, resetting counts, and obtaining counts. The advantages of Redis counters include fast speed, high concurrency, durability and simplicity and ease of use. It can be used in scenarios such as user access counting, real-time metric tracking, game scores and rankings, and order processing counting.

In Debian systems, readdir system calls are used to read directory contents. If its performance is not good, try the following optimization strategy: Simplify the number of directory files: Split large directories into multiple small directories as much as possible, reducing the number of items processed per readdir call. Enable directory content caching: build a cache mechanism, update the cache regularly or when directory content changes, and reduce frequent calls to readdir. Memory caches (such as Memcached or Redis) or local caches (such as files or databases) can be considered. Adopt efficient data structure: If you implement directory traversal by yourself, select more efficient data structures (such as hash tables instead of linear search) to store and access directory information

There are two types of Redis data expiration strategies: periodic deletion: periodic scan to delete the expired key, which can be set through expired-time-cap-remove-count and expired-time-cap-remove-delay parameters. Lazy Deletion: Check for deletion expired keys only when keys are read or written. They can be set through lazyfree-lazy-eviction, lazyfree-lazy-expire, lazyfree-lazy-user-del parameters.
