“This is the 15th day of my participation in the Gwen Challenge in November. Check out the details: The Last Gwen Challenge in 2021.”
Solutions to concurrent problems
- The first thing that comes to mind is locking, such as mysql locking, to solve the concurrency problem. There are many articles of this kind, so I will not repeat them.
- After all, the performance of NOSQL is much faster than that of mysql and other relational databases. The concurrency level of NOSQL lock is higher than that of mysql and other relational databases.
Nosql lock case
- My business scenario is that I log in the APP for the first time every day and send coupons
- I have added the judgment of existence in the business code. If coupons from the same source have been delivered to the same user at the same time today, they will not be delivered again
- Because the business needs to be the same type of coupon from the same source can be delivered to the same person at the same time, we can’t use a unique index of MySQL
- The judgment we mentioned in [2] can only limit requests in non-concurrent cases, and large requests do not work.
- In this case, you can use noSQL lock ideas, such as redis lock:
- Lock at the start of a business
- Execute business code
- Locks are released after services are executed
- Principle: NoSQL locks at a higher concurrency level than relational databases such as mysql.
The sample code
Unlocked code
Public static function everydayTriggerCoupon($userId) {$userVip = userVip ::getByUserid($userID, 'type'); If ($userVip['type'] == VipInfo::TYPE_USER_NORMAL) {// If (! self::checkCouponExist($userid, CouponInfo::PROP_COUPON_SUBSCRIBE_APPOINTMENT_ID, CouponInfo::TYPE_COUPON_SOURCE_DAILY_BENEFITS, Utility::getTomorrowTimestamp())) { self::saveCoupon($userid, CouponInfo::PROP_COUPON_SUBSCRIBE_APPOINTMENT_ID, CouponInfo::TYPE_COUPON_SOURCE_DAILY_BENEFITS, Utility::getTomorrowTimestamp(), CouponInfo::COUNT_COUPON_EVERYDAY_TRIGGER_NOT_VIP); }} else {// Member will issue 5 booking tickets if (! self::checkCouponExist($userid, CouponInfo::PROP_COUPON_SUBSCRIBE_APPOINTMENT_ID, CouponInfo::TYPE_COUPON_SOURCE_DAILY_VIP_BENEFITS, Utility::getTomorrowTimestamp())) { self::saveCoupon($userid, CouponInfo::PROP_COUPON_SUBSCRIBE_APPOINTMENT_ID, CouponInfo::TYPE_COUPON_SOURCE_DAILY_VIP_BENEFITS, Utility::getTomorrowTimestamp(), CouponInfo::COUNT_COUPON_EVERYDAY_TRIGGER_VIP); } // Members will receive an additional 1 super like coupon if (! self::checkCouponExist($userid, CouponInfo::PROP_COUPON_SUPER_LIKE_ID, CouponInfo::TYPE_COUPON_SOURCE_DAILY_VIP_BENEFITS, Utility::getTomorrowTimestamp())) { self::saveCoupon($userid, CouponInfo::PROP_COUPON_SUPER_LIKE_ID, CouponInfo::TYPE_COUPON_SOURCE_DAILY_VIP_BENEFITS, Utility::getTomorrowTimestamp()); }}}Copy the code
Add lock code
- Based on business requirements, coupons are issued at the first login every day, which is valid on the same day. Therefore, the current date is taken as one of the parameters when setting the key
- Since I’m using the Laravel framework, Cache integrates Redis, Cache add and Redis
setnx
The key returns 0, but the key does not return 1. - For our business scenario, I added an expiration time of 24 hours to add(); Of course, if your scenario does not invite expiration time, you can actively release cache resources after the business code has finished executing.
Note: Always remember to release lock resources under appropriate circumstances to avoid resource abuse
Public static function everydayTriggerCoupon($userId) {$userVip = userVip ::getByUserid($userID, 'type'); $cacheKey = cacheKey ::getCacheKey(cacheKey ::TYPE_USER_REWARD_EVERYDAY_TRIGGER, $userid . '_' . date('Y-m-d')); $res = Cache::add($cacheKey, true, 60 * 60 * 24); //add() does not exist to return true; If ($res) {return false if ($res) {same as unlocked core code..}}Copy the code
Nosql lock in asynchronous tasks
If we have concurrency problems with asynchronous tasks, we can also consider introducing noSQL locks to solve the problem.
Note: if the asynchronous task is single-threaded and executed sequentially, add a lock to the method that calls the asynchronous task. Do not add a lock to the method that calls the asynchronous task to avoid blocking.
The sample code
$lockKey = CacheKey::getCacheKey(CacheKey::TYPE_JOB_USER_EDIT_AVOID_CONCURRENT, $userid); $lockRes = Cache::add($lockKey, true, 60); if ($lockRes) { UserInfoEdit::dispatch([ 'userid' => $userid, . . . ])->onQueue(QueueNameBuilder::getName(QueueNameBuilder::USER_INFO_EDIT)); // Unlock the redis Cache::forget($lockKey); }Copy the code
Last but not least
Technical group please come here. Or add my wechat account wangzhongyang0601 to learn together.
Thank you for your likes, comments and followings. Thank you for your support. Thank you.