redis

前言

数据结构

Redis所有数据结构都是字符串作为名称,然后通过key获取不同的value结构数据

字符串

列表

hash

set

zset

list/set/hash/zset都是create if not exists,drop if no elements

分布式锁

异步消息队列

队列为null导致空转

空闲连接自动断开

Redis服务器会自动断开长时间无请求客户端连接,客户端再次请求会抛异常,注意捕获

延迟队列

使用zset实现延迟队列,多线程并发获取任务,loop方法可多线程同时调用,,zrem方法返回决定唯一获取到任务属主

位图

本质是byte数组,用于存储类似于bool型数据,数组自动扩展,某偏移位置超出数组长度,自动将位数组进行零扩充,适用签到等场景。

// 将 1 位设置为 1
setbit s 1 1
// 获取 2 位 的 值
getbit s 2

// 统计为1的位数
bitcount s
// 统计范围start end 为1的位数(start end是8倍数)
bitcount s 0 1
// 从哪一位开始第一个是1的位
bitpos w 1
// 对指定位片段进行读写,但是最多只能处理 64 个连续的位
bitfield w get u4 0 从第一个位开始取 4 个位结果是无符号数 (u)

HyperLogLog

提供不精确的去重计数方案,标准误差是 0.81%,占用12k存储空间

常用于统计场景,但不是哪种特别多数据例如每个用户相关统计

// 增加计数
 pfadd codehole user1
// 获取计数
 pfcount codehole
// 多个pf计数值进行合并
 pfmerge codehole1 codehole2

布隆过滤器

4.0以插件形式出现

 docker pull redislabs/rebloom # 拉取镜像
> docker run -p6379:6379 redislabs/rebloom # 运行容器
> redis-cli #
// 添加元素
bf.add codehole user1
// 是否存在
bf.exists codehole user1
// 添加多个元素
bf.madd codehole user4 user5 user6
// 是否存在多个元素
 bf.mexists codehole user4 user5 user6 user7
 
// 自定义布隆过滤器
bf.reservekey, error_rate, initial_size//  误判率  预计元素数量

漏斗限流

Redis-Cell todo

地理位置距离排序

GeoHash 算法

。GeoHash 算法将二维的经纬度数据映射到一维的整数,个一维的线上获取附近的点。通过无限两刀切分割二维平面为二进制 。

// 添加地理位置
geoadd company 116.48105 39.996794 addressA
// 计算两元素间距离
geodist company addressA addressB km
// 获取地址坐标
geopos company addressA
// 获取元素经纬度hash字符串
geohash company addressA
// 查询指定元素附近其他元素
georadiusbymember company addressA 20 km count 3 asc/desc
// 
georadiusbymember company addressA 20 km withcoord withdist withhash count 3 asc

// 根据坐标值查询元素
georadius company 116.514202 39.905409 20 km withdist count 3 asc

如果数据量过大,还需对数据拆分,国家,省,市,降低单个zset集合大小

模糊匹配

// 没有offset,limit一次获取数量不可预计;会造成redis其他指令阻塞
keys yang*

// 通过游标分布进行不会阻塞线程;提供limit参数;但需客户端进行去重
scan 1996 match key99* count 1000//cursor 正则 limit

1) "12594"
2) 1) "key9939"
 2) "key9941"
 3) "key9967"
 4) "key9938"
 5)
 

Redis的key存储在一个类似于HashMap的字典中,scan返回结果游标为数组中索引slot,limit表示遍历slot数,所以返回结果元素有多有少

scan遍历使用高位加法遍历,避免字典扩缩容导致遍历重复和遗漏

大key会造成集群迁移 redis卡顿,可以通过scan查看key的len大小,类型

此处输入图片的描述 此处输入图片的描述 此处输入图片的描述 此处输入图片的描述

Redis单线程带来的问题

单线程阻塞

阻塞点–客户端交互

键值删除耗时统计

此处输入图片的描述

阻塞点–磁盘交互

阻塞点–从节点加载RDB文件

从节点加载RDB文件时需要先FLUSHDB指令清空当前数据库,造成键值删除阻塞主线程

Redis异步机制

Redis提供异步线程机制来避免阻塞的操作,而可以使用异步线程操作必须满足:该操作不是redis主线程上关键路径的操作(客户端不需要等待操作结果,也就是读操作)。所以上述操作中只有删除和AOF日志同步可以做到异步

主线程启动后会创建三个子线程,对应键值删除,AOF日志同步,文件关闭等操作;使用链表结构的队列(操作入队列)与子线程交互,删除操作如对成功就会返回客户端成功信息(子线程进行惰性删除)

lazy-free

lazy-free在不同环境下,是否异步释放内存

开启lazy-free后,Redis在释放key内存时,会先评估代价,如果释放内存代价小(存储类型,元素数量,综合考量),直接使用主线程进行操作,而不是异步线程

CPU核绑定实例

一个CPU上有多个物理核,一个物理核可能会有多个逻辑核,一个物理核私有L1,L2,不同物理核共享L3;

多CPU架构,应用程序会在不同核上运行,切换带来的缓存命中问题和远端内存访问问题(非同一内存访问架构NUMA),从而导致尾延迟的增高

我们读取网络请求数据时,网络中断程序实例所绑定的CPU SOCKET不一致时,需要把访问命令发送到对应前者核所在本地内存进行数据读取,也就是远端访问,所以建议报网络中断程序核redis实例绑定到同一个CPU SOCKET

lscpu:查看各CPU SOCKET中 逻辑核命名
taskset -c 0 ./redis-server // redis实例绑定逻辑核操作

但是实例绑定同一个逻辑核上又会造成,子线程核主线程的竞争,从而导致阻塞,所以我们绑定的维度应该是物理核

taskset -c 0,12 ./redis-server  // redis实例绑定一个物理核上所有逻辑核操作

修改源码 TODO

Redis毛刺现象

// 监测和统计测试期间内的最大延迟,这个延迟可以作为Redis的基线时延
./redis-cli --intrinsic-latency 120

如果我们通过观察运行时时延大于两倍基线时延时,就可认定Redis性能变慢了

排查,解决Redis变慢

SCAN指令之高位进位法

SCAN指令遍历数据时即使Rehash也不会导致漏key,但是可能会重复key,所以需要到客户端进行一次排重

此处输入图片的描述

扩容:扩容不会导致重复遍历到之前的槽位 缩容:可能会遍历到缩容后的相同槽位的数据

AOF重写和fsync同时发生时造成的主线程阻塞

子线程执行fsync时,如果发现上一次fsync还没有完成时,该子线程会被阻塞

内存不够导致的swap交换区过大

redis-cli info | grep process_id // 查询进程id
cd /proc/5332 // 进入对应进程目录下,进行进程对应swap区的占用查询
cat smaps | egrep '^(Swap|Size)'

内存大页导致的COW算法拷贝的数据也过大

cat /sys/kernel/mm/transparent_hugepage/enabled //查询是否开启内存大页

echo never /sys/kernel/mm/transparent_hugepage/enabled // 关闭内存大页

Redis内存碎片

产生原因

INFO memory 查看Redis内存使用情况

INFO memory
# Memory
used_memory:1073741736 // 实际申请内存
used_memory_human:1024.00M
used_memory_rss:1997159792  // 物理分配内存
used_memory_rss_human:1.86G
…
mem_fragmentation_ratio:1.86 // 碎片率   大于1.5就需要做些碎片整理工作,小于1表示内存不够用,部分内存被置换到swap区

碎片清理

清理碎片会造成主线程阻塞。redis提供自动清理机制,考虑碎片空间占比,本身清理占用CPU时间占比

// 启用自动碎片清理
config set activedefrag yes


// 内存碎片达到多少容量时进行清理
active-defrag-ignore-bytes

// 内存碎片占os分配Reids总空间比例时进行清理

// 自动清理过程占用CPU最小比例
active-defrag-cycle-min

// 自动清理过程占用CPU最大比例
active-defrag-cycle-max

缓存区

输入缓冲区溢出原因

查看和服务端相连客户端输入缓冲区使用情况

CLIENT LIST
id=5 addr=127.0.0.1:50487 fd=9 name= age=4 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=26 //已使用大小 qbuf-free=32742 // 未使用大小 obl=0 oll=0 omem=0 events=r cmd=client

输入缓冲区大小溢出避免

输出缓冲区溢出原因

输出缓冲区大小溢出避免

主从集群缓冲区

读写缓存和只读缓存

根据数据的修改频繁度,一致性要求,访问性能要求,来决定使用哪种缓存读写方案

长尾效应

百分之20的数据贡献百分之80的访问量;

重尾效应

随着个性化越来多,传统二八原理不符合数据请求特征了

缓存最大内存大小配置应该应用数据访问特性成本开销综合考虑,一般建议 15%~30%区间兼顾访问性能和内存空间开销