php分布式框架(PHP分布式框架有哪些官网)
分布式锁的实现方式有三种:1。基于数据库实现分布式锁;2.实现基于缓存的分布式锁(Redis等)。);3.基于Zookeeper实现分布式锁。从性能角度(从高到低):“缓存模式Zookeeper模式=数据库模式”。
操作环境:windows系统,redis 6.0,thinkpad t480电脑。
分布式锁的三种实现方法;
1.基于数据库实现分布式锁;2.实现基于缓存的分布式锁(Redis等)。);3.基于Zookeeper实现分布式锁;
1、基于数据库实现分布式锁定
1.悲观锁
使用select … where …更新排他锁
注:其他附加功能与实现1基本相同。这里要注意“凡名=锁”。名称字段必须建立索引,否则表将被锁定。在某些情况下,例如,如果表不大,mysql优化器将不会采用这个索引,从而导致锁定表的问题。
2.乐观锁定
所谓的乐观锁和前者最大的区别是基于CAS,不互斥,不会造成锁等待,消耗资源。在操作过程中,认为不存在并发冲突,只有更新版本失败后才能注意到。我们利用这种认识来防止我们的抢购和尖峰信号的超卖。乐观锁定是通过增加增量版本号字段来实现的
第二,基于缓存(Redis等。)来实现分布式锁
1.使用命令介绍:(1)SETNXSETNX key val:当且仅当key不存在时,设置一个key为val的字符串并返回1;如果键存在,则什么也不做并返回0。(2)expireexpire key timeout:为key设置一个超时,单位为秒,之后锁会自动释放,避免死锁。(3)deletedelete键:delete键
使用Redis实现分布式锁时,主要使用这三个命令。
2.实现思路:(1)获取锁时,使用setnx锁定,使用expire命令为锁添加超时时间,之后锁会自动释放,锁的值是随机生成的UUID,可以用来判断何时释放锁。(2)获取锁时,设置一个获取超时时间,超过这个时间就放弃获取锁。(3)开锁时,由UUID判断是否是锁,如果是,执行delete开锁。
3.分布式锁的简单实现代码:
/** *分布式锁的简单实现代码*/ public class DistributedLock {
私有最终JedisPool jedisPool
公共分布式锁(JedisPool jedisPool) { this.jedisPool = jedisPool }
/** *锁定 * @param lockName锁的密钥 * @param acquireTimeout获取超时值 * @param超时锁定超时 * @返回锁id */ 公共字符串锁定超时(字符串锁定名称,长获取超时,长超时){ Jedis conn = null 字符串重标识符=空; 尝试{ //获取连接 conn = JedisPool . GetResource; //随机生成一个值 字符串标识符= UUID.randomUUID。toString; //锁名,即键值 string LockKey = lock:+LockName; //超时时间,锁定后,锁会自动解除。 int lockExpire =(int)(time out/);
//获取锁的超时时间。如果超过这个时间,放弃获取锁。 long end = system . current timemillis+acquire time out; while(system . CurrentMemillis& lt;end) { if (conn.setnx(lockKey,identifier)=){ conn.expire(lockKey,LockExpire); //返回值,用于确认锁的释放时间 retIdentifier =标识符; 返回重新标识符; } //return-表示key没有设置超时,而是为key设置了超时。 if(conn . TTL(LockKey)=-){ conn.expire(lockKey,LockExpire); }
尝试{ thread . sleep; } catch(中断异常e) { Thread.currentThread。interrupt; } } } catch (JedisException e) { e . printstacktrace; }最后{ if (conn!= null) { conn . close; } } 返回重新标识符; }
/** *松开锁 * @param lockName锁的密钥 * @param标识符释放锁的标识符 * @返回 */ 公共布尔释放锁(字符串锁名,字符串标识符){ Jedis conn = null string LockKey = lock:+LockName; boolean retFlag = false 尝试{ conn = JedisPool . GetResource; while (true) { //监控锁,准备开始交易 conn . watch(LockKey); //通过前面返回的值判断是否是锁。如果是,删除它并释放它 if(identifier . equals(conn . get(LockKey))){ transaction transaction = con . multi; transaction . del(LockKey); 列表<。object results = transaction . exec; if (results == null) { 继续; } retFlag = true } conn . unwatch; 打破; } } catch (JedisException e) { e . printstacktrace; }最后{ if (conn!= null) { conn . close; } } 返回retFlag } }4.测试刚刚实现的分布式锁
在该示例中,50个线程用于模拟杀死一个商品,而–运算符用于减少商品。是否锁定,从结果的有序性可以看出。
模拟尖峰服务,其中配置了jedis线程池,该线程池被传输到分布式锁,供其在初始化期间使用。
公共级服务{
私有静态JedisPool池= null
私有分布式锁=新分布式锁(池);
int n = 500
静态{ JedisPoolConfig = new JedisPoolConfig; //设置最大连接数 config . SetMaxTotaL(200); //设置最大空闲数 config . SetMaxidle(8); //设置最大等待时间 config . setmaxwaitmillis(1000 * 100); //当您请求jedis实例时,是否需要验证?如果为真,则所有jedis实例都可用 config . settestonborrow(true); pool = new JedisPool(config,127.0.0.1,6379,3000); }
public void seckill { //释放锁时返回锁的值以供判断 字符串标识符= lock.lockWithTimeout(资源,5000,1000); 系统。out.println(线程。currentthread。getname+得到了锁); system . out . println(-n); lock.releaseLock(资源,标识符); } }为spike服务模拟线程;
公共类ThreadA扩展Thread { 私人服务;
公共线程(服务){ this.service = service }
@覆盖 public void run { service . seckill; } }
公共类测试{ 公共静态void main(String[] args) { 服务服务=新服务; for(int I = 0;i <。50;i++) { ThreadA threadA = new ThreadA(服务); threada . start; } } }结果如下,结果有序:
如果您注释掉使用锁的部分:
public void seckill { //释放锁时返回锁的值以供判断 //String identifier = lock . lockhittime out(资源,5000,1000); 系统。out.println(线程。currentthread。getname+得到了锁); system . out . println(-n); //lock.releaseLock(resource,identifier); }从结果可以看出,其中一些是异步的:
第三,基于动物园管理员实现分布式锁
ZooKeeper是一个开源组件,为分布式应用程序提供一致的服务。其内部是分层的文件系统目录树结构,规定同一目录下只能有一个唯一的文件名。基于ZooKeeper实现分布式锁的步骤如下:
(1)创建目录mylock;(2)如果线程a想要获取锁,它在mylock目录中创建一个临时序列节点;(3)获取mylock目录下的所有子节点,然后获取比自己小的兄弟节点。如果不存在,则表示当前线程序号最小,获得锁;(4)线程B获取所有节点,判断不是最小节点,设置一个小于自身的节点;(5)线程A处理完毕后,删除自己的节点,线程B监控变化事件,判断是否是最小节点,如果是,获取锁。
在这里我们推荐一个Apache开源库馆长,是ZooKeeper的客户端。馆长提供的进程间互斥是分布式锁的实现。获取方法用于获取锁,释放方法用于释放锁。
实现源代码如下:
import lombok . extern . SLF 4j . SLF 4j; import org . Apache . comes . lang . Stringuils; import org.apache .策展人. org.apache.curator.framework.CuratorFramework import org.apache .策展人. framework . curatorframeworkFactory; import org.apache .策展人. retry . retry ntimes; import org . Apache . zoo keeper . create mode; import org . Apache . zoo keeper . data . Stat; import org . spring framework . beans . factory . annotation . value; import org . spring framework . context . annotation . Bean; import org . spring framework . stereotype . component;
/** *分布式锁Zookeeper实现 * */ @Slf4j @组件 公共类ZkLock实现分布式锁{ private String ZkADdress = ZK _ adress; 私有静态最终字符串根=包根; 私有CuratorFramework zkClient
私有最终字符串LOCK _ PREFX =/LOCK _;
@豆 public DistributionLock InitzkLock{ if (StringUtils.isBlank(root)) { 抛出新的RuntimeException(zoo keeper & # 39;根& # 39;can & # 39t为空); } zkClient = CuratorFrameworkFactory 。构建器 。连接字符串(zkAddress) 。retryPolicy(新RetryNTimes(2000,20000)) 。命名空间(根) 。build; zkclient . start; 归还这个; }
公共布尔tryLock(字符串锁名){ lockName = LOCK _ PREFIX+lockName; 布尔锁定=真; 尝试{ Stat stat = zkClient.checkExists。for path(LockName); if (stat == null) { log.info(tryLock:{},lockName); stat = zkClient.checkExists。for path(LockName); if (stat == null) { 独立线程 。创建 。creatingParentsIfNeeded 。withMode(创建模式。短命) 。forPath(lockName,1 . GetBytes); } else { log.warn(双击stat.version:{},stat . GetVersion); locked = false } } else { log.warn(check stat.version:{},stat . GetVersion); locked = false } }捕获(例外e) { locked = false } 返回锁定; }
公共布尔tryLock(字符串键,长超时){ 返回false }
公共无效释放(字符串锁名){ lockName = LOCK _ PREFIX+lockName; 尝试{ 独立线程 。删除 。保证 。deletingChildrenIfNeeded 。for path(LockName); log.info(发布:{},lockName); }捕获(例外e) { log.error (delete,e); } }
public void setZkAddress(String zkAddress){ this.zkAddress = zkAddress } }优点:具有高可用性、重入性和阻塞锁的特点,可以解决无效死锁问题。
缺点:由于节点需要频繁创建和删除,性能不如Redis。
第四,对比
数据库分布式锁实现的缺点;
1.db操作性能差,有锁表的风险。2.非阻塞操作失败后,需要轮询,占用cpu资源;3.长时间不提交或长时间轮询可能会占用更多的连接资源
Redis分布式锁实现的缺点;
1.锁删除失败的到期时间难以控制。2.非阻塞,操作失败后,需要轮询,占用cpu资源;
ZK分布式锁实现的缺点:它的性能不如redis实现,主要是因为所有的写操作(获取锁和释放锁)都需要在Leader上执行,然后同步到follower。
总之,ZooKeeper的性能和可靠性更好。
从易于理解的角度来看(从低到高),数据库缓存Zookeeper
从实现的复杂度来看(从低到高),Zookeeper = cache数据库
缓存管理员=从性能角度看的数据库(从高到低)
动物园管理员从可靠性角度(从高到低)缓存数据库