特别是电商的公司,经常会问起有关php高并发的时候,你是如何处理的。懂的人都知道要加锁,可是叫你详细的说说原理的时候就支支吾吾了。说明你还不够了解锁的原理或者你不懂思路。下面就来给大家详细的聊聊Redis的分布式锁。
在单机环境下,有个秒杀商品的活动,在短时间内,服务器压力和流量会陡然上升。这个就会存在并发的问题。想要解决并发需要解决以下问题
1、提高系统吞吐率也就是qps 每秒处理的请求书
2、避免商品在高并发的情况下,出现资源争抢导致的超买超买问题
解决问题一:采用内存型数据库提高系统的qps
解决问题二:就要用到经常会遇到的锁,例如MySQL 有读锁、写锁、排他锁、悲观锁、乐观锁。
简单版设置锁
$redis = new Redis(); $redis->connect('127.0.0.1', 6379); //连接Redis $expire = 10;//有效期10秒 $key = 'lock';//key $value = time() + $expire;//锁的值 = Unix时间戳 + 锁的有效期 $lock = $redis->setnx($key, $value); //判断是否上锁成功,成功则执行下步操作 if(!empty($lock)) { //下步操作... }
如果以这样的简单版设置锁就能解决所有问题,未免也太小看锁在程序中应用了。
按正常的操作示例基本上都是这样写的。但是这样写有一些问题
1、假如有10000 个请求访问了redis 不存在的键,这样请求就是指接到了MySQL数据,造成CPU短时间内达到100%甚至宕机。这样场景俗称缓存击穿造成的缓存雪崩。
2、 假如CPU过高或者网络延时问题,造成锁没有被删除掉或者缓存键过期没有被回收的情况,就会形成死锁。
解决问题:引用reids setnx 方法的作用是,当设置的key 不存在时,设置新的值。这样就避免了缓存击穿的问题。检测键的过期时间,避免产生死锁
解决死锁问题
$expire = 10;//有效期10秒 $key = 'lock';//key $value = time() + $expire;//锁的值 = Unix时间戳 + 锁的有效期 $status = true; while($status) { $lock = $redis->setnx($key, $value); if(empty($lock)) { $value = $redis->get($key); if($value < time()) { $redis->del($key); } }else{ $status = false; //下步操作.... } }
1、按理说这样解决单机版锁竞争问题已经是没有多大问题了。那么特殊情况来了,如果这个设置锁的键由于意外情况没有被删除,这样同样会有死锁的情况发生。
2、分布式集群业务业务场景下,每台服务器是独立存在的。多台服务器怎么通过一个标识来相互竞争锁呢。这里就用到了分布式锁
这里简单介绍一下,以MYSQL 的事务机制来延生。
事务四个特性ACID,有四种隔离级别:未提交读、已提交读、可重复读、串行化。这些特性都只在单台服务器上生效。
到了分布式集群了,数据在不同的服务器上,紧靠事务很难保持数据的一致性及隔离性,事务的作用就意义不大了。Redis也是如此。
正确的分布式锁的打开方式
/** * 实现Redis分布锁 */ $key = 'demo'; //要更新信息的缓存KEY $lockKey = 'lock:'.$key; //设置锁KEY $lockExpire = 10; //设置锁的有效期为10秒 //获取缓存信息 $result = $redis->get($key); //判断缓存中是否有数据 if(empty($result)) { $status = TRUE; while ($status) { //设置锁值为当前时间戳 + 有效期 $lockValue = time() + $lockExpire; /** * 创建锁 * 试图以$lockKey为key创建一个缓存,value值为当前时间戳 * 由于setnx()函数只有在不存在当前key的缓存时才会创建成功 * 所以,用此函数就可以判断当前执行的操作是否已经有其他进程在执行了 * @var [type] */ $lock = $redis->setnx($lockKey, $lockValue); /** * 满足两个条件中的一个即可进行操作 * 1、上面一步创建锁成功; * 2、 1)判断锁的值(时间戳)是否小于当前时间 $redis->get() * 2)同时给锁设置新值成功 $redis->getset() */ if(!empty($lock) || ($redis->get($lockKey) < time() && $redis->getSet($lockKey, $lockValue) < time() )) { //给锁设置生存时间 $redis->expire($lockKey, $lockExpire); //****************************** //此处执行插入、更新缓存操作... //****************************** //以上程序走完删除锁 //检测锁是否过期,过期锁没必要删除 if($redis->ttl($lockKey)) $redis->del($lockKey); $status = FALSE; }else{ /** * 如果存在有效锁这里做相应处理 * 等待当前操作完成再执行此次请求 * 直接返回 */ sleep(2);//等待2秒后再尝试执行操作 } } }
以上就是“php面试题:说说高并发下你是如何加分布式锁的”的详细内容,想要了解更多php教程或者面试相关资讯欢迎关注编程学习网
扫码二维码 获取免费视频学习资料
- 本文固定链接: http://phpxs.com/post/8120/
- 转载请注明:转载必须在正文中标注并保留原文链接
- 扫码: 扫上方二维码获取免费视频资料