Redis的高级特性


 

高级数据类型

1、Bitmap 位图

按 bit 位存储信息,常用于实现布隆过滤器

 

2、HyperLogLog 基数统计

提供去重计数功能,根据输入元素来计算基数,不储存输入元素本身。

优点是在数量量很大时,计算基数所需的空间总是固定 的且很小的。和Bitmap一样,不精确、有一定的错误率。

基数:一个数据集中不重复的元素个数(剔除重复元素后的元素数量)

 

3、Geospatial 地址空间

用于保存地理位置,可以做距离计算、根据半径范围查找等,比如计算最优地图路径、查找附近范围内的人。

 

支持事务

redis支持事务,可以将多个命令作为打包为事务来执行。

redis的事务只是把多个命令打包,放在一个队列中执行,只是保证了事务中指令的串行执行,并没有给事务添加原子性的保障机制,redis的事务具有隔离性但不具备原子性,执行失败时不会回滚。

#标记事务开始
multi

#多个命令,依次入队
#...

#执行|提交事务
exec

#取消事务
discard
  • 如果在exec执行事务之前发生了错误,比如某个要入队的命令语法不对,会自动取消这个事务。
  • 如果exec执行事务时,中途某个命令执行出错,并不会回滚已执行的命令,而是跳过出错的命令继续往下执行,可以在执行结果中看到各个命令的执行结果。

 

支持Lua脚本

redis使用lua脚本的优点

  • redis会将整个lua脚本作为一个整体执行,具有原子性
  • 可以将多个操作打包为脚本一次性发送,减少网络开销。
  • 可以抽取频繁使用的一段redis指令,打包为脚本由redis服务器缓存可以缓存起来,其它客户端可以直接复用缓存的脚本。

redis 2.6 版本开始内置了lua解释器,服务器无需单独配置lua环境。此处介绍redis常用的脚本指令

 

eval
#直接执行lua脚本
eval script keyNum [key...] [arg...]
  • script:lua脚本内容,lua脚本中可以使用2个全局数组:KEYS 存储 [key…] 传递的所有 redis key,ARGV 存储 [arg…] 传递的所有实参,注意lua脚本中 数组index从1开始
  • keyNum:[key…] 传递的redis key数量,若没有传递任何redis key,则指定为0
  • [key…]:可选,传给lua脚本的redis key
  • [arg…]:可选,传给lua脚本的实参列表
     

示例

# username age chy 20
eval "return {KEYS[1], KEYS[2], ARGV[1], ARGV[2]}" 2 username age chy 20

# 20
eval "return ARGV[1]" 0 20

# 20 21
eval "return {ARGV[1], ARGV[2]}" 0 20 21

 

lua脚本中使用redis指令

set username 'chy'

# lua脚本中调用redis指令,第一个是指令名称,后续是参数列表
eval "return redis.call('GET', 'username')" 0
eval "return redis.call('SET', 'username', 'chy1')" 0

eval "return redis.call('GET', KEYS[1])" 1 username
eval "return redis.call('SET', KEYS[1], ARGV[1])" 1 username chy1

lua脚本中有两种方式可以调用redis指令:redis.call()、redis.pcall(),区别在于 call() 执行异常时会直接返回错误、终止后续代码的执行,pcall() 执行异常时会忽略异常、继续执行后续代码。

 

evalsha

eval 每次都要传输 lua 脚本,如果 lua 脚本过长、频繁使用,对网络带宽、执行效率有影响。需要多次、频繁使用的lua脚本,可以先由redis服务器加载、缓存lua脚本,redis客户端保存 lua 脚本的 sha 值,后续通过sha 值去调用lua脚本。

# 加载lua脚本,会返回一个sha值。加载的脚本会被永久缓存,除非调用 script flush 被清除
script load "return redis.call('SET', KEYS[1], ARGV[1])"

#通过sha值调用脚本
evalsha sha keyNum [key...] [arg...]


# 根据sha值检查对应的lua是否已存在(已加载),可传递多个sha值,返回与 sha 个数对应的列表,元素值为0(不存在)、1(已存在)
script exists sha...

# 删除所有加载的脚本
script flush

# 杀死当前正在运行的 Lua 脚本。这个命令主要用于终止执行时间过长的脚本,比如这个脚本中发生了 bug 导致无限循环。
# 当且仅当这个脚本没有执行过任何写操作时,此命令才会生效;脚本被杀死后,执行这个脚本的redis客户端会从 eval 命令的阻塞当中退出,并收到一个错误作为返回值。
script kill

 

lua脚本示例

控制用户访问频率

# visitNumKey 统计访问次数的key,可以拼接userId、ip之类的用户信息;rangeSecond 统计时段 单位秒,比如60;threshold 阈值
EVALSHA sha numkeys 1 visitNumKey rangeSecond threshold
local visitNum = redis.call('incr', KEYS[1])
-- 值为1说明之前不存在这个key,设置key有效期(统计时间片段)
if visitNum == 1 then
    redis.call('expire', KEYS[1], ARGV[1])
end

if visitNum > ARGV[2]) then
        return 0
end

return 1;

 

支持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等更专业的消息中间件,功能更加强大。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Redis(Remote Dictionary Server)是一个开源的内存数据结构存储系统,常用作数据库、缓存和消息中间件。它支持多种数据结构,包括字符串、哈希表、列表、集合和有序集合等,并且提供了丰富的功能和灵活的配置选项。 下面是一些深入了解 Redis 的主题: 1. 数据结构:Redis 提供了多种数据结构,每种数据结构都有自己的特点和用途。了解每种数据结构的基本操作和适用场景,可以更好地利用 Redis 的功能。 2. 持久化:Redis 支持两种持久化方式,分别是快照(RDB)和追加日志文件(AOF)。深入了解这两种方式的工作原理、优缺点和配置选项,可以根据实际需求选择合适的持久化方式。 3. 高可用性:Redis 提供了一些机制来提高系统的可用性,如主从复制、哨兵和集群等。了解这些机制的原理和使用方法,可以搭建高可用的 Redis 环境,并保证数据的可靠性和可恢复性。 4. 事务和管道:Redis 支持事务和管道操作,可以将多个操作封装成一个原子操作或批量操作,提高系统的性能和效率。深入了解事务和管道的使用方法和注意事项,可以更好地利用这些特性提升系统的性能。 5. 性能优化:Redis 是一个高性能的存储系统,但在实际使用中仍然有一些性能优化的技巧。了解 Redis 的内部原理和性能指标,可以通过优化配置、合理使用数据结构和命令,提升系统的响应速度和吞吐量。 以上只是 Redis 的一些高级深入了解的主题,如果你有具体的问题或者需要更详细的解答,可以继续提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值