在单机环境下,有个秒杀商品的活动,在短时间内,服务器压力和流量会陡然上升。这个就会存在并发的问题。想要解决并发需要解决以下问题
1、提高系统吞吐率也就是qps 每秒处理的请求书
2、避免商品在高并发的情况下,出现资源争抢导致的超买超买问题
解决问题一:采用内存型数据库提高系统的qps
解决问题二:就要用到经常会遇到的锁,例如MySQL 有读锁、写锁、排他锁、悲观锁、乐观锁。不过这里只讨论redis来实现锁
在Redis中我们用的最多的是Set方法,可是在Redis中有这么一个方法setnx来做锁,Redis的setnx指令,设置一个键值对,当且仅当键不存在的时候,才能设置成功。
这里用的是PHP的版本做列子
我们先看看如何设置一把简单的锁
$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,array('nx', 'ex' => 10));
//判断是否上锁成功,成功则执行下步操作
if(empty($lock))
{
return "error";
}
//这里写我们的操作逻辑
当然这样做是不科学的,因为有一定并发概念的同学都知道,在高并发场景下。
1、假如有10000 个请求访问了redis 不存在的键,这样请求就是指接到了MySQL数据,造成CPU短时间内达到100%甚至宕机。这样场景俗称缓存击穿造成的缓存雪崩。
2、 假如CPU过高或者网络延时问题,造成锁没有被删除掉或者缓存键过期没有被回收的情况,就会形成死锁。
try{
$expire = 10;//有效期10秒
$key = 'lock';//key
$value = time() + $expire;//锁的值 = Unix时间戳 + 锁的有效期
$lock = $redis->setnx($key, $value,array('nx', 'ex' => 10));
if(empty($lock))
{
return "error";
}
}finally {
//等程序运行完后 释放锁
$redis->del($key);
}
到了已经可以满足一定并发下的处理可,可以在更高的情况下面,会出现解锁的情况还是会出现解锁的人不是同一个人。
所以redis社区也有人建议使用UUID
try{
$expire = 10;//有效期10秒
$key = 'lock';//key
$value = uuid();//锁的值 = Unix时间戳 + 锁的有效期
$lock = $redis->setnx($key, $value,array('nx', 'ex' => 10));
if(empty($lock))
{
return "error";
}
}finally {
//等程序运行完后 释放锁,通过uuid判断是不是这个线程,如果是那就是当前进程可是释放锁
if($redis->get($key) == $value){
$redis->del($key);
}
}
这样处理已经可以在中小型企业可以完全够用了,可是如果你到了阿里,京东这些大规模的企业里面,这样写还是存在问题的,我们设置锁的有效时间是10秒,在这么大并发的情况下,我们的程序有可能运行时间超过三秒。
那么这样的情况下面,我们需要怎么处理呢?
一般的做法是加锁成功后,马上开一个线程,让他自己续命。时间有效时间为1/3
#让锁自动续命
while($redis->get($key)){
$lock = $redis->setnx($key, $value,array('nx', 'ex' => 10));
sleep(3);
}
到了这里一般的中大型企业已经购用了,因为Redis已经可以处理几w并发。利用并发redis把并发变成串行执行。
可是还是会遇到一些问题,因为redis肯定不是单机的,因为单机的redis只能处理8w的并发,这个时候就会出现一个问题,就是你的锁写在A服务器中,可是A服务器还没来得及时处理同步的时候,它就已经挂了,这里就会遇到一个大厂会平凡问的问题,你是如何处理这一块的内容。
简单点书就是主重架构出现锁失效的问题。
我们可是再加锁的时候
往每个节点添加锁,超过一半的时候,认为成功,当然也会出现一个问题,就是加锁没有一半以上,就认为失败,出现锁回滚问题。一年偶尔出现一两次而已,当然也不用怕,有运维在的大企业,有人工处理的。
这个时候大多数的运维或者架构师会选择性能比redis低的分布式锁。Zookeeper
Zookeeper
ZooKeeper 是一个开源的分布式协调服务,ZooKeeper框架最初是在“Yahoo!"上构建的,用于以简单而稳健的方式访问他们的应用程序。 后来,Apache ZooKeeper成为Hadoop,HBase和其他分布式框架使用的有组织服务的标准。 例如,Apache HBase使用ZooKeeper跟踪分布式数据的状态。ZooKeeper 的设计目标是将那些复杂且容易出错的分布式一致性服务封装起来,构成一个高效可靠的原语集,并以一系列简单易用的接口提供给用户使用。
hadoop本来就是做分布式存储的,所以Zookeeper基于Hadoop来制作,不过Zookeeper现在只有java端和c端。
可以参考这一块的内容
https://segmentfault.com/a/1190000016173878