Coupon system activity inventory is generally divided into: total inventory and daily inventory. When a user comes to claim the coupon, it is necessary to judge whether the current remaining total inventory and daily inventory are sufficient. If so, inventory deduction will be made; otherwise, the user will be prompted to fail to claim. Gross inventory and daily inventory reduction is an atomic operation that either succeeds or fails. We know that database transactions satisfy the “ACID” property, so we can put these two operations together in one transaction.

Original stage inventory design

In the initial stage, the number of users in the system is small, and the flow of coupon receiving request is not large. We can put the total inventory and daily inventory of an activity into the same MySQL database. The total inventory for an activity corresponds to one record, and the daily inventory for an activity has one daily inventory record for each day of the activity. When the user comes to claim the coupon, we start a transaction, which first deducts the total inventory, then deducts the daily inventory. If either step fails, the transaction is rolled back, and the front end indicates that the user failed to claim the coupon.

Considering the scalability of the system database module, we adopt the way of “depots table” data “sharding”, according to the specific business ID to depots, for example, we can according to the activity to fragmentation of our data, so that the same activity of total inventory and inventory on a database, you can make a transaction. We do not use distributed transactions for performance reasons.

The advantages of this system design are: simple implementation, strong consistency of inventory (guaranteed by transactions); However, the disadvantage is obvious: when a transaction is started, MySQL adds a row lock (InnoDB storage engine) to the total stock and daily stock. The row lock is exclusive and all other transactions must wait before a transaction is committed. Therefore, although our system enables multi-threading to process requests concurrently in the coupon process, all requests are still “serialized” when updating the database. Assuming that it takes 10ms for us to update the total inventory and daily inventory, the maximum “TPS” of the system for each activity is only 100, which is unbearable for some “hot” activities with high concurrent receipt.

Redis in-memory database inventory deduction

Redis is a high-performance distributed caching system that can be used as both a cache and an in-memory database. It is characterized by extremely high performance because the operations are pure memory operations. Up to 10W QPS can be achieved on a single machine, which is enough for a typical system.” Every coin has two sides”, although the performance of Redis is very high, but it also has disadvantages, for example, Redis transactions can not guarantee the “ACID” property of MySQL transactions, Redis transactions guarantee the consistency (C) and isolation (I) of the ACID. But atomicity (A) and persistence (D) are not guaranteed. This may cause problems, for example, when deducting inventory, the total inventory is deducted first and then the daily inventory is deducted. Assume that total inventory deduction succeeds and daily inventory deduction fails. Redis does not have a rollback mechanism, so there is no way to roll back the total inventory even if the transaction fails, causing problems.

High availability of Redis

Redis supports cluster deployment. A Redis cluster has 16384 slots. You can calculate the CRC16 checksum of the key and obtain the slot (CRC16(key) % 16384) in which the key locates. When a single Redis node can not support, the Redis cluster can be considered to achieve inventory deduction. For master/slave replication, Sentinel is used for failover between master and slave nodes.

Redis synchronously deducts inventory, asynchronously synchronizes to database solutions

Although Redis provides a persistence scheme (RDB, AOF), Redis itself may not be enough for some important business data, and we need to asynchronously synchronize the inventory data recorded in Redis to the database. In extreme cases Redis dies and the database acts as “credentials”. Therefore, depending on your business needs, consider starting a background task that periodically synchronizes inventory data recorded in Redis to the database.