Redis分布式锁整合和压力测试
项目需要整合Redis来应对高并发问题
第一种锁 set key value px nx
1、 客户端A请求服务器设置key的值,如果设置成功就表示加锁成功
2、 客户端B也去请求服务器设置key的值,如果返回失败,那么就代表加锁失败
3、 客户端A执行代码完成,删除锁
4、 客户端B在等待一段时间后在去请求设置key的值,设置成功
5、 客户端B执行代码完成,删除锁
业务开始的简单实现的简单流程:
// if(如果缓存中isNotBlank){从缓存中获取}
// else{
// 如果缓存中没有,查询mysql
// 开始抢锁
// if(抢锁成功) {
// 在有效时间内从db中查询数据;
// if(mysql有){存入redis} else{无,则返回空值并设置过期时间,防止缓存穿透}
// 将锁释放,删除自己的锁
// }
// else {
// 抢锁失败,无法查询mysql,开始自旋
// }
// }
代码实例:
if(StringUtils.isNotBlank(skuJson)){ pmsSkuInfo = JSON.parseObject(skuJson,PmsSkuInfo.class); } else { //没有缓存,查db String token = UUID.randomUUID().toString(); String OK = jedis.set( "sku:" + skuId + ":lock",token,"np","px",10*1000); //开始抢锁 if(StringUtils.isNotBlank(OK)&&OK.equals("OK")){ //设置锁成功 pmsSkuInfo = getSkuByIdFromDb(skuId); if(pmsSkuInfo!=null){ //查询不为空,将结果存入redis jedis.set("sku:" + skuId + ":info",JSON.toJSONString(pmsSkuInfo)); }else { //防止缓存穿透,将空字符串传给redis jedis.setex("sku:" + skuId + ":info", 60*3, JSON.toJSONString("")); } //访问完毕,释放锁 String lockToken = jedis.get("sku:" + skuId +":info"); if(StringUtils.isNotBlank(lockToken)&&lockToken.equals(token)){ //保证锁是自己的 jedis.del("sku:" + skuId + ":info"); } }else { //设置锁不成功 return getSkuById(skuId); } } // 关闭资源 jedis.close();
第二种锁 使用redisson工具框架
它实现了juc的lock锁,并且可以在分布式的redis环境下使用,
实现了java.util.concurrent.locks.Lock接口。同时还提供了异步(Async)、反射式(Reactive)和RxJava2标准的接口。
另外Redisson还通过加锁的方法提供了leaseTime的参数来指定加锁的时间。超过这个时间后锁便自动解开了。
RLock lock = redisson.getLock("anyLock"); // 最常见的使用方法 lock.lock();
//---------------------------------------------
lock.lock(10, TimeUnit.SECONDS);
// 尝试加锁,最多等待100秒,上锁以后10秒自动解锁
boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);
if (res) {
try {
...
} finally {
lock.unlock();
}
}
缓存常见问题
- (1)缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中,将去查询数据库,但是数据库也无此记录,并且处于容错考虑,我们没有将这次查询的null写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义。在流量大时,可能DB就挂掉了,要是有人利用不存在的key频繁攻击我们的应用,这就是漏洞。
- 解决:空结果进行缓存,但它的过期时间会很短,最长不超过五分钟
- (2)缓存雪崩是指在我们设置缓存时采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发到DB,DB瞬时压力过重雪崩。
- 解决:原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。
- (3)缓存击穿是对于一些设置了过期时间的key,如果这些key可能会在某些时间点被超高并发地访问,是一种非常“热点”的数据。这个时候,需要考虑一个问题:如果这个key在大量请求同时进来前正好失效,那么所有对这个key的数据查询都落到db,我们称为缓存击穿。
- 解决:分布式锁
Linux下
yum -y install httpd-tools,然后ab -V测试
Windows下
软件Aapche24,
下载路径 https://www.apachehaus.com/cgi-bin/download.plx
在安装目录下修改httpd.conf设置define srvroot 为自己的安装目录
cmd启动httpd.exe
另开cmd命令窗口,在bin目录下输入:
ab -c 200 -n 1000 http:jd.com
200为每秒并发数,1000为htttp请求总数,后面就是网址,不接受https请求