redis
1.库的概念

2.发布和订阅
listener配置:



测试后发现存在重复消费和丢消息问题(只能收到客户端链接之后的消息)
3.事务和pipeline:
先看下没有事务时的执行情况:


然后我们用Pipeline:


可以看到每个线程的多个redis操作结果被放到了list<Object>中,且如果我们持续用redis-cli刷transaction的值会发现直到10s之后第二个redis命令执行(实际上在closePipeline之后)才出现transaction的key。查看executePipelined的实现:

如果我们手动close:


open和close其实初始化了connection中的pipeline对象 在后续execute中如果pipeline被初始化 则做不通的逻辑 将结果放到List<Object> 并在close时取出并清空最后返回
pipeline和事务是不同的概念,pipeline并不保证原子性,更多的是客户端的行为来减少rtt,在不超过报文大小的情况下,pipeline中的命令会打包一起发送,结果也打包返回,当我们用事务时,则是每条命令分开发送,服务端响应queue直到exec返回结果。
pipeline非原子性实验:
for (int i=0;i<1;i++) { threadPoolExecutor.execute(new Runnable() { @Override public void run() { List<Object> result=redisTemplate.executePipelined(new RedisCallback<Object>() { @Override public Object doInRedis(RedisConnection redisConnection) throws DataAccessException { System.out.println("pipeline start"); for (int j=0;j<1000;j++){ redisConnection.incrBy("transaction".getBytes(),1); } return null; } }); System.out.println(result); } }); threadPoolExecutor.execute(new Runnable() { @Override public void run() { Object result=redisTemplate.execute(new RedisCallback<Object>() { @Override public Object doInRedis(RedisConnection redisConnection) throws DataAccessException { System.out.println("dddd start"); for (int j=0;j<1000;j++){ System.out.println(redisConnection.incrBy("transaction".getBytes(),-1)); } return null; } }); System.out.println(result); } });
我们在两个线程里:一个线程用pipeline自增1000次,另外一个线程里不用pipeline自减1000次,pipeline执行日志如下:

在递增到49时执行了不在pipeline中的命令:

可以看到在第243次也发生了同样的情况
事务:
我们发现redisTemplate提供的multi,exec,watch可以实现事务,同时execute也可以 关于2者的区别:
转载:https://www.cnblogs.com/wuhaonan/p/10646277.html
我们用同样的方法测试下事务的原子性:
redisTemplate.execute(new SessionCallback() { @Override public Object execute(RedisOperations redisOperations) throws DataAccessException { System.out.println("transaction start"); redisOperations.multi(); for (int i=0;i<1000;i++){ redisOperations.opsForValue().increment("transaction", 1); } List<Object> res = redisOperations.exec(); System.out.println(res); return null; } }); redisTemplate.execute(new SessionCallback() { @Override public Object execute(RedisOperations redisOperations) throws DataAccessException { System.out.println("dddd start"); for (int i=0;i<1000;i++){ System.out.println(redisOperations.opsForValue().increment("transaction", -1)); } return null; } });
分别在2个线程里执行这2个execute,结果如下

![]()

可见事务的执行过程是保证了原子性的
4.rdb:

5.aof:




6.复制

我们关掉master之后,slave会一直报错但数据还是在的,当我们清掉master的快照和aof再次上线的时候,slave会重新同步,此时slave的数据也会被清掉
7.用redis构建锁
简单的setnx和del会有问题:
1.持有锁的进程因为操作时间过长而导致锁被自动释放,但进程本身并不知晓这一点,甚至还可能会错误地释放掉了其他进程持有的锁。
2.在一个进程持有的锁过期之后,其他多个进程同时尝试去获取锁
3.以上2点同时出现,导致多个进程获得了锁,而每个进程都以为自己是唯一一个获得锁的进程。
解决方法:在释放锁的时候 检查当前的锁是否是开始持有的锁,并在整个释放锁的过程watch锁
8sentinel:
一主两从3sentinel:

把master下线重新上线后发现如上,检查配置文件发现多了下面这行配置:

1从也变成了这样:

2从的slaveof配置则被删掉了:

并且这个主从转化的过程有可能相当漫长

浙公网安备 33010602011771号