基于Redis的分布式锁的简单应用
基于Redis的分布式锁
- 支持分布式
- 可以细粒度话的控制
- 实现多台机器多个进程对一个数据进行操作的互斥(存放于Redis存储系统,多台机器的业务可以共同到Redis系统上进行基于分布式锁的业务操作)
处理并发问题
处理并发问题,主要就是不发生异常的情况下,想办法提升访问速度。
第一种,可以提前把数据库中的数据以Map的形式加载到内存,处理完之后再将处理结果统一写入数据库,提高访问数据的速度。
第二种,使用数据库,要在对应使用线程的方法上加上Synchronized,使其变为单线程,保证了数据的一致性。
如下例子表示的秒杀下单的例子,为该方法加上synchronized:
public synchronized void orderProductMockDiffUser(StringproductId)
{
{
//1:查询该商品库存,0表示活动结束
int stockNum=stock.get(productId);
if(stockNum==0){
thrownew SellException(100,"活动结束");
}
else{
//2:下单,(不同用户的openid不同)
orders.put(KeyUtil.genUniquekey().productId);
stockNum--;
try
try{
Thread.sleep(
Thread.sleep(100);
}
catch(InterruptedException e){
e.printStackTrace()
e.printStackTrace();
}
Stock.put(productid
Stock.put(productid,stockNum);
}
}
}
上述第二种办法,是一种解决办法,但是还是有问题,这种情况只适合单点秒杀的情况。无法细粒度控制一些秒杀的情况。比如,一起秒杀的线程秒杀不同的商品,有的秒杀用户多,有的秒杀用户少,这种情况就会导致所有的秒杀一样的速度,忽略了那些用户少的情况。因此,需要根据情况个别对待,提高效率,这时就需要基于Redis的分布式锁来解决这个问题了。
Redis
百度百科这样解释Redis的:
Redis是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。它支持多种类型的数据结构,如字符串(strings), 散列(hashes), 列表(lists),集合(sets), 有序集合(sorted sets)与范围查询,bitmaps, hyperloglogs和地理空间(geospatial)索引半径查询。 Redis 内置了复制(replication),LUA脚本(Lua scripting),LRU驱动事件(LRU eviction),事务(transactions)和不同级别的磁盘持久化(persistence),并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。
首先要认识两个Redis的命令:
1)SETNX命令
命令形式:SETNX KEYVALUE
意思是:
该例子:在redis存储系统中,将mykey设置为“Hello”,返回1表示设置成功。重新给该key设置为world,不成功,因为已经存在了键值,则什么也不做,返回了0。GET KEY查询到key的value值为“Hello”;
2)GETSET命令
命令形式:GETSET keyvalue
意思是:
基于Redis的加解锁
加锁方法lock()
参数 key:传递的是商品productId,value:当前时间+超时时间,保证在上一个线程正常解锁时间之后。
首先在redis中进行键值对的设置(SETNX命令),返回true表示加锁成功,设置时间为value。
否则,获取redis中已有key对应的value值,如果其值不为空并且小于系统当前时间,表示上一个线程的锁已经过期了,此时应该重新设置覆盖以前redis中key的value值(对应Redis中GetAndSet命令),此时当前线程可以抢占锁了。
此时如果有多个线程抢占加锁,则每个线程都需要利用getAndSet得到的旧时间值来个当前key对应的时间值比较,如果相等,表示就是自己拿到的上一个锁的解锁时间,可以抢占加锁;如果不相等,则表示被其他线程捷足先登了,此时不可加锁,需要等到起解锁。
解锁方法unlock()
参数 key:传递的是商品productId,value:当前时间+超时时间,保证在上一个线程正常解锁时间之后。
判断当前商品加锁时间是否为空,如果不为空则并且等于当前传入的value时间戳的值则表示上一个锁已经过期了,此时可以直接删除redis中上一个线程的锁,以释放给其他线程来使用。
业务加解锁
对下单的方法进行加解锁:
public synchronized void orderProductMockDiffUser(StringproductId)
{
//加锁
longtime =System.currentTimeMillis()+TIMEOUT;
if(!redisLock.lock(productId.String.valueOf(time)))
{
Throw new SellException(101,"人太多了,换个姿势再试试")
}
//1:查询该商品库存,0表示活动结束
int stockNum=stock.get(productId);
if(stockNum==0){
thrownew SellException(100,"活动结束");
}
else{
//2:下单,(不同用户的openid不同)
orders.put(KeyUtil.genUniquekey().productId);
stockNum--;
try{
Thread.sleep(100);
}
catch(InterruptedException e){
e.printStackTrace();
}
Stock.put(productid,stockNum);
}
//解锁
redisLock.unlock(productId,String.valueOf(time));
}
Redis数据缓存
这一部分见 高并发优化Redis缓存部分
Jpa和Mybatis怎么选?
JPA:小型项目
Mybatis:SSM架构的则继续使用
建表格使用sql,不用JPA建立,因为不够直观,所以选择Mybatis.
压测模拟并发
Apache ab模拟并发,直接在命令行输入如下命令:
ab -n 100 -c 100 url地址 表示 模拟100条请求的url有100个人同时请求
ab -t 60 -c 100 url地址 表示模拟60秒内100个人同时发送请求
还没有评论,来说两句吧...