In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-02-28 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >
Share
Shulou(Shulou.com)06/02 Report--
This article will explain in detail how to implement distributed locks. The editor thinks it is very practical, so I share it for you as a reference. I hope you can get something after reading this article.
There are three ways to realize distributed lock: 1, to realize distributed lock based on database, 2, to realize distributed lock based on cache (Redis, etc.), and 3, to realize distributed lock based on Zookeeper. From a performance point of view (from high to low): "caching mode > Zookeeper mode > = database mode".
There are three ways to implement distributed locks:
1. Implementation of distributed Lock based on Database
two。 Implementation of distributed lock based on cache (Redis, etc.)
3. Implementation of distributed Lock based on Zookeeper
First, implement distributed lock based on database
1. Pessimistic lock
Take advantage of select... Where... For update exclusive lock
Note: other additional functions are basically the same as the implementation. What you need to note here is "where name=lock". The name field must be indexed, otherwise the table will be locked. In some cases, such as a small table, the mysql optimizer will not move the index, resulting in table locking problems.
two。 Optimistic lock
The biggest difference between the so-called optimistic lock and the previous one is that based on the idea of CAS, it is not mutually exclusive, it will not produce lock waiting and consume resources, and it is considered that there is no concurrency conflict in the process of operation, which can only be detected after the failure of update version. Our rush to buy, second kill is to use this kind of implementation to prevent overselling.
Optimistic locking is achieved by adding an incremental version number field
Second, implement distributed lock based on cache (Redis, etc.)
1. Use the command to introduce:
(1) SETNX
SETNX key val: if and only if key does not exist, set a string whose key is val and returns 1; if key exists, it does nothing and returns 0.
(2) expire
Expire key timeout: set a timeout for key in second, after which the lock will be released automatically to avoid deadlock.
(3) delete
Delete key: deleting key
These three commands are mainly used when using Redis to implement distributed locks.
two。 Realize the idea:
(1) when acquiring a lock, use setnx to add a lock, and use the expire command to add a timeout for the lock. After this time, the lock is automatically released. The value of the lock is a randomly generated UUID, which is used to determine when the lock is released.
(2) when acquiring the lock, it also sets an acquisition timeout, and if it exceeds this time, the acquisition lock is abandoned.
(3) when releasing the lock, it is judged by UUID whether it is the lock or not. If it is the lock, delete is executed to release the lock.
3. Simple implementation code for distributed locks:
/ * simple implementation code of distributed lock * / public class DistributedLock {private final JedisPool jedisPool; public DistributedLock (JedisPool jedisPool) {this.jedisPool = jedisPool } / * * Lock * @ param lockName lock key * @ param acquireTimeout get timeout * @ param timeout lock timeout * @ return lock ID * / public String lockWithTimeout (String lockName, long acquireTimeout, long timeout) {Jedis conn = null; String retIdentifier = null Try {/ / get connection conn = jedisPool.getResource (); / / randomly generate a value String identifier = UUID.randomUUID () .toString (); / / lock name, that is, the key value String lockKey = "lock:" + lockName / / timeout, after which the lock is automatically released int lockExpire = (int) (timeout /); / / the timeout of the lock is obtained. If the timeout is exceeded, the lock long end = System.currentTimeMillis () + acquireTimeout; while (System.currentTimeMillis ()) is abandoned
< end) { if (conn.setnx(lockKey, identifier) == ) { conn.expire(lockKey, lockExpire); // 返回value值,用于释放锁时间确认 retIdentifier = identifier; return retIdentifier; } // 返回-代表key没有设置超时时间,为key设置一个超时时间 if (conn.ttl(lockKey) == -) { conn.expire(lockKey, lockExpire); } try { Thread.sleep(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } catch (JedisException e) { e.printStackTrace(); } finally { if (conn != null) { conn.close(); } } return retIdentifier; } /** * 释放锁 * @param lockName 锁的key * @param identifier 释放锁的标识 * @return */ public boolean releaseLock(String lockName, String identifier) { Jedis conn = null; String lockKey = "lock:" + lockName; boolean retFlag = false; try { conn = jedisPool.getResource(); while (true) { // 监视lock,准备开始事务 conn.watch(lockKey); // 通过前面返回的value值判断是不是该锁,若是该锁,则删除,释放锁 if (identifier.equals(conn.get(lockKey))) { Transaction transaction = conn.multi(); transaction.del(lockKey); List results = transaction.exec(); if (results == null) { continue; } retFlag = true; } conn.unwatch(); break; } } catch (JedisException e) { e.printStackTrace(); } finally { if (conn != null) { conn.close(); } } return retFlag; } } 4. 测试刚才实现的分布式锁 例子中使用50个线程模拟秒杀一个商品,使用-运算符来实现商品减少,从结果有序性就可以看出是否为加锁状态。 模拟秒杀服务,在其中配置了jedis线程池,在初始化的时候传给分布式锁,供其使用。 public class Service { private static JedisPool pool = null; private DistributedLock lock = new DistributedLock(pool); int n = 500; static { JedisPoolConfig config = new JedisPoolConfig(); // 设置最大连接数 config.setMaxTotal(200); // 设置最大空闲数 config.setMaxIdle(8); // 设置最大等待时间 config.setMaxWaitMillis(1000 * 100); // 在borrow一个jedis实例时,是否需要验证,若为true,则所有jedis实例均是可用的 config.setTestOnBorrow(true); pool = new JedisPool(config, "127.0.0.1", 6379, 3000); } public void seckill() { // 返回锁的value值,供释放锁时候进行判断 String identifier = lock.lockWithTimeout("resource", 5000, 1000); System.out.println(Thread.currentThread().getName() + "获得了锁"); System.out.println(--n); lock.releaseLock("resource", identifier); }} 模拟线程进行秒杀服务; public class ThreadA extends Thread { private Service service; public ThreadA(Service service) { this.service = service; } @Override public void run() { service.seckill(); }}public class Test { public static void main(String[] args) { Service service = new Service(); for (int i = 0; i < 50; i++) { ThreadA threadA = new ThreadA(service); threadA.start(); } }} 结果如下,结果为有序的:If you comment out the part that uses the lock:
Public void seckill () {/ / returns the value of the lock for judging / / String indentifier = lock.lockWithTimeout ("resource", 5000, 1000); System.out.println (Thread.currentThread (). GetName () + "acquired lock"); System.out.println (--n); / / lock.releaseLock ("resource", indentifier);}
As you can see from the results, some are done asynchronously:
Third, realize distributed lock based on Zookeeper.
ZooKeeper is an open source component that provides consistency services for distributed applications. It has a hierarchical file system directory tree structure, which stipulates that there can be only one unique file name in the same directory. The steps to implement a distributed lock based on ZooKeeper are as follows:
(1) create a directory mylock
(2) if thread A wants to acquire the lock, create a temporary sequential node under the mylock directory.
(3) get all the child nodes in the mylock directory, and then get the sibling nodes smaller than yourself. If they do not exist, the sequence number of the current thread is the lowest and the lock is obtained.
(4) Thread B gets all the nodes, determines that it is not the smallest node, and sets the node that listens less than it does.
(5) Thread A finishes processing and deletes its own node. Thread B listens to the change event and determines whether it is the smallest node. If so, it gets the lock.
Here we recommend an open source library Curator of Apache, which is a ZooKeeper client, the InterProcessMutex provided by Curator is the implementation of distributed lock, the acquire method is used to acquire the lock, and the release method is used to release the lock.
The source code for implementation is as follows:
Import lombok.extern.slf4j.Slf4j;import org.apache.commons.lang.StringUtils;import org.apache.curator.framework.CuratorFramework;import org.apache.curator.framework.CuratorFrameworkFactory;import org.apache.curator.retry.RetryNTimes;import org.apache.zookeeper.CreateMode;import org.apache.zookeeper.data.Stat;import org.springframework.beans.factory.annotation.Value;import org.springframework.context.annotation.Bean;import org.springframework.stereotype.Component / * distributed lock Zookeeper implementation * * / @ Slf4j@Componentpublic class ZkLock implements DistributionLock {private String zkAddress = "zk_adress"; private static final String root = "package root"; private CuratorFramework zkClient; private final String LOCK_PREFIX = "/ lock_"; @ Bean public DistributionLock initZkLock () {if (StringUtils.isBlank (root)) {throw new RuntimeException ("zookeeper 'root' can't be null") } zkClient = CuratorFrameworkFactory .builder () .connectString (zkAddress) .retryPolicy (new RetryNTimes (2000, 20000)) .namespace (root) .build (); zkClient.start (); return this;} public boolean tryLock (String lockName) {lockName = LOCK_PREFIX+lockName; boolean locked = true Try {Stat stat = zkClient.checkExists () .forPath (lockName); if (stat = = null) {log.info ("tryLock: {}", lockName); stat = zkClient.checkExists () .forPath (lockName) If (stat = = null) {zkClient .create () .creatingParentsIfNeutral () .withMode (CreateMode.EPHEMERAL) .forPath (lockName, "1" .getBytes ()) } else {log.warn ("double-check stat.version: {}", stat.getAversion ()); locked = false;}} else {log.warn ("check stat.version: {}", stat.getAversion ()); locked = false } catch (Exception e) {locked = false;} return locked;} public boolean tryLock (String key, long timeout) {return false;} public void release (String lockName) {lockName = LOCK_PREFIX+lockName Try {zkClient. Delete () .resume () .deletingChildrenIfNeedness () .forPath (lockName); log.info ("release: {}", lockName);} catch (Exception e) {log.error ("delete", e) }} public void setZkAddress (String zkAddress) {this.zkAddress = zkAddress;}}
Advantages: it has the characteristics of high availability, reentrant and blocking lock, which can solve the problem of failure deadlock.
Disadvantages: because nodes need to be created and deleted frequently, the performance is not as good as that of Redis.
Fourth, contrast
Implementation of Database distributed Lock
Disadvantages:
1.db has poor operation performance and the risk of locking the table.
two。 When a non-blocking operation fails, polling is required to consume cpu resources
3. Not commit for a long time or polling for a long time may take up more connection resources
Implementation of Redis (cache) distributed lock
Disadvantages:
1. Lock deletion failure expiration time is difficult to control
two。 Non-blocking. If the operation fails, polling is required, which takes up cpu resources.
Implementation of ZK distributed Lock
Cons: performance is not as good as the redis implementation, mainly because write operations (acquiring lock release locks) need to be performed on Leader and then synchronized to follower.
In short: ZooKeeper has good performance and reliability.
From the point of view of the difficulty of understanding (from low to high) database > cache > Zookeeper
From the perspective of implementation complexity (from low to high) Zookeeper > = cache > database
From a performance perspective (from high to low) caching > Zookeeper > = database
From the perspective of reliability (from high to low) Zookeeper > cache > database
On how to achieve distributed locks to share here, I hope that the above content can be of some help to you, can learn more knowledge. If you think the article is good, you can share it for more people to see.
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.