Redis学习马士兵
2020-08-31 16:34:37 17 举报
AI智能生成
Redis学习笔记
作者其他创作
大纲/内容
性能参数
1.5M ops/sec
秒级10W操作???
<1ms延迟(latency)
全内存
数据类型和底层数据结构
Redis是利用KV对的数据结构,所以KV层是利用的哈希表
KV查询图例
对应K的查询是依赖哈希计算,时间复杂度O(1)
对Value为集合类型值的处理逻辑:
1. 通过全局哈希表找到对应的哈希桶:
2. 在集合中(Value)再增删改查
1. 通过全局哈希表找到对应的哈希桶:
2. 在集合中(Value)再增删改查
底层数据结构
简单动态字符串
String
双向链表
List
压缩表
List / Hash
头尾操作O(1)
中间数据的查询操作O(N)
中间数据的查询操作O(N)
哈希表
Hash / Set
跳表
Sorted Set
实质:增加了多级索引,通过索引位置的跳转,实现数据的快速定位
时间复杂度O(logN)
时间复杂度O(logN)
整数数组
Set
潜在风险和突然变慢的原因?
哈希表的冲突问题
利用链表解决
查询时间复杂度变为O(n)
n为链表的长度
n为链表的长度
哈希表大小变化引发的rehash操作,
带来操作阻塞
带来操作阻塞
解决方案
双全局哈希表
过程:
1. 给哈希表2分配更大的空间;
2. 把哈希表1的数据重新映射并拷贝到哈希表2;
3. 释放哈希表1的空间
1. 给哈希表2分配更大的空间;
2. 把哈希表1的数据重新映射并拷贝到哈希表2;
3. 释放哈希表1的空间
改进策略:渐进式rehash
复杂度的特点
单元素操作,通常看具体使用的数据结构,大多数为O(1)
范围操作,通常返回集合中的多个数据,时间复杂度一般为O(N),比较耗时,耗资源
统计操作通常比较快,因为额外的存储空间记录了元素的个数,所以多为O(1)
整数数组和压缩列表在查找时间复杂度方面并没有很大的优势,
那为什么 Redis 还会把它们作为底层数据结构呢?
那为什么 Redis 还会把它们作为底层数据结构呢?
内存利用率。数组和压缩列表是非常紧凑的数据结构,比链表占用的内存更少。Redis是内存数据库,大量时间的内存存放,需要尽可能优化
数组对CPU告诉缓存支持更友好。
Redis在设计时,集合数据元素较少的时候,默认采用内存紧凑排列的方式存储,同时利用CPU高速缓存提高访问速度。
当数据元素超过设定阈值后,避免查询时间复杂度太高,转为哈希和跳表数据结构存储,保证查询效率。
Redis在设计时,集合数据元素较少的时候,默认采用内存紧凑排列的方式存储,同时利用CPU高速缓存提高访问速度。
当数据元素超过设定阈值后,避免查询时间复杂度太高,转为哈希和跳表数据结构存储,保证查询效率。
KV对
key对象
指示value对象的数据类型
可以用type命令查看
编码encoding
用OBJECT encoding KEY查看
帮助一些方法的使用时,能加速
包含:
raw/int/ht/zipmap/linkedlist/ziplist/intset
raw/int/ht/zipmap/linkedlist/ziplist/intset
数据指针(ptr)
虚拟内存(vm)
是一个对象
Redis内部用一个redisObject对象来表示所有的key和value
value
支持5+2数据类型
支持操作和运算
5+2数据类型
String
字符串
常用操作
SET/GET
EX/PX/NX/XX/KEEPTTL
SETNX
可以作为锁,
当有值的,SETNX返回1,没有值的时候返回0.
根据返回值,确定是否获得锁
当有值的,SETNX返回1,没有值的时候返回0.
根据返回值,确定是否获得锁
bitmaps
对应关系
常用操作
BITCOUNT
BITOP
SETBIT
BITPOS
BITOP
SETBIT
BITPOS
应用场景
用户系统,统计用户登录天数,且窗口随机
解决方案:用46Bytes表示全年。其每一天用一个位表示。
SITBIT USER 1 1
SITBIT USER 7 1
SITBIT USER 364 1
统计最后两周是否有登录:BITCOUNT USER -2 -1
K(用户): V(每一天用一位代表)
数据总量:每用户46Bytes * 用户数 1,000,000,000
解决方案:用46Bytes表示全年。其每一天用一个位表示。
SITBIT USER 1 1
SITBIT USER 7 1
SITBIT USER 364 1
统计最后两周是否有登录:BITCOUNT USER -2 -1
K(用户): V(每一天用一位代表)
数据总量:每用户46Bytes * 用户数 1,000,000,000
京东活动,618做活动,送礼物。大库需要备多少礼物?假设有2亿用户。
(用户分为:僵尸用户、冷热用户、忠诚用户)
活跃用户统计:随机窗口
方案:SITBIT 20200101 1 1 (1=>用户,1=>登录)
SETBIT 20200102 1 1
SETBIT 20200102 7 1
BITOP OR DESTKEY 20200101 20200102
BITCOUNT DESTKEY 0 -1
K(天) :V(每一个用户用一个位代表)
(用户分为:僵尸用户、冷热用户、忠诚用户)
活跃用户统计:随机窗口
方案:SITBIT 20200101 1 1 (1=>用户,1=>登录)
SETBIT 20200102 1 1
SETBIT 20200102 7 1
BITOP OR DESTKEY 20200101 20200102
BITCOUNT DESTKEY 0 -1
K(天) :V(每一个用户用一个位代表)
int
常用操作
incr
desr
应用场景
抢购,秒杀,详情页,点赞,评论等不需要严格事务处理的事务
规避并发下,对数据库的实务操作,完全有redis内存操作代替
规避并发下,对数据库的实务操作,完全有redis内存操作代替
hashes
指Value值是一个hashmap
可以对数值进行计算
常用命令
HGET
HSET
HGETALL
应用场景
点赞、收藏、详情页
lists
实现数据结构
栈Stack
同向命令
队列Queue
逆向命令
数组
对Index操作
阻塞队列、单播订阅
FIFO
FIFO
用B*操作
底层实现
带头、尾指针的双向链表
O(1)正反向查询
内存开销略大
常用命令
L/R POP L/R PUSH
LINDEX
LRANGE
LINSERT
当有重复值的时候,仅操作于正向的第一个值前或后
LREM
count有三种值
<0 从后算
>0 正向算
=0 remove所有相等的值
<0 从后算
>0 正向算
=0 remove所有相等的值
BLPOP/BRPOP/BRPOPUSH
LTRIM
应用场景
类似Wechat的关注列表
消息队列
消息队列
sets
特征
无序
去重
注意事项
集合等操作消耗资源
如果必须需要此类操作,
建议将此类Redis单独出来
如果必须需要此类操作,
建议将此类Redis单独出来
常用操作
SADD/SREM/SMOVE/SPOP
SMEMBERS
集合操作
SINTER/
SINTERSTORE
SINTERSTORE
SUNION/
SUNIONSTORE
SUNIONSTORE
SDIFF/
SDIFFSTORE
SDIFFSTORE
有方向性
随机事件
SRANDOM
应用场景
共同好友
(利用唯一性,统计访问网站的所有IP)
(利用唯一性,统计访问网站的所有IP)
sorted sets
ZSET
ZSET
实现方式
HashMap
跳表skipList
应用场景
排行榜:带权重的消息队列
(通过用户额外提供一个优先级(score)
的参数来为成员排序)
(通过用户额外提供一个优先级(score)
的参数来为成员排序)
二进制安全
编码并没有影响数据长度
redis通信用字节流
淘汰算法
LRU
volatile
allkeys
volatile-ttl
回收过期的
有限回收存活时间(TTL)短的
RANDOM
volatile
allkeys
LFU
(Least Frequency Use)
最不经常使用
Redis >= 4.0
(Least Frequency Use)
最不经常使用
Redis >= 4.0
volatile
allkeys
不淘汰策略
NOEVICTION
NOEVICTION
管理技巧
设置password
Redis非常快,所以需要使用强password
例如:
openssl rand 60 | openssl base64 -A
生成强password
openssl rand 60 | openssl base64 -A
生成强password
避免危险命令
命令集
FLUSHDB/FLUSHALL
KEYS
PEXPIRE
DEL/CONFIG/SHUTDOWN
SPOP/SREM/RENAME
DEBUG
禁止命令
rename-command FLUSHDB ""
rename-command FLUSHALL ""
rename-command DEBUG ""
rename-command FLUSHALL ""
rename-command DEBUG ""
改名
rename-command SHUTDOWN SHUTDOWN_MENOT
rename-command CONFIG ASC12_CONFIG
rename-command CONFIG ASC12_CONFIG
注意
. . .
# Please note that changing the name of commands that are logged into the
# AOF file or transmitted to replicas may cause problems.
. . .
# Please note that changing the name of commands that are logged into the
# AOF file or transmitted to replicas may cause problems.
. . .
常识
磁盘
寻址:MS
带宽:G/M
内存
寻址: ns
网卡
ms级响应
寻址:磁盘比内存在寻址上慢了10万倍
IO Buffer:成本问题
磁盘于磁道:扇区:一个扇区512Byte
如果扇区很多,索引成本变大。
磁盘于磁道:扇区:一个扇区512Byte
如果扇区很多,索引成本变大。
操作系统默认读写4K (从磁盘)
data pag 4K
data pag 4K
数据在内存和磁盘中的体积不一样
在磁盘存储时,存在大量的冗余现象
内存存储优化,压缩等
计算机两个基本设施
冯洛伊曼体系硬件
以太网TCP/IP网络
不稳定,可能丢包
概述
内存KV数据库
速度快,支持10W+QPS
单进程,单线程,线程安全,采用IO多路复用机制
网络IO和键值对读写是由一个线程来完成的
Value支持丰富的数据类型,且支持数据操作
支持数据持久化
集群,支持主从复制,哨兵
可以用作分布式锁
可以做消息中间件,支持发布订阅
为啥单线程Redis快?
Redis只是在网络IO和键值对读写上是单线程,其他处理会利用多线程,例如AOF等
多线程不快的几个原因
如果有竞争资源,例如多线程同时访问共享资源,
则引来额外开销,而且增加系统设计实现的复杂度,例如锁等
则引来额外开销,而且增加系统设计实现的复杂度,例如锁等
大部分操作在内存上完成
采用高效的数据结构,如哈希表,跳表
采用多路复用机制,使在网络IO操作中能够并发处理大量的客户端请求,实现高吞吐率
允许内核中,同时存在多个监听套接字和已连接的套接字
select/epoll提供了基于事件的回调机制
即针对不同时间的发生,调用响应的处理函数
即针对不同时间的发生,调用响应的处理函数
单线程处理IO请求的性能瓶颈
任意一个请求在Server中,一旦发生耗时的操作,都会影响整个Server的性能
即影响后续处理的请求,(按序执行)。
主要耗时操作是:
即影响后续处理的请求,(按序执行)。
主要耗时操作是:
操作bigkey,添加和删除时均耗时
>4.0,推出了Lazy-free机制,把bigkey是否内存的耗时操作放在了异步线程中执行
使用复杂度过高的命令:如SORT/SUNION/ZUNION,
或者O(N)的命令,当N很大的时候。
或者O(N)的命令,当N很大的时候。
大量key集中过期
Redis的过期机制也是主线程负责,大量key集中过期,则每处理一个请求时候,都需要耗时删除过期key
淘汰策略
淘汰策略也是主线程负责
AOF开启always策略
主从全量同步生成RDB
虽然采用fork,但fork的这一瞬间仍然会阻塞整个线程,实例越大,阻塞越长
并发量很大的时候,单线程读写客户端IO数据存在性能瓶颈
读写客户端依然采用同步模型,
只能单线程依次读取客户端数据,无法利用到CPU多核。
只能单线程依次读取客户端数据,无法利用到CPU多核。
>6.0, 推出了多线程,可以在高并发情况下利用CPU多核多线程读写客户端数据,进一步提高Server性能。
只是针对读写是并行,每个命令的操作仍然是单线程。
只是针对读写是并行,每个命令的操作仍然是单线程。
随着文件变大,速度必然变慢
磁盘IO成为瓶颈
关系型数据库
建表时必须给出shcema
建表时必须定义类型
实质定义数据字节宽度
存的时候:倾向于行级存储
没有数据的地方会padding
查询的时候:B+树->索引page->数据page
通常B+树干存在于内存
实质:减少IO操作(读取的数据量)
数据库很大的时候,性能下降?
如果没有索引,增删改查变慢
查询速度?
一个和少量查询,速度基本不变
并发大,会受硬盘带宽影响查询速度。
和memchached
的比较和优势
的比较和优势
均为KV型数据库
存储方面
memcache只用到内存
Redis会有部分数据持久化
数据支持类型
memcached没有类型概念
Redis数据有类型,而且数据可以计算
底层模型
Redis直接构建了VM机制,数据可以直接计算
Value大小
memcache 1MB
Redis 可以达到 1GB
极端情况:如果client需要从
缓存中取回v中的某一个元素
缓存中取回v中的某一个元素
memcached需要返回
全量value数据到client
全量value数据到client
server端网卡IO压力
client端需要实现代码去解析value值,并操作
redis在server端对每一种类型
都有自己的方法,可以定位并
返回某一元素
都有自己的方法,可以定位并
返回某一元素
减少数据传输量
客户端不需要额外代码解析数据
Notes:Both在Value都可以存复杂的数据结构,例如JSON。
EPOLL的发展过程
Spring Boot的使用Redis缓存
RedisTemplate
Spring Cache集成Redis
核心注解
@Cachable
@CachePut
@CacheEvict
Redis管道(PIPE)
PUBSUB
SUBCRIBE必须先监听,然后才能看到PUB的消息
不能看到之前已经发布的消息
实时性
Redis事务
(transaction)
(transaction)
核心:Redis的目的是快,如果事务繁杂影响速度的时候,不应当使用Redis
操作
MULTI
DISCARD
EXEC
WATCH
UNWATCH
等到EXEC到达,再执行那一组指令集(序列化顺序执行)
Redis不支持回滚(roll-back)
Redis布隆过滤器
(Bloom Filter)
(Bloom Filter)
三种实现架构
解决缓存穿透问题
一般业务缓存流程:
即使数据不存在(穿透了,数据不存在),也需要创建一个缓存,
(增加Redis中的key/value标记),用来防止穿库。
如果数据库增加了元素,必须要完成元素对bloom过滤器的添加。
即使数据不存在(穿透了,数据不存在),也需要创建一个缓存,
(增加Redis中的key/value标记),用来防止穿库。
如果数据库增加了元素,必须要完成元素对bloom过滤器的添加。
Redis作为
数据库和缓存的区别
数据库和缓存的区别
缓存基本概念
缓存(相对)不重要
不是全量数据
存放热数据
key的有效期
业务逻辑推导
业务运转要求
内存有限,随着访问的变化,应该淘汰冷数据
key的有效期问题
key的有效期是否会随访问(get)延长? 不会
key发生写操作,是否会延长有效期?新的写操作会剔除过期事件
可以用倒计时策略,且Redis不能延长
可以定时
如何淘汰过期的keys
被动方式
当客户端尝试访问,key会被发现并主动的过期
主动方式
定时随机测试设置keys的过期时间,所有过期的keys会从key空间删除
具体每秒10次做
1. 测试随机的20个keys进行过期检测
2. 删除已经过期的keys
3. 如果有多远25%的keys过期,重复第一步
持久化策略
RDB
快照
快照
直接把内存中的数据定时保存到一个dump文件中
dump.rdb (参考配置文件)
dump.rdb (参考配置文件)
会丢失
工作原理
考虑两个问题
1. 速度
2. 内存空间够不够
当需要持久化时,Redis会fork一个子进程,子进程
写到一个临时的RDB文件。
完成写后,替换掉原来的RDB文件。
写到一个临时的RDB文件。
完成写后,替换掉原来的RDB文件。
Copy-on-Write
操作
save
非常明确的时候用save,例如:关机维护等
bgsave
fork方式
配置文件中制定bgsave规则,但用的是save标识符
默认模式开启
优缺点
时点和时点之间窗口数据会丢失
不支持拉链,只有一个dump.rdb
优点:类似Java中的序列号,恢复速度快
AOF
日志
日志
Log方式,将所有的修改命令都存到一个文件里
append only
写到appendonly.aof文件, 配置
开关:appendfsync yes
指不调用fsync,完全依赖kernel。
当Kernel Buffer满的时候,调用fsync将数据写到磁盘上
当Kernel Buffer满的时候,调用fsync将数据写到磁盘上
sync方式
appendfsync everysec ==> 默认方式
appendfsync always
每一个写操作,则调用一次fsync
Redis可以同时开启RDB和AOF
如果开启了AOF,只会用AOF恢复
>4.0之后,AOF中包含RDB全量,增加记录新的写操作
优势和问题
丢失的内容少于RDB
AOF文件体积大于RDB文件
包含冗余信息,例如:a=1->2->3->4,
AOF恢复的时候需要记录所有的a的变化过程
AOF恢复的时候需要记录所有的a的变化过程
弊端:导致恢复变慢
设计方案,让AOF足够小
设计方案,让AOF足够小
借鉴方案:在hdfs, fsimage + edits.log
让日志只记录增量数据
让日志只记录增量数据
4.0以前, 重写后
删除抵消的命令
合并重复的命令
删除抵消的命令
合并重复的命令
最终也是一个纯指令的日志文件
4.0以后,重写后
将老的数据RDB到AOF文件中
将增量的数据已指令的方式Append到AOF
将老的数据RDB到AOF文件中
将增量的数据已指令的方式Append到AOF
AOF是一个混合体
利用了RDB的速度快
利用了日志的全量优点
利用了RDB的速度快
利用了日志的全量优点
由于频繁写盘,速度可能会慢
三个策略级别
NO
AWAYS
PER SEC
tools/config
redis-check-rdb
bgsave
bgrewriteaof
清除没有意义的过程,加速恢复
/etc/redis/redis.conf or PID.conf
aof-use-rdb-preamble yes|no
appendfsync always
appendfsync everysec
appendfsync no
appendfsync everysec
appendfsync no
appendonly yes|no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
auto-aof-rewrite-min-size 64mb
Redis记录每一次rewrite后文件的大小
单机、单节点、单实例的问题
单点故障/Single Point of Failure
容量上限有限/Limited capacity
全量数据处理下的连接数压力,处理能力压力/Overstressed
主从复制
(leader follower/
master-slave)
(leader follower/
master-slave)
最终 “主=从”
复制的方案发展比较
主从和主备
默认异步复制,特点是低延时和高性能
命令
REPLICAOF MASTER PORT
<5.0 SLAVEOF
REPLICAOF NO ONE => Enabled as master node
sync [runId] [offset]
只支持全量复制
runId:每个节点的UUID
offset: 主从节点都各自维护自己的主从复制偏移量
offset=offset+命令的字节长度
offset=offset+命令的字节长度
repl_backlog_size:保存在主节点撒花姑娘的一个固定长度的FIFO队列,默认大小是 1MB
psync [runId] [offset]
支持全量和部分复制
主能知道有多少从连接
重要配置参数
增量备份:repl-backlog-size 1mb (根据业务性质调整队列大小)
如果队列太小(即不能找全offset的数据),Redis会自动触发全量RDB同步。
如果队列太小(即不能找全offset的数据),Redis会自动触发全量RDB同步。
replica-serve-stale-data yes
replica-read-only yes
replica-diskless-sync no
replica-read-only yes
replica-diskless-sync no
min-replicas-to-write 3
min-replicas-max-lag 10
min-replicas-max-lag 10
HA / Sentinel哨兵
哨兵的监控选举原则 ==> 减低误判(false positive)可能性
半数以上哨兵同意监控的主Redis宕机
当不是所有的哨兵都工作的情况下,哨兵仍然可以工作
功能
监控Monitoring
提醒Notification
自动故障迁移Automatic failover
配置提供方Confgiuration provider
会修改自己的配置文件
实现
所有哨兵都是利用master的PUBSUB发现其他哨兵
配置
启动: redis-sentinel
redis-server --sentinel
redis-server --sentinel
port XXXXX
sentinel monitor NameOfMaster HostIP Port Quota
sentinel monitor NameOfMaster HostIP Port Quota
分片
演进
Y轴分片
Hash取模分片
Random分片:应用于消息队列
一致性Hash算法(Redis Cluster实现)
Hash环的使用
多Redis实例连接模型演变
优秀代理软件
Twitter twemproxy
Predixy
Child Topic
缓存的常见问题
击穿
雪崩
穿透
一致性(双写)
Redis常见问题及方案
Redis做分布式锁
要点
利用setnx
设置锁过期时间
利用多线程,加入守护进程,负责延长锁时间
考虑redisson
考虑zookeeper
0 条评论
下一页