二、redis高级技巧

除了基本的五种数据结构提供的功能以外,redis还提供了其他的功能

慢查询分析

类似关系性数据库,redis也有机制来保存和查看慢查询

  1. 预设值 slowlog-log-slower-than
    如果比这个值大,则会记录日志,默认值时10000,可以通过config get slowlog-log-slower-than命令获取
  2. 日志存储 slowlog-max-len
    同普通数据一样,慢查询日志也是存在内存当中,是一个先进先出的队列,而slowlog-max-len这个值,代表的就是该日志队列的长度的长度。同样可以通过CONFIG get slowlog-max-len,默认是128
  3. 查询慢查询日志 slowlog subcommand [arg]
    可以获取该条慢查询的id、时间戳、命令耗时和命令内容

pipeline

Pipeline机制能让一组Redis的命令,通过一次RTT传输到服务器,并将结果按顺序返回给客户端,在网络延时较大时,提升性能非常明显。因为很多情况下,redis的性能瓶颈都在网络上。

与原生批量命令如mset、hmset、hmget有以下不同:

  • 原生批量命令是原子的,而pipepline不是
  • 原生批量命令一般都是多个不同的key,而pipeline不是
  • 原生批量命令是服务端支持的,而pipeline是客户端和服务端之间的

事务

同关系型数据类似,redis也支持事务,事务的写法如下:

multi
command arg
...
exec # 如果要取消,则使用discard来替换

错误处理:

  1. 命令错误,例如set写成了sett,那么整个事务无法提交

  2. 运行时错误,例如sadd,写成了zadd,语法没有错,但运行时报错。这种场景会提交正确的命令,不支持回滚

  3. 对于事务过程中,其他客户端导致的修改,可以使用watch机制来抛弃这次事务,有点类似java中的CAS:

    set key java
    #客户端1
    watch key
    multi
    append key python
    #客户端2
    append key jedis
    #客户端1
    exec
    (nil) # 也就是提交失败
    
  4. 缺点不支持回滚,不过不会滚会带来以下好处

    • 仅当使用错误的语法(并且在命令队列期间无法检测到该问题)或针对包含错误数据类型的键调用Redis命令时,该命令才能失败:这实际上意味着失败的命令是编程错误的结果, 以及一种很可能在开发过程中而不是生产过程中发现的错误。也就是说这种错误需要我们从程序中改
    • Redis在内部得到了简化和更快,因为它不需要回滚的能力

Lua脚本

  1. 基本语法
  2. redis中使用Lua
    • 在客户端中使用eval命令

      # eval [脚本内容]  [key的个数]  [key列表]  [参数列表]
      eval 'return "hello" .. KEYS[1]..ARGV[1]' 1 redis world
      
    • redis-cli --eval 直接执行文件,该方法本质和eval是一样的,过程如下

      eval命令执行Lua脚本过程.PNG

    • evalsha 除了以上方法,也可以使用evalsha将脚本加载在内存当中,以键值对的方式存储,其中key为脚本的SHA1校验值,value就是脚本。

      redis-cli script load "$ cat lua_get.lua"
      "a4eb97a51a5fd626ad3966923fa44190a2283cbb" # 返回sha1值
      # evalsha [脚本sha1值]  [key的个数]  [key列表]  [参数列表]
      evalsha a4eb97a51a5fd626ad3966923fa44190a2283cbb 1 redis world
      
    • Lua在调用的时候可以反过来通过redis.call() ,实现对redis访问

    • Lua脚本在redsi中是原子执行的,我们可以利用Lua脚本来定制功能,并常驻在redis内存中。

  3. redis管理Lua脚本
    • script load 加载脚本
    • script exists 判断是否存在sha1
    • script flush 用于清除redis已加载的Lua脚本
    • script kill 停止正在执行的Lua脚本,在耗时较长或脚本存在问题时很有用,但如果脚本正在写,则不会kill

HyperLogLog

HyperLogLog不是一种新的数据结构(实际结构是字符串),而是一种基数算法,它可以实现用极小的内存空间完成独立总数的统计。

  • 什么叫独立总数统计
    类似数据库中的distinct 关键字,也就是去重后的总数,这种情形使用计数器是无法做到的,但如果用set这种数据结构,则会占用大量内存。

  • 用途:只为了计算独立总数,不需要获取单条数据源

  • 缺点:会有一定的误差(官方统计是0.81%),所以需要容忍一定的误差率。

  • 三个命令:

    • PFADD key element[element],添加一个数据 e.g pfadd user user1 user2 user3
    • pfcount 统计独立总数
    • pfmerge destkey sourcekey[...],将多个HyperLogLog合并

发布订阅

redis的消息发布,就是传统的发布/订阅模型,生产者产生消息到服务端,然后服务端会通知订阅了这个channel的订阅者。

  1. 发布 publish channel message

    127.0.0.1:6379> publish channel:test "i am test message"
    (integer) 0  #返回0 代表没有观察者
    
  2. 消息订阅 subscribe channel [...] 订阅一个或多个channel

    #1. 客户端1 订阅该channel
    SUBSCRIBE channel:test
    Reading messages... (press Ctrl-C to quit)
    1) "subscribe"
    2) "channel:test"
    3) (integer) 1
    #2. 客户端2 发布消息到该channel
    127.0.0.1:6379> publish channel:test "i am test message2"
    (integer) 1
    #3. 客户端1 收到通知
    1) "message"
    2) "channel:test"
    3) "i am test message2"
    
  3. 取消订阅 unsubscribe [channel]
    取消之后就不再收到该channel发送的消息,另外再redis-cli客户端中我们无法执行这个命令,ctrl-c会退出该客户端,因此可以采用java客户端jedis来测试

  4. 按照channel匹配模式来订阅或者取消 psubscribe pattern [...]

  5. 查询订阅

    • 查看活跃的channel,也就是有订阅客户端的channel: pubsub channels [pattern]
    • 查看channel的订阅客户端数: pubsub numsub [channel ...]

缺陷
redis的发布订阅模型相对专业的消息队列如kafka、RocketMQ比较简单,不支持消息的持久化,也就是无法重复消费,或者回溯消息以及其他高级功能。

GEO

地理位置,支持存储地理位置信息来实现一些功能。

  1. 增加地理位置信息geoadd,结果返回成功的个数,如果已存在或者只是修改,返回0
  2. 获取地理位置信息geopos
  3. 获取两个地理位置之间的距离geodist
  4. 返回指定范围内的地理位置集合georadius和georadiusbymemeber,一个使用的地理位置值,一个是用成员
  5. 获取地理位置信息的hash值,hash值越长,代表越精确
#geoadd key longitude latitude memeber [...other]
# 添加北京和天津的地理位置
127.0.0.1:6379> GEOADD cities:location 116.28 39.55 beijing
(integer) 1
127.0.0.1:6379> GEOPOS cities:location beijing
1) 1) "116.28000229597091675"
   2) "39.5500007245470826"
127.0.0.1:6379> GEOADD cities:location 114.29 38.02 tianjing
(integer) 1

#计算两者之间的距离
127.0.0.1:6379> geodist cities:location beijing tianjing
"242326.2997"
# 求出指定坐标下,指定半径内的城市
127.0.0.1:6379> GEORADIUS cities:location 120.5 39.3 1000 km
1) "tianjing"
2) "beijing"
# 求出指定城市下,指定半径内的城市
127.0.0.1:6379> GEORADIUSBYMEMBER cities:location beijing 300 km
1) "tianjing"
2) "beijing"

# 获取hash值
127.0.0.1:6379> GEOHASH cities:location beijing tianjing
1) "wx48ypbe2q0"
2) "wwc229r6w10"

geo利用的是有序列表(sorted set)这种结构,并结合geohash的一些特性来实现。我们可以尝试用zXXX命令来查看

127.0.0.1:6379> ZRANGE cities:location 0 -1
1) "tianjing"
2) "beijing"
127.0.0.1:6379> 

# 删除
127.0.0.1:6379> ZREM cities:location beijing
(integer) 1

redis 自带的shell脚本

redis提供了redis-cli、redis-server等工具脚本。有时候很有用

  1. redis-cli
    使用redis-cli --help 获取使用指南

    • -r 重复执行命令多次 ./redis-cli -r 3 incr count
    • -i 每隔几秒执行一次,和-r参数一起使用
    • -x 读取数据作为最后一个参数你。echo "world" | redis-cli -x set hello
    • -a (auth),如果配置了密码,需要鉴权
    • --slave,将客户端模拟redis的从节点
    • --bigkeys 查看大对象
  2. redis-server,有个--test-memory 的参数,检测是否有足够内存

  3. redis-benchmark,提供了很多性能测试

    • -c 表示客户端的并发数量(client,默认是50)
    • -n nums代表客户端请求总量
    • -q 仅显示每秒请求的数量,也就是简要信息
    • -r 随机向redis插入更多的键值对

你可能感兴趣的:(二、redis高级技巧)