1、慢查询分析
慢查询日志:系统在命令执行前后计算每条命令的执行时间,当超过预设阀值,就将该条命令的相关信息记录下来(比如发生时间、耗时、命令详细信息)记录下来。
redis客户端执行一条命令有以下步骤:
1)、发送命令;
2)、命令排队;
3)、命令执行;
4)、命令返回;
慢查询只统计步骤3的时间,故没有慢查询并不代表客户端没有超时。
1.1 :慢查询配置参数
redis提供了两个可供配置参数:
1)、slowlog-log-slower-than:预设阀值,其单位为微妙,默认值为10000;
2)、slowlog-max-len:redis使用一个列表来存储慢查询日志,slowlog-max-len就是列表的最大长度。一个新的命令满足慢查询条件时被插入这个列表中,当慢查询日志列表已处于其最大长度时,最早插入的一个命令将从列表中移出。
redis中有两种修改配置的方式:修改配置文件或使用config set命令动态修改。
config set slowlog-log-slower-than 20000
config set slowlog-max-len 1000
config rewrite
若需要将配置持久化到配置文件,需执行config rewrite命令。
管理慢查询日志:
1)、获取慢查询日志:
slowlog get [n]
每个慢查询日志由4个属性组成,分别是慢查询日志标识id、发生时间戳、命令耗时、执行命令及参数;
2)、获取慢查询日志列表当前的长度:
slowlog len
3)、慢查询日志重置:
slowlog reset
1.2 :配置建议
1)、slowlog-max-len配置建议:线上建议调大慢查询列表,记录慢查询时redis会对长命令做截断操作,并不会占用大量内存;
2)、slowlog-log-slower-than:默认命令执行超过10毫秒的命令判定为慢查询,需根据redis并发量调整该值;
3)、慢查询只记录命令执行时间,并不包括命令排队和网络传输时间;
4)、由于慢查询日志是一个先进先出的队列,如果在慢查询比较多的情况下就会丢失部分慢查询命令;
2、redis shell
redis提供了redis-cli、redis-server、redis-benchmark等shell工具。
2.1 :redis-cli
命令详解:
1、-r(repeat):代表将同一条命令执行多次:
redis-cli -a xxx -r 3 ping
2、-i(interval):代表每隔几秒钟执行一次命令,-i 选项必须与-r 选项一起使用:
redis-cli -a xxx -r 5 -i 1 ping
3、-x :代表从标准输入读取数据作为redis-cli的最后一个参数:
$ echo "redis" | redis-cli -a yhj1994 -x set hello
4、-c(cluster) :连接redis-cluster节点时需要使用的,可以防止moved和ask异常:
5、-a(auth) :如果配置了密码,此选项和避免输入密码:
6、–scan和–pattern:用于扫描特定模式的键,相当于scan命令:
7、–slave:可以将当前客户端模拟成当前节点的从节点,用来获取当前redis节点的更新操作:PING命令是主从复制产生的;
8、–rdb:该选项会请求redis实例生成并发送rdb持久化文件保存到本地:
9、–pipe:用于将命令封装成redis通信协议定义的数据格式,批量发送给redis执行:
10、–bigkeys:该选项使用scan命令对redis的键进行采样,从中找到内存占用比较大的键值:
redis-cli -a xxx --bigkeys
11、–eval:用于执行指定Lua脚本:
12、–latency:
1)、–latency:测试客户端到目标redis的网络延迟;
2)、–latency-history:以分时段的形式展示延迟信息;
3)、–latency-dist:使用统计图表的形式从控制台输出延迟统计信息;
redis-cli -a xxx --latency
redis-cli -a xxx --latency-history
redis-cli -a xxx --latency-dist
13、–stat:实时获取redis的重要统计信息,虽info命令的统计信息更全面,但stat命令能看到一些增量信息;
redis-cli -a xxx --stat
14、–raw和–no-raw:
–no-raw选项要求命令的返回结果必须是原始格式,–raw要求命令返回的结果必须是序列化之后的结果;
redis-cli -a xxx --raw
redis-cli -a xxx --no-raw
2.2 :redis-server
redis-server除了启动redis外,还有一个–test-memory选项,该命令用来测试当前操作系统能否稳定地分配指定容量的内存给redis。
redis-server --test-memory 1024
参考地址:
http://www.memtest86.com
http://pyropus.ca/software/memtester
2.3 :redis-benchmark
1、-c(clients):代表客户端的并发数量(默认50);
2、-n(num) :代表客户端请求总量(默认100000):
redis-benchmark -c 100 -n 20000
代表100个客户端同时请求redis,共执行20000次;
3、-q:仅仅显示redis-benchmark的request per second的值:
redis-benchmark -c 100 -n 20000 -q
4、-r:随机向库中插入随机键值,选项会在key、counter键上加一个12位的后缀,-r 10000表示只对后四位进行随机处理:
redis-benchmark -c 100 -n 20000 -r 10000
5、-P:代表每个请求pipeline的数据量(默认1);
6、-k :代表客户端是否使用keepalive,1为使用,0为不使用,默认使用;
7、-t :可以指定命令进行基准测试:
redis-benchmark -t get,set -q
8、–csv:将结果按照csv格式输出,便于后续处理:
redis-benchmark -t get,set --csv
3、Pipeline
3.1 :概念
redis客户端执行命令分为4步:
1)、发送命令;
2)、命令排队;
3)、命令执行;
4)、返回结果;
其中第1和第4步成为Round Trip Time(RTT往返时间)。
redis提供了批量操作命令(mget、mset等),有效节约RTT,但大部分命令是不支持批量操作的;
Pipeline机制可改善上述问题,它将一组redis命令进行组装,通过一次RTT传输给redis,再将这组redis命令执行结果按顺序返回给客户端;
3.2 :原生命令对比
1、原生批量命令是原子的,Pipeline是非原子的;
2、原生批量命令是一个命令对应多个key,Pipeline支持多个命令;
3、原生批量命令是redis服务端支持实现的,而Pipeline需要服务端和客户端的共同实现;
3.3 :配置建议
实际当中Pipeline组装的命令个数不能没有节制,否则一次组装Pipeline数据量过大,一方面会增加客户端的等待时间,另一方面会造成网络阻塞,可以将一次包含大量命令的Pipeline拆分成多次较小的Pipeline来完成。
4、事务与Lua
4.1 :redis事务
redis提供了简单的事务功能,将一组需要一起执行的命令放到multi和exec两个命令之间,multi命令代表事务开始,exec命令代表事务结束,它们之间的命令是原子顺序执行的:
multi
sadd user:a:follow user:b
sadd user:b:fans user:a
只有在exec命令执行后,事务才算结束:
exec
sismember user:a:follow user:b
如果要停止事务的执行,使用discard命令代替exec命令即可:
discard
sismember user:a:follow user:b
1、命令错误:
下面操作误将set写成了sett,属于语法错误,会造成整个事务无法执行,key和counter的值未发生变化:
multi
sett key world
incr counter
exec
2、运行时错误:
例如用户b再添加粉丝列表时,误将sadd命令写成zadd命令,这种情况redis并不会回滚,因为这属于运行时错误:
multi
sadd user:a:follow user:b
zadd user:b:fans 1 user:a
exec
有些场景需要在事务之前,确保事务中的key没有被其他客户端修改过才执行事务,否则不执行,故redis提供了watch命令来解决这类问题。
时间点 | ClientA | ClientB |
---|---|---|
T1 | set key “java” | |
T2 | watch key | |
T3 | multi | |
T4 | append key python | |
T5 | append key redis | |
T6 | exec | |
T7 | get key |
最终事务未执行,返回结果为"javapython"
4.2 :redis与Lua
1、在redis当中使用Lua:
1)、eval:
eval 脚本内容 key个数 key列表 参数列表
eg:eval 'return "hello " .. KEYS[1] .. ARGV[1]' 1 redis world
如果Lua脚本较长,还可以使用redis-cli–eval直接执行文件。
2)、evalsha:
首先将Lua脚本加载到服务端,得到该脚本的SHA1校验和,evalsha命令使用sha1作为参数可以直接执行对应Lua脚本,避免每次发送Lua脚本的开销。如此客户端就不需要每次执行脚本内容,脚本也会常驻在服务端,脚本功能得到了复用。
加载脚本:script load命令可以将脚本内容加载到redis内存中;
redis-cli script load "${cat lua_get.lua}"
执行脚本:evalsha命令使用sha1值,执行逻辑与eval一致;
evalsha xxxx 1 redis world
2、Lua的redis api:
Lua可以使用redis.call函数实现对redis的访问,以下示例调用get/set命令:
redis.call("set","hello","world")
redis.call("get")
在redis中执行:
eval 'return redis.call("get",KEYS[1])' 1 hello
3、Lua脚本对redis开发运维的好处:
1)、Lua脚本在redis中是原子执行的,执行过程中间不会插入其他命令;
2)、Lua命令脚本可以定制命令并可以将命令常驻在redis内存中;
3)、Lua脚本可以将多条命令一次性打包,有效减少网络开销;
案例:
redis-cli -a xxx --eval /xx/xx/lrange_and_mget.lua hot:user:list
脚本lrange_and_mget.lua内容:
local mylist = redis.call("lrange",KEYS[1],0,-1)
local count = 0
for index,key in ipairs(mylist)
do
redis.call("incr",key)
count = count + 1
end
return count
4.3 :管理Lua脚本
redis提供了4个命令实现对Lua脚本的管理:
1)、script load:命令用于将Lua脚本加载到redis内存当中;
script load script
2)、script exists:判断sha1是否已经加载到redis内存中;
script exists sha1 [sha1 ...]
3)、script flush:清除redis内存中已加载的所有Lua脚本;
4)、script kill:杀掉正在执行的脚本,如果Lua脚本比较耗时,甚至Lua脚本本身存在问题,那么此时Lua脚本的执行会阻塞redis;
如果Lua脚本在执行写入操作,那么script kill将不会生效,只能使用shutdown nosave停掉服务;
5、bitmaps
5.1 :结构模型
1、bitmaps本身不是一种数据结构,实际上它就是字符串,但是它可以对字符串的位进行操作;
2、bitmaps单独提供了一套命令,所以在redis中使用bitmaps和使用字符串不太相同。可以理解为一个以位为单位的数组,数组的每个单元只能存储1和0,数组下标在bitmaps中称之为偏移量;
5.2 :常用命令
命令 | 解释 |
---|---|
setbit key offset value | 设置值 |
getbit key offset | 获取值 |
bitcount key [start] [end] | 获取bitmaps指定范围值为1的个数 |
bitop op destkey key [key …] | bitmaps间的运算 |
bitpos key targetBit [start] [end] | 计算bitmaps中第一个值为targetBit的偏移量 |
6、HyperLogLog
6.1 :常用命令
1、添加:
pfadd key element [element ...]
2、计算独立用户数:
pfcount key [key ...]
集合类型与HyperLogLog占用空间对比
数据类型 | 1天 | 1个月 | 1年 |
---|---|---|---|
集合类型 | 80M | 2.4G | 28G |
HyperLogLog | 15K | 450K | 5M |
3、合并:
pgmerge destkey sourcekey [sourcekey ...]
pfmerge可以求出多个HyperLogLog的并集并赋值给destkey。
7、发布订阅
7.1 :常用命令
1、发布命令:
publish channel message
2、订阅消息:
subscribe channel [channel ...]
注意点:
1)、客户端在执行订阅命令之后进入了订阅状态,只能接受subscribe、psubscribe、unsubscribe、punsubscribe的四个命令;
2)、新开启的订阅客户端,无法收到该频道之前的消息,因为redis不会对发布的消息进行持久化;
3、取消订阅:
unsubscribe [channel [channel ...]]
4、按照模式订阅或取消:
psubscribe pattern [pattern ...]
punsubscribe [pattern [pattern ...]]
5、查询订阅:
1)、查看活跃频道:
pubsub channels [pattern]
2)、查看频道订阅数:
pubsub numsub [channels]
3)、查看模式订阅数:
pubsub numpat
8、GEO
GEO功能支持存储地理位置信息用来实现诸如附近位置、摇一摇等依赖于地理位置信息的功能
8.1 :常用命令
1、增加地理位置信息:
geoadd key longitude latitude member [longitude latitude number ...]
eg:geoadd cities:locations 116.30 40.00 beijing
如果key对应的member添加结果返回为1则为新增,为0表示已存在或更新。
2、获取地理位置信息:
geopos key member [member ...]
3、获取两个地理位置的距离:
geodist key member1 member2 [unit]
unit可选值:
1)、m 代表米;
2)、km 代表公里;
3)、mi 代表英里;
4)、ft 代表尺;
4、获取指定位置范围内的地理信息位置集合:
georadius key longitude latitude radius m|km|mi|ft [withcoord] [withdist] [withhash] [COUNT count] [asc|desc] [store key] [storedist key]
georadiusbymember key member radius m|km|mi|ft [withcoord] [withdist] [withhash] [COUNT count] [asc|desc] [store key] [storedist key]
两个命令的效果类似,都是给定一个具体位置及半径求出半径范围内匹配的位置。
参数解释:
1)、withcoord:返回结果中包含经纬度;
2)、withdist:返回结果中包含离中心节点位置的距离;
3)、withhash:返回结果中包含geohash;
4)、COUNT count:指定返回结果的数量;
5)、asc|desc:返回结果根据距中心距离升序或降序;
6)、store key:将返回结果的地理位置信息保存到指定key;
7)、storedist key:将返回结果距离中心结果的距离保存到指定key;
5、获取geohash:
geohash key member [member ...]
redis使用geohash将二维经纬度转换为一维字符串。
geohash的特点:
1)、GEO的数据类型为zset,redis将所有地理位置信息的geohash存放在zset中;
2)、字符串越长,表示的位置更精确;
3)、两个字符串相似度越近,它们之间的距离越近,redis使用字符串的前缀匹配算法实现相关命令;
4)、geohash编码的经纬度是可以相互转化的;
geohash长度与精度的对应关系
geohash长度 | 精度(km) |
---|---|
1 | 2500 |
2 | 630 |
3 | 78 |
4 | 20 |
5 | 2.4 |
6 | 0.61 |
7 | 0.076 |
8 | 0.019 |
9 | 0.002 |
6、删除地理位置信息:
zrem key member
GEO未提供删除成员命令,但因其底层实现是zset,故可使用zrem命令实现对地理位置信息的删除
参考《redis开发与运维》
redis官网文档:https://redis.io/documentation