Redis系列
Redis系列——第一章 Redis配置文件
Redis系列——第二章 Redis数据类型以及基本使用
Redis系列——第三章 Redis开启事务并实现乐观锁
Redis系列——第四章 Redis发布订阅模式
Redis系列——第五章 Redis持久化策略RDB与AOF
Redis系列——第六章 Redis主从同步
Redis系列——第七章 Redis开启哨兵模式
文章目录
- Redis系列
- [Redis系列——第一章 Redis配置文件](https://blog.csdn.net/weixin_42083036/article/details/112462218?spm=1001.2014.3001.5501)
- [Redis系列——第二章 Redis数据类型以及基本使用](https://blog.csdn.net/weixin_42083036/article/details/112479336?spm=1001.2014.3001.5501)
- [Redis系列——第三章 Redis开启事务并实现乐观锁](https://blog.csdn.net/weixin_42083036/article/details/112600432?spm=1001.2014.3001.5501)
- [Redis系列——第四章 Redis发布订阅模式](https://blog.csdn.net/weixin_42083036/article/details/112601397?spm=1001.2014.3001.5501)
- [Redis系列——第五章 Redis持久化策略RDB与AOF](https://blog.csdn.net/weixin_42083036/article/details/112976935?spm=1001.2014.3001.5501)
- [Redis系列——第六章 Redis主从同步](https://blog.csdn.net/weixin_42083036/article/details/113363406?spm=1001.2014.3001.5501)
- [Redis系列——第七章 Redis开启哨兵模式](https://blog.csdn.net/weixin_42083036/article/details/113564295)
- 一、Redis事务
- 二、使用redis事务实现乐观锁
一、Redis事务
本质:一组命令的集合 要么一起成功 要么一起失败,一个事务中的所有命令都会被序列化,在事务执行过程中会按照顺序执行
注意:
- Redis单条命令是保证原子性的,但是事务是不保证原子性的
- Redis没有隔离级别的概念
首先我们需要了解 Redis底层是由C编写,命令直接交由CPU执行,而CPU一旦执行命令是不会被中断的,所以基本每一条指令都是原子性的。但是虽然Redis每一条指令都是原子性的但是一旦指令联合使用就有可能被打断,即没有原子性。
1、开启事务
multi命令
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 2
QUEUED
127.0.0.1:6379> set k2 3
QUEUED
127.0.0.1:6379> get k1
QUEUED
127.0.0.1:6379> set k3 1
QUEUED
127.0.0.1:6379>
开启事务,所有在事务之后执行的命令都会进入队列 等待执行
2、执行事务
exec 命令——>执行事务队列的命令
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 2
QUEUED
127.0.0.1:6379> set k2 3
QUEUED
127.0.0.1:6379> get k1
QUEUED
127.0.0.1:6379> set k3 1
QUEUED
127.0.0.1:6379> exec
1) OK
2) OK
3) "2"
4) OK
127.0.0.1:6379>
3.取消事务
discard 命令
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 2
QUEUED
127.0.0.1:6379> set k2 3
QUEUED
127.0.0.1:6379> discard
OK
127.0.0.1:6379> exec
(error) ERR EXEC without MULTI //没有事务可执行
127.0.0.1:6379>
4、watch 监视命令
watch 监视命令:监视一个key值如果发生变化会导致事务执行失败,但是是一次性的操作所以每次执行失败后需要重新使用监视命令
与之对应的 UNwatch 放弃监视
二、使用redis事务实现乐观锁
- 乐观锁:我很乐观,我不认为需要控制,数据不可能出问题,所以不上锁 典型:cas
- 悲观锁:我很悲观,我认为数据无时无刻都有可能出现问题,所以我上锁了 典型:synchronized
代码
public static void main(String[] args) throws InterruptedException {
// 获取jedis连接
Jedis jedis = new Jedis("127.0.0.1",6379);
// 抢到的用户
String key_s = "user_name";
// 商品数量
String key = "test_count";
jedis.set("count","0");
jedis.set(key,"100");
for (int i= 0; i< 101;i++){
new Thread(()-> testWatch(key_s ,key)).start();
}
Thread.sleep(5000);
System.out.println("抢到用户数量为:" +jedis.get("count"));
}
public static void testWatch(String key_s ,String key){
Jedis jedis = new Jedis("127.0.0.1",6379);
try {
//模拟用户
String customer = UUID.randomUUID().toString().replace("-", "");
while (true) {
try {
//监控商品数量
jedis.watch(key);
System.out.println("用户:" + customer + "开始抢商品");
System.out.println("当前商品的个数:" + jedis.get(key));
int prdNum = Integer.parseInt(jedis.get(key));
if (prdNum > 0) {
//开启事务
Transaction transaction = jedis.multi();
transaction.set(key, String.valueOf(prdNum - 1));
//提交事务
List<Object> result = transaction.exec();
//如果监控失败 提示
if (result == null || result.isEmpty()) {
System.out.println("用户:" + customer + "没有抢到");
System.out.println("当前商品的剩余:" + jedis.get(key));
} else {
//记录抢到商品的用户信息 并记录用户个数
jedis.sadd(key_s, customer);
jedis.incr("count");
System.out.println("用户:" + customer + "抢到商品");
System.out.println("当前商品剩余个数:" + jedis.get(key));
break;
}
} else {
System.out.println("库存为0,用户:" + customer + "没有抢到商品");
break;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//exec,discard,unwatch命令都会清除连接中的所有监视
jedis.unwatch();
}
}
} catch (Exception e) {
// TODO: 处理Redis 错误或其他
System.out.println("redis bug:" + e.getMessage());
} finally {
try {
jedis.close();
} catch (Exception e) {
System.out.println("redis bug:" + e.getMessage());
}
}
}
巧妙的使用redis事务的