Get the App
SLTechnology News&Howtos  ›  Database  › 

How to understand the distributed lock in Redis

Shulou Source: shulou.com Published: 2022-05-31 16:08:59 09月30日 Update

This article introduces the knowledge of "how to understand distributed locks in Redis". Many people will encounter this dilemma in the operation of actual cases, so let the editor lead you to learn how to deal with these situations. I hope you can read it carefully and be able to achieve something!

Redis distributed lock

Distributed locks are used in all projects, which are usually used for orderly operation of data, such as a refund of an order (if it can be returned multiple times). Or users place orders at multiple ends. [related recommendation: Redis video tutorial]

Maven dependence

My implementation is mainly based on Spring-Boot 2.1.2 + Jedis.

4.0.0 org.springframework.boot spring-boot-starter-parent 2.1.2.RELEASE cn.edu.cqvie redis-lock 1.0-SNAPSHOT UTF-8 1.8 2.9.0 5.0.7 org.springframework.boot spring-boot-autoconfigure Org.springframework.data spring-data-redis redis.clients jedis ${redis.version} org.springframework.boot spring-boot-starter-logging org.slf4j log4j-over-slf4j Org.springframework.boot spring-boot-starter-test test org.springframework.boot spring-boot-maven-plugin

Configuration file

The application.properties configuration file is as follows:

Spring.redis.host=127.0.0.1spring.redis.port=6379spring.redis.password=spring.redis.timeout=30000spring.redis.jedis.pool.max-active=8spring.redis.jedis.pool.min-idle=2spring.redis.jedis.pool.max-idle=4logging.level.root=INFO

Interface definition

Interface definition, for the lock our core is actually even the methods lock and unlock.

Public interface RedisLock {long TIMEOUT_MILLIS = 30000; int RETRY_MILLIS = 30000; long SLEEP_MILLIS = 10; boolean tryLock (String key); boolean lock (String key); boolean lock (String key, long expire); boolean lock (String key, long expire, long retryTimes); boolean unlock (String key);}

Distributed lock implementation

My implementation is done through setnx. If there is tryLock logic, I will retry it by spinning.

/ / AbstractRedisLock.java abstract class public abstract class AbstractRedisLock implements RedisLock {@ Override public boolean lock (String key) {return lock (key, TIMEOUT_MILLIS);} @ Override public boolean lock (String key, long expire) {return lock (key, TIMEOUT_MILLIS, RETRY_MILLIS);}} / / concrete implementation @ Componentpublic class RedisLockImpl extends AbstractRedisLock {private Logger logger = LoggerFactory.getLogger (getClass ()); @ Autowired private RedisTemplate redisTemplate Private ThreadLocal threadLocal = new ThreadLocal (); private static final String UNLOCK_LUA; static {StringBuilder sb = new StringBuilder (); sb.append ("if redis.call (\" get\ ", KEYS [1]) = = ARGV [1]"); sb.append ("then"); sb.append ("return redis.call (\" del\ ", KEYS [1])"); sb.append ("else") Sb.append ("return 0"); sb.append ("end"); UNLOCK_LUA = sb.toString ();} @ Override public boolean tryLock (String key) {return tryLock (key, TIMEOUT_MILLIS) } public boolean tryLock (String key, long expire) {try {return! StringUtils.isEmpty (redisTemplate.execute ((RedisCallback) connection-> {JedisCommands commands = (JedisCommands) connection.getNativeConnection (); String uuid = UUID.randomUUID (). ToString (); threadLocal.set (uuid); return commands.set (key, uuid, "NX", "PX", expire) });} catch (Throwable e) {logger.error ("set redis occurred an exception", e);} return false;} @ Override public boolean lock (String key, long expire, long retryTimes) {boolean result = tryLock (key, expire) While (! result & & retryTimes-- > 0) {try {logger.debug ("lock failed, retrying... {}", retryTimes); Thread.sleep (SLEEP_MILLIS);} catch (InterruptedException e) {return false;} result = tryLock (key, expire);} return result @ Override public boolean unlock (String key) {try {List keys = Collections.singletonList (key); List args = Collections.singletonList (threadLocal.get ()); Long result = redisTemplate.execute ((RedisCallback) connection-> {Object nativeConnection = connection.getNativeConnection () If (nativeConnection instanceof JedisCluster) {return (Long) ((JedisCluster) nativeConnection) .eval (UNLOCK_LUA, keys, args);} if (nativeConnection instanceof Jedis) {return (Long) ((Jedis) nativeConnection) .eval (UNLOCK_LUA, keys, args);} return 0L }); return result! = null & & result > 0;} catch (Throwable e) {logger.error ("unlock occurred an exception", e);} return false;}}

Test code

Finally, let's look at how to use it. (here is a scene that simulates a second kill)

@ RunWith (SpringRunner.class) @ SpringBootTestpublic class RedisLockImplTest {private Logger logger = LoggerFactory.getLogger (getClass ()); @ Autowired private RedisLock redisLock; @ Autowired private StringRedisTemplate redisTemplate; private ExecutorService executors = Executors.newScheduledThreadPool (8); @ Testpublic void lock () {/ / initialize inventory redisTemplate.opsForValue (). Set ("goods-seckill", "10"); List futureList = new ArrayList (); for (int I = 0; I)

< 100; i++) { futureList.add(executors.submit(this::seckill)); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } // 等待结果,防止主线程退出 futureList.forEach(action ->

{try {action.get ();} catch (InterruptedException | ExecutionException e) {e.printStackTrace ();}} public int seckill () {String key = "goods"; try {redisLock.lock (key) Int num = Integer.valueOf (Objects.requireNonNull (redisTemplate.opsForValue (). Get ("goods-seckill")); if (num > 0) {redisTemplate.opsForValue () .set ("goods-seckill", String.valueOf (--num)); logger.info ("second kill, remaining inventory: {}", num) } else {logger.error ("second kill failed, remaining inventory: {}", num);} return num;} catch (Throwable e) {logger.error ("seckill exception", e);} finally {redisLock.unlock (key);} return 0;}} Summary

This paper is a simple implementation of Redis lock, and the retry operation of lock is realized based on jedis. However, there are some disadvantages, such as automatic renewal of locks, reentry of locks, and fairness (currently achieved through spin, which is equivalent to an unfair way).

That's all for "how to understand distributed locks in Redis". Thank you for reading. If you want to know more about the industry, you can follow the website, the editor will output more high-quality practical articles for you!

Tags: Mode distributed content inventory scenario situation interface file more knowledge surplus configuration practical orderly successful successful next code fairness dilemma Apple Docker Huawei Linux macOS MariaDB Microsoft MySQL NVidia OPPO Reno MySQL Huawei Shulou Technology Xiaomi Apple