Redis的高级特性
目录
- 高级数据类型
- 支持事务
- 支持Lua脚本
- 支持pipeline 管道|流水线
- 支持发布/订阅
- 可实现异步队列 | 消息队列
高级数据类型
1、Bitmap 位图
按 bit 位存储信息,常用于实现布隆过滤器
2、HyperLogLog 基数统计
提供去重计数功能,根据输入元素来计算基数,不储存输入元素本身。
优点是在数量量很大时,计算基数所需的空间总是固定 的且很小的。和Bitmap一样,不精确、有一定的错误率。
基数:一个数据集中不重复的元素个数(剔除重复元素后的元素数量)
3、Geospatial 地址空间
用于保存地理位置,可以做距离计算、根据半径范围查找等,比如计算最优地图路径、查找附近范围内的人。
支持事务
redis支持事务,可以将多个命令作为打包为事务来执行。
redis的事务只是把多个命令打包,放在一个队列中执行,只是保证了事务中指令的串行执行,并没有给事务添加原子性的保障机制,redis的事务具有隔离性但不具备原子性,执行失败时不会回滚。
#标记事务开始
multi
#多个命令,依次入队
#...
#执行|提交事务
exec
#取消事务
discard
- 如果在exec执行事务之前发生了错误,比如某个要入队的命令语法不对,会自动取消这个事务。
- 如果exec执行事务时,中途某个命令执行出错,并不会回滚已执行的命令,而是跳过出错的命令继续往下执行,可以在执行结果中看到各个命令的执行结果。
#
支持Lua脚本
Lua脚本可以用于打包执行一些列指令
- 关键特点:与redis事务都是打包命令,但lua脚本中的一系列操作、指令具有原子性
- 复用方便
如果业务逻辑比较复杂或者多个指令需要具有原子性,可以将这些指令封装在lua脚本中发送给redis执行。
支持pipeline 管道|流水线
管道可以打包一批指令,一次性发送给redis服务器执行,redis服务器会一次性返回这批命令的所有执行结果,减少了与redis服务器频繁交互的网络通信的时间开销。
与redis事务相比
- 相同点:都是将多个指令打包发送给redis服务器执行,整个操作都不具备原子性,某个指令执行失败时会继续执行后续指令,不会回滚之前的操作。
- 不同点:redis事务是作为一个整体执行的,具有隔离性,redis服务器执行事务期间不会穿插执行其它客户端的指令;管道中的命令不是作为一个整体执行的,而是逐个指令执行,虽然事务、管道都能保证所包含命令执行的顺序性,但执行管道中命令的期间可能会穿插执行其它客户端的指令,最终执行结果可能会被影响,所以管道只适合对顺序性要求不高的场景。
原生的mset、mget实质是批量执行set、get指令,但具有原子性。
使用示例
@Autowired
private StringRedisTemplate redisTemplate;
public void test() {
//...
//返回值list,元素对应各条命令的执行结果
List<Object> resultList = redisTemplate.executePipelined(new RedisCallback<Object>() {
@Override
public Object doInRedis(RedisConnection redisConnection) throws DataAccessException {
//打开一个pipeline
redisConnection.openPipeline();
redisConnection.set("name".getBytes(), "chy".getBytes());
redisConnection.set("tel".getBytes(), "188xxxxxx".getBytes());
redisConnection.set("email".getBytes(), "xxxxxxx@qq.com".getBytes());
//不要调用redisConnection的closePipeline()关闭pipeline,也不要关闭连接欸,不然拿不到返回的执行结果
//返回null即可
return null;
}
});
//...
}
不要一次性组装发送太多指令,或者发送big key这种大key,可能导致网络拥塞,可以将大量指令拆分到小的pipeline中分批完成。
支持发布/订阅
发布/订阅(publish/subscribe)是一种消息通信模式,redis客户端订阅redis服务器上的一个或多个channel(管道、频道),客户端发布消息到redis服务器中的某个channel后,redis服务器会将消息推送给所有订阅了此channel的redis客户端。
发布/订阅常用命令
subscribe channel1 channel2 #订阅一个或多个频道
psubscribe CCTV1 #p即pattern,用正则表达式指定要订阅的频道,会订阅所有匹配的频道
psubscribe CCTV* #订阅所有以CCTV开头的频道
psubscribe *TV #订阅所有以TV结尾的频道
psubscribe CCTV* CNTV* #可以指定多个匹配模式,订阅以CCTV开头的所有频道、订阅所有以CNTV开头的所有频道
unsubscribe channel1 channel2 #退订一个或多个频道
punsubscribe CCTV* CNTV* #退订以CCTV开头的所有频道、退订以CNTV开头的所有频道
publish channel1 "hello" #往某个频道里推送消息
说明
- channel不需要手动创建
- redis客户端无需订阅channel就可以直接往任意channel中发布消息
- 客户端订阅的channel列表是作为session存储在redis服务器上的,订阅只在本次会话期间有效,断开与redis服务器的连接后订阅失效,所以客户端每次启动连接到redis服务器时都需要重新订阅。
- redis发布/订阅的消息类似于广播,是在线即时消息,只有在线的订阅者(已连接到redis服务器的客户端)才会收到推送。若客户端当时不在线,后续连接到服务器时,服务器也不会再次推送之前的消息。
redis的发布订阅不能提供可靠的消息投递,一般只用于日志、流水等对消息投递可靠性要求不高的场景。
可实现异步队列 | 消息队列
redis实现异步|消息队列有2种方式
1、使用发布/订阅模式实现
- 可以让多个消费者同时消费消息。消费者集群部署时,消息会被每个消费者节点进行消费,存在重复消费
- 无法保证消息送达(不可靠)
2、使用list实现
- 生产者用rpush实现消息入队,消费者用blpop阻塞获取list中的元素实现消息出队
- list有序,可以实现顺序消息
- 消息只会被一个消费者进行消费
3、使用zset实现延时队列
- 把消息内容作为key,消息发送的时间转换为时间戳作为score,使用zadd指令实现消息入队
- 消费者间隔指定时间用 zrangebyscore 指令获取当前时间范围内的key进行处理。
实现消息队列尽量用rabbitmq、kafka等更专业的消息中间件,功能更加强大。
还没有评论,来说两句吧...