Alice52 / database

ddf13ad8d4be76a80a336418b5cf5727bf6e3059
gitee.com
MIT License
0 stars 0 forks source link

[distributed] lock #58

Open Alice52 opened 4 years ago

Alice52 commented 4 years ago

non-distributed lock

  1. core of lock
    • whether is same lock
  2. method to realize
    • juc: ReentrantLock
    • synchronized method
    • synchronized partical code

      the evolution of non-distributed locks[Redis]

  3. core of distributed lock
    • the lock operation should be atomic, such as get and set value in Redis
    • lock should expire
    • del should be atomic, get and del
  4. evolution

    • version 1

      // judge wether has lock, if none add lock
      public void addLock() {
      String lockName = "lock";
      // question: if many thread enter, and will get lock is nil, so it not thread-safe
      String lock = getFromRedis(lockName);
      if (lock == null) {
          setRedisKey(lockName, "value");
          // exec logic business
          del(lockName);
          return;
      } else {
          // spin
          addLock(); // can modify with Thread sleep
      }
      }
      // solution: makes judgment and locking an atomic operation 
    • version 2: get and set lock in Redis is atomic

      public void addLock() {
      String lockName = "lock";
      // makes judgment and locking an atomic operation 
      Integer lock = setnx(lockName);
      if (lock == null) {
          // question: if sever is down or unhandle exception here, others thread will wait for lock forever
          // exec logic business
          del(lockName);
          return;
      } else {
          // spin
          addLock()
      }
      }
      // solurion: add lock expire for lock key in Redis
    • version 3: add lock expire

    public void addLock() {
        String lockName = "lock";
        // makes judgment and locking an atomic operation 
        Integer lock = setnx(lockName);
        if (lock == null) {
            // question: if sever is down here, others thread will wait for lock forever
            expire(lockName);
            // exec logic business
            del(lockName);
            return;
        } else {
            // spin
            addLock()
        }
    }
    // solution: make get , set and expire is atomic operation
    • version 4: make get , set and expire is atomic operation
      public void addLock() {
      String lockName = "lock";
      // makes judgment, locking and expire key an atomic operation 
      String lock = setnxex(lockName, expireTime);
      if (lock == "ok") {
          // exec logic business
          // question:  if logic is time-consuming operation and Redis key is expire, so this thread will delete the lock, which is adding by other thread
          del(lockName); // get then del, so they should be atomic 
          return;
      } else {
          // spin
          addLock()
      }
      }
      // solution: make thread just can delete itself lock, and make get then del an atomic operation
    • version 5: make thread just can delete itself lock, and make get then del an atomic operation[lua]
    public void addLock() {
        String threadId = Thread.getCurrentThread().getName();
        String lockName = "lock";
        String luaScript = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"
        Integer lock = setnxex(lockName, timeout);
    
        if (lock = "ok") {
            // exec logic business
            delWithLua(lockName, lockName, threadId);
            return;
        } else {
            // spin
            addLock()
        }
    }
    // modify: can implement with AOP

    more think about lock

    • spin: times and timeout
    • cache: 击穿, 穿透, 雪崩
    • granularity: different pro should has itself lock[lock name should be relative with pro]
    • readwrite lock

use redisson

Alice52 commented 4 years ago

About thread safe in Redis

redisson

  1. https://javamana.com/2021/01/20210111224852022P.html