Redis-其他用法

一 慢查询

1.1 生命周期

我们配置一个时间,如果查询时间超过了我们设置的时间,我们就认为这是一个慢查询.

慢查询发生在第三阶段

客户端超时不一定慢查询,但慢查询是客户端超时的一个可能因素

image-20191225102102218

1.2 两个配置

1.2.1 slowlog-max-len

慢查询是一个先进先出的队列

固定长度

保存在内存中

1.2.2 slowlog-max-len

慢查询阈值(单位:微秒)

slowlog-log-slower-than=0,记录所有命令

slowlog-log-slower-than <0,不记录任何命令

1.2.3 配置方法

1 默认配置

config get slowlog-max-len=128

Config get slowly-log-slower-than=10000

2 修改配置文件重启

3 动态配置

1
2
3
4
5
6
7
8
9
10
11
# 设置记录所有命令
config set slowlog-log-slower-than 0
# 最多记录100条
config set slowlog-max-len 100
# 持久化到本地配置文件
config rewrite

'''
config set slowlog-max-len 1000
config set slowlog-log-slower-than 1000
'''

1.3 三个命令

1
2
3
4
5
6
7
8
9
10
11
12
slowlog get [n]  #获取慢查询队列
'''
日志由4个属性组成:
1)日志的标识id
2)发生的时间戳
3)命令耗时
4)执行的命令和参数
'''

slowlog len #获取慢查询队列长度

slowlog reset #清空慢查询队列

1.4 经验

1
2
3
4
1 slowlog-max-len 不要设置过大,默认10ms,通常设置1ms
2 slowlog-log-slower-than不要设置过小,通常设置1000左右
3 理解命令生命周期
4 定期持久化慢查询

二 pipeline与事务

2.1 什么是pipeline(管道)

Redis的pipeline(管道)功能在命令行中没有,但redis是支持pipeline的,而且在各个语言版的client中都有相应的实现

将一批命令,批量打包,在redis服务端批量计算(执行),然后把结果批量返回

1次pipeline(n条命令)=1次网络时间+n次命令时间

1
pipeline期间将“独占”链接,此期间将不能进行非“管道”类型的其他操作,直到pipeline关闭;如果你的pipeline的指令集很庞大,为了不干扰链接中的其他操作,你可以为pipeline操作新建Client链接,让pipeline和其他正常操作分离在2个client中。不过pipeline事实上所能容忍的操作个数,和socket-output缓冲区大小/返回结果的数据尺寸都有很大的关系;同时也意味着每个redis-server同时所能支撑的pipeline链接的个数,也是有限的,这将受限于server的物理内存或网络接口的缓冲能力

2.2 客户端实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import redis
pool = redis.ConnectionPool(host='10.211.55.4', port=6379)
r = redis.Redis(connection_pool=pool)
# pipe = r.pipeline(transaction=False)
#创建pipeline
pipe = r.pipeline(transaction=True)
#开启事务
pipe.multi()
pipe.set('name', 'lqz')
#其他代码,可能出异常

pipe.set('role', 'nb')

pipe.execute()

2.3 与原生操作对比

1
通过pipeline提交的多次命令,在服务端执行的时候,可能会被拆成多次执行,而mget等操作,是一次性执行的,所以,pipeline执行的命令并非原子性的

2.4 使用建议

1 注意每次pipeline携带的数据量

2 pipeline每次只能作用在一个Redis的节点上

3 M(mset,mget….)操作和pipeline的区别

2.5 原生事务操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 1 mutil  开启事务,放到管道中一次性执行
multi # 开启事务
set name lqz
set age 18
exec

# 2 模拟事务
# 在开启事务之前,先watch
wathc age
multi
decr age
exec

# 另一台机器
mutil
decr age
exec # 先执行,上面的执行就会失败(乐观锁,被wathc的事务不会执行成功)

三 发布订阅

3.1 角色

发布者/订阅者/频道

发布者发布了消息,所有的订阅者都可以收到,就是生产者消费者模型(后订阅了,无法获取历史消息)

3.2 模型

image-20191225163659941

3.3 API

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
publish channel message #发布命令
publish souhu:tv "hello world" #在souhu:tv频道发布一条hello world 返回订阅者个数

subscribe [channel] #订阅命令,可以订阅一个或多个
subscribe souhu:tv #订阅sohu:tv频道

unsubscribe [channel] #取消订阅一个或多个频道
unsubscribe sohu:tv #取消订阅sohu:tv频道

psubscribe [pattern...] #订阅模式匹配
psubscribe c* #订阅以c开头的频道

unpsubscribe [pattern...] #按模式退订指定频道

pubsub channels #列出至少有一个订阅者的频道,列出活跃的频道

pubsub numsub [channel...] #列出给定频道的订阅者数量

pubsub numpat #列出被订阅模式的数量

3.4 发布订阅和消息队列

发布订阅数全收到,消息队列有个抢的过程,只有一个抢到

四 Bitmap位图

4.1 位图是什么

下面是字符串big对应的二进制(b是98)

image-20191225172053447

4.2 相关命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
set hello big #放入key位hello 值为big的字符串
getbit hello 0 #取位图的第0个位置,返回0
getbit hello 1 #取位图的第1个位置,返回1 如上图

##我们可以直接操纵位
setbit key offset value #给位图指定索引设置值
setbit hello 7 1 #把hello的第7个位置设为1 这样,big就变成了cig

setbit test 50 1 #test不存在,在key为test的value的第50位设为1,那其他位都以0补

bitcount key [start end] #获取位图指定范围(start到end,单位为字节,注意按字节一个字节8个bit为,如果不指定就是获取全部)位值为1的个数

bitop op destkey key [key...] #做多个Bitmap的and(交集)/or(并集)/not(非)/xor(异或),操作并将结果保存在destkey中
bitop and after_lqz lqz lqz2 #把lqz和lqz2按位与操作,放到after_lqz中

bitpos key targetBit start end #计算位图指定范围(start到end,单位为字节,如果不指定是获取全部)第一个偏移量对应的值等于targetBit的位置
bitpos lqz 1 #big 对应位图中第一个1的位置,在第二个位置上,由于从0开始返回1
bitpos lqz 0 #big 对应位图中第一个0的位置,在第一个位置上,由于从0开始返回0
bitpos lqz 1 1 2 #返回9:返回从第一个字节到第二个字节之间 第一个1的位置,看上图,为9

image-20191225172547661

4.3 独立用户统计

1 使用set和Bitmap对比

2 1亿用户,5千万独立(1亿用户量,约5千万人访问,统计活跃用户数量)

数据类型每个userid占用空间需要存储用户量全部内存量
set 32位(假设userid是整形,占32位) 5千万 32位*5千万=200MB
bitmap 1位 1亿 1位*1亿=12.5MB

假设有10万独立用户,使用位图还是占用12.5mb,使用set需要32位*1万=4MB

4.5 总结

1 位图类型是string类型,最大512M

2 使用setbit时偏移量如果过大,会有较大消耗

3 位图不是绝对好用,需要合理使用

五 HyperLogLog

5.1 介绍

基于HyperLogLog算法:极小的空间完成独立数量统计

本质还是字符串

5.2 三个命令

1
2
3
4
5
6
7
8
9
10
11
12
13
pfadd key element #向hyperloglog添加元素,可以同时添加多个
pfcount key #计算hyperloglog的独立总数
pfmerge destroy sourcekey1 sourcekey2#合并多个hyperloglog,把sourcekey1和sourcekey2合并为destroy

pfadd uuids "uuid1" "uuid2" "uuid3" "uuid4" #向uuids中添加4个uuid
pfcount uuids #返回4
pfadd uuids "uuid1" "uuid5"#有一个之前存在了,其实只把uuid5添加了
pfcount uuids #返回5

pfadd uuids1 "uuid1" "uuid2" "uuid3" "uuid4"
pfadd uuids2 "uuid3" "uuid4" "uuid5" "uuid6"
pfmerge uuidsall uuids1 uuids2 #合并
pfcount uuidsall #统计个数 返回6

5.3 内存消耗&总结

百万级别独立用户统计,百万条数据只占15k

错误率 0.81%

无法取出单条数据,只能统计个数

六 GEO

6.1 介绍

GEO(地理信息定位):存储经纬度,计算两地距离,范围等

北京:116.28,39.55

天津:117.12,39.08

可以计算天津到北京的距离,天津周围50km的城市,外卖等

6.2 5个城市纬度

城市经度纬度简称
北京 116.28 39.55 beijing
天津 117.12 39.08 tianjin
石家庄 114.29 38.02 shijiazhuang
唐山 118.01 39.38 tangshan
保定 115.29 38.51 baoding

6.3 相关命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
geoadd key longitude latitude member #增加地理位置信息
geoadd cities:locations 116.28 39.55 beijing #把北京地理信息天津到cities:locations中
geoadd cities:locations 117.12 39.08 tianjin
geoadd cities:locations 114.29 38.02 shijiazhuang
geoadd cities:locations 118.01 39.38 tangshan
geoadd cities:locations 115.29 38.51 baoding

geopos key member #获取地理位置信息
geopos cities:locations beijing #获取北京地理信息

geodist key member1 member2 [unit]#获取两个地理位置的距离 unit:m(米) km(千米) mi(英里) ft(尺)
geodist cities:locations beijing tianjin km #北京到天津的距离,89公里

georadius key logitude latitude radiusm|km|ft|mi [withcoord] [withdist] [withhash] [COUNT count] [asc|desc] [store key][storedist key]

georadiusbymember key member radiusm|km|ft|mi [withcoord] [withdist] [withhash] [COUNT count] [asc|desc] [store key][storedist key]
#获取指定位置范围内的地理位置信息集合
'''
withcoord:返回结果中包含经纬度
withdist:返回结果中包含距离中心节点位置
withhash:返回解雇中包含geohash
COUNT count:指定返回结果的数量
asc|desc:返回结果按照距离中心店的距离做升序/降序排列
store key:将返回结果的地理位置信息保存到指定键
storedist key:将返回结果距离中心点的距离保存到指定键
'''
georadiusbymember cities:locations beijing 150 km
'''
1) "beijing"
2) "tianjin"
3) "tangshan"
4) "baoding"
'''

6.4 总结

3.2以后版本才有

geo本质时zset类型

可以使用zset的删除,删除指定member:zrem cities:locations beijing

 

一 持久化的作用

1.1 什么是持久化

redis的所有数据保存在内存中,对数据的更新将异步的保存到硬盘上

1.2 持久化的实现方式

1
2
3
4
5
6
7
快照:某时某刻数据的一个完成备份,
-mysql的Dump
-redis的RDB
写日志:任何操作记录日志,要恢复数据,只要把日志重新走一遍即可
-mysql的 Binlog
-Hhase的 HLog
-Redis的 AOF

二 RDB

2.1 什么是RDB

image-20191226120500154

2.2 触发机制-主要三种方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

'''
save(同步)
1 客户端执行save命令----》redis服务端----》同步创建RDB二进制文件
2 会造成redis的阻塞(数据量非常大的时候)
3 文件策略:如果老的RDB存在,会替换老的
4 复杂度 o(n)
'''

'''
bgsave(异步,Backgroud saving started)

1 客户端执行save命令----》redis服务端----》异步创建RDB二进制文件(fork函数生成一个子进程(fork会阻塞reids),执行createRDB,执行成功,返回给reids消息)
2 此时访问redis,会正常响应客户端
3 文件策略:跟save相同,如果老的RDB存在,会替换老的
4 复杂度 o(n)
'''

'''
自动(通过配置)
配置 seconds changes
save 900 1
save 300 10
save 60 10000
如果60s中改变了1w条数据,自动生成rdb
如果300s中改变了10条数据,自动生成rdb
如果900s中改变了1条数据,自动生成rdb

以上三条符合任意一条,就自动生成rdb,内部使用bgsave
'''

#配置:
save 900 1 #配置一条
save 300 10 #配置一条
save 60 10000 #配置一条
dbfilename dump.rdb #rdb文件的名字,默认为dump.rdb
dir ./ #rdb文件存在当前目录

stop-writes-on-bgsave-error yes #如果bgsave出现错误,是否停止写入,默认为yes
rdbcompression yes #采用压缩格式
rdbchecksum yes #是否对rdb文件进行校验和检验

#最佳配置
save 900 1
save 300 10
save 60 10000
dbfilename dump-${port}.rdb #以端口号作为文件名,可能一台机器上很多reids,不会乱
dir /bigdiskpath #保存路径放到一个大硬盘位置目录
stop-writes-on-bgsave-error yes #出现错误停止
rdbcompression yes #压缩
rdbchecksum yes #校验

2.3 触发机制-不容忽略的方式

1
2
3
1 全量复制 #没有执行save和bgsave没有添加rdb策略,还会生成rdb文件,如果开启主从复制,主会自动生成rdb
2 debug reload #debug级别的重启,不会将内存中的数据清空
3 shutdown save#关闭会出发rdb的生成

2.4 试验

1
2


三 AOF

3.1 RDB问题

耗时,耗性能:

不可控,可能会丢失数据

3.2 AOF介绍

客户端每写入一条命令,都记录一条日志,放到日志文件中,如果出现宕机,可以将数据完全恢复

3.3 AOF的三种策略

日志不是直接写到硬盘上,而是先放在缓冲区,缓冲区根据一些策略,写到硬盘上

always:redis–》写命令刷新的缓冲区—》每条命令fsync到硬盘—》AOF文件

everysec(默认值):redis——》写命令刷新的缓冲区—》每秒把缓冲区fsync到硬盘–》AOF文件

no:redis——》写命令刷新的缓冲区—》操作系统决定,缓冲区fsync到硬盘–》AOF文件

命令alwayseverysecno
优点 不丢失数据 每秒一次fsync,丢失1秒数据 不用管
缺点 IO开销大,一般的sata盘只有几百TPS 丢1秒数据 不可控

3.4 AOF 重写

随着命令的逐步写入,并发量的变大, AOF文件会越来越大,通过AOF重写来解决该问题

原生AOFAOF重写
set hello world
set hello java
set hello hehe
incr counter
incr counter
rpush mylist a
rpush mylist b
rpush mylist c
过期数据
set hello hehe
set counter 2
rpush mylist a b c

本质就是把过期的,无用的,重复的,可以优化的命令,来优化

这样可以减少磁盘占用量,加速恢复速度

实现方式

bgrewriteaof:

客户端向服务端发送bgrewriteaof命令,服务端会起一个fork进程,完成AOF重写

AOF重写配置:

配置名含义
auto-aof-rewrite-min-size AOF文件重写需要尺寸
auto-aof-rewrite-percentage AOF文件增长率
统计名含义
aof_current_size AOF当前尺寸(单位:字节)
aof_base_size AOF上次启动和重写的尺寸(单位:字节)

自动触发时机(两个条件同时满足):

aof_current_size>auto-aof-rewrite-min-size:当前尺寸大于重写需要尺寸

(aof_current_size-aof_base_size)/aof_base_size>auto-aof-rewrite-percentage:(增长率)当前尺寸减去上次重写的尺寸,除以上次重写的尺寸如果大于配置中的增长率

重写流程

image-20191229185839519

配置

1
2
3
4
5
appendonly yes #将该选项设置为yes,打开
appendfilename "appendonly-${port}.aof" #文件保存的名字
appendfsync everysec #采用第二种策略
dir /bigdiskpath #存放的路径
no-appendfsync-on-rewrite yes #在aof重写的时候,是否要做aof的append操作,因为aof重写消耗性能,磁盘消耗,正常aof写磁盘有一定的冲突,这段期间的数据,允许丢失

3.5 AOF 重写演示

1
2


四 RDB和AOF的选择

4.1 rdb和aof的比较

命令rdbaof
启动优先级 高(挂掉重启,会加载aof的数据)
体积
恢复速度
数据安全性 丢数据 根据策略决定
轻重

4.2 rdb最佳策略

rdb关掉,主从操作时

集中管理:按天,按小时备份数据

主从配置,从节点打开

4.3 aof最佳策略

开:缓存和存储,大部分情况都打开,

aof重写集中管理

everysec:通过每秒刷新的策略

4.4 最佳策略

小分片:每个redis的最大内存为4g

缓存或存储:根据特性,使用不通策略

时时监控硬盘,内存,负载网络等

有足够内存

一 子进程开销和优化

1 cpu

开销:rdb和aof文件生成,属于cpu密集型

优化:不做cpu绑定,不和cpu密集型的服务一起部署

2 内存

开销:fork内存开销,copy-on-write,

优化:单机部署尽量少重写

3 硬盘

开销:aof和rdb写入,可以结合分析工具使用

优化:

1 不要和高硬盘负载的服务部署在一起:存储服务,消息队列

2 在aof重写期间,不要对aof进行追加:no-appendfsync-on-rewrite=yes

3 根据写入量决定磁盘类型:例如ssd

4 单机多实例持久化考虑分盘

二 fork操作

1 fork是同步操作

2 与内存量嘻嘻相关:内存越大,耗时越长,跟机型也有关系

3 info:latest_fok_usec:查看持久化执行时间

改善fork

1 有限使用无机或高效支持fork操作的虚拟化技术

2 控制redis实例最大可用内存:maxmemory

3 合理配置linux内存分配策略

4 降低fork频率,例如放宽aof重写自动触发时机,不必要的全量复制

三 aof追加阻塞

image-20191229192629198

aof阻塞:看日志定位

info Persistence:每次阻塞一次就会+1

 

 

posted @ 2020-12-26 04:29  silencio。  阅读(95)  评论(0编辑  收藏  举报