redis
2022-04-04 17:43:18 27 举报
AI智能生成
redis
作者其他创作
大纲/内容
持久化
RDB快照:snapshotting
将Redis内存中的数据保存到磁盘上,一般线上redis内存都是以G为单位的,所以该操作会影响Redis性能
save 900 1 900秒内发生一次
save 300 10 300秒内发生10次
save 60 10000 60秒内发生10000
dbfilename dump.rdb 快照名
dir ./ 快照路径
save 300 10 300秒内发生10次
save 60 10000 60秒内发生10000
dbfilename dump.rdb 快照名
dir ./ 快照路径
手动快照
登陆状态下:bgsave
未登录状态下:./redis-cli bgsave
save
会阻塞用户请求处理的性能
bgsave
后台fork一个进程,来做持久化操作,不会影响用户请求处理。注意:COW技术
恢复速度快,文件体积小,但是容易丢数据
AOF追加:appendonly
appendonly yes 开启
appendfilename "appendonly.aof" 文件名
# appendfsync always 每次操作都存储(最多只丢一条数据)
appendfsync everysec 每秒存储一次(最多丢一秒的数据)
# appendfsync no
appendfilename "appendonly.aof" 文件名
# appendfsync always 每次操作都存储(最多只丢一条数据)
appendfsync everysec 每秒存储一次(最多丢一秒的数据)
# appendfsync no
AOF中存储的是一条条命令,恢复时是重新执行这些命令
AOF重写
AOF文件里可能有太多没用指令,所以AOF会定期根据内存的最新数据生成aof文件
AOF重写频率
auto‐aof‐rewrite‐min‐size 64mb//aof文件至少要达到64M才会自动重写
auto‐aof‐rewrite‐percentage 100//aof文件自上一次重写后文件大小增长了100%则再次触发重写
恢复速度慢,文件体积大,但是可以根据策略决定数据安全性
Redis启动后,默认用AOF恢复数据,因为AOF数据更全
Redis4.0引入混合持久化
Redis混合持久化开关:aof-use-rdb-preamble yes
AOF重写时不在单纯地将内存数据转换为命令存储在AOF中,而是将这一刻之前的数据做RDB快照处理,
并且将RDB快照内容和增量的AOF修改数据命令存在一起,都写入新的AOF文件。
Redis 重启的时候,可以先加载 RDB 的内容,然后再重放增量 AOF 日志就可以完全替代之前的 AOF 全
量文件重放,因此重启效率大幅得到提升。
并且将RDB快照内容和增量的AOF修改数据命令存在一起,都写入新的AOF文件。
Redis 重启的时候,可以先加载 RDB 的内容,然后再重放增量 AOF 日志就可以完全替代之前的 AOF 全
量文件重放,因此重启效率大幅得到提升。
主从数据
http://doc.redisfans.com/topic/replication.html
在从服务器中配置
slaveof 192.168.1.1 6379
masterauth <password>
slaveof 192.168.1.1 6379
masterauth <password>
配置
配置主服务器地址:slaveof 192.168.1.1 6379
从服务器默认只读,设置从服务器只读:slave-read-only yes
如果主服务器设置了密码,那么需要配置密码来访问:masterauth <password>
从服务器至少达到N个才让从服务器执行写操作:min-slaves-to-write N
从服务器延迟低于M秒才向从服务器发送写命令:min-slaves-max-lag M
命令
设置主服务器地址:SLAVEOF 192.168.1.1 10086
设置只读: CONFIG SET
设置主服务器访问密码:config set masterauth <password>
原理和流程
全量复制
子主题
部分复制
子主题
安全
redis默认没有密码,可以通过requirepass设置密码
设置redis.conf文件中的requirepass pass 设置密码
获取密码:CONFIG get requirepass
设置密码:CONFIG set requirepass pass
redis设置密码时
redis可以连接,但是不能操作需要输入密码
AUTH password
连接时输入密码
./redis-cli 127.0.0.1 -p 6379 -a pass
发布/订阅
publish
发布
PUBLISH channel message
subscribe
订阅
SUBSCRIBE channel [channel ...]
unsubscribe
取消订阅
pubsub
活跃频道指的是那些至少有一个订阅者的频道
psubscribe
订阅一个或多个符合给定模式的频道,支持通配符
SUBSCRIBE channel.*
基本数据类型命令
string
基本命令
特性
一个key中最大存储512M的数据
查询/赋值
get/set
mget/mset
多个key操作
mset name caijian sex man age 23
mget name sex age
getrange
获取指定下标范围内的字符串
getrange name 3 6
setrange
设置指定下表开始替换字符,有多长替换多长
setrange name 3 jian
setex
相当于set + expire命令的组合
setex name 600 ciajian
incr
将 key 中储存的数字值增 1,如果 key 不存在,那么 key 的值会先被初始化为 0 ,
然后再执行 INCR 操作,字符串类型的值不能表示为数字,那么返回一个错误
然后再执行 INCR 操作,字符串类型的值不能表示为数字,那么返回一个错误
incr nums
incrby
原理同incr,但是可以指定一次增加的数量
incrby nums 10
decr
自减 1
decr nums
decrby
自减 指定数
decrby nums 10
append
追加字符串
append name caijian
strlen
字符串长度
二进制
getbit
获取二进制中offset这个位置的“0,1”状态
get num1 0
setbit
设置二进制中offset这个位置的“0,1”状态
set num1 7 1
bitcount
获取二进制中1的个数
bitcount num1
bitop
二进制位的操作(^ & |),对一个或多个保存二进制位的字符串
key 进行位元操作,并将结果保存到 destkey 上
key 进行位元操作,并将结果保存到 destkey 上
BITOP AND destkey key [key ...]
BITOP OR destkey key [key ...]
BITOP XOR destkey key [key ...]
BITOP NOT destkey key
SDS:Simple Dynamic String
redis中用SDS表示字符串
二进制安全的数据结构:C语言字符串以/0结尾,不能兼容其他语言的字符串类型
内存预分配机制,避免频繁的内存分配
String的三种存储结构
int
存储8个字节的长整形
raw
存储大于44个字节的字符串
raw需要分配两次内存空间
embstr
存储小于等于44个字节的字符串
只分配一次内存空间
embstr是只读的,如果修改value后就变成raw了
如果int数据不再是整数,会从int类型变成raw
hash
存储结构
ziplist
ziplist是一个双向链表,但是不存preview和next指针,存的是上一个节点的长度和当前结点的长度
当hash中存储的field小于512个的时候,这时候用ziplist
hash中所有的field和value的字符串长度都小于64byte
hash table
数组加链表
应用场景
购物车
List
基本命令
BLPOP key[key ...]
删除,并获取第一个元素
BRPOP key[key ...]
删除,并获取最后一个元素
BRPOPLPUSH source destination
弹出一个列表值,将他插入到另一个列表中
LINDEX key index
通过索引获取一个元素
LINSERT key BEFORE|AFTER pivot value
在列表中的另一个元素之前或之后插入元素
LLEN key
获取队列长度
LPOP key
从队列左边出队一个元素
LPUSH key value[value]
从队列的左边入队一个或多个元素
LPUSHX key value
当队列存在时,从队左边入队一个元素
LRANGE key start stop
获取指定的元素
LREM key count value
从列表中删除元素
LSET key index value
根据下标设置队列元素值
LTRIM key start stop
子主题
RPOP key
从队列右边出队一个元素
RPOPLPUSH source destination
删除队列最后一个元素,最加到另一个队列
RPUSH key value[value ...]
从队列右边入队一个元素
RPUSHX key value
从队列右边入队一个元素,仅队列存在时有效
存储结构
quick list+zip list
应用场景
文章列表
消息列表
评论列表
set
基本命令
SADD key member[member]
添加一个或多个元素到集合中
SCARD key
获取集合中元素数量
SUBTER key [key]
获取集合交集
SINTERSTORE destination key [key ...]
获取集合交集并存储在一个结果集上
SMEMBERS key
获取集合中所有元素
SMOVE source destination member
移动集合中的一个元素到另一个集合
SPOP key [count]
删除集合里的元素
SRANDMEMBER key[count]
随机获取一个元素
SREM key member [member]
集合里删除一个或多个元素
SUNION key[key...]
添加多个set元素
SUNIONSTORE DESTINATION KEY[KEY ...]
合并set元素,并将结果存入新的set
存储结构
intset
hashtable
应用场景
抽奖
spop
对账
sinter
sdiff
社交关注、点赞
sadd
srem
sismember
smembers
去重
SortedSet
存储结构
skip list
应用场景
热搜排行
zincrby
zrevrange
Redis分布式锁
缓存击穿(只穿透了缓存)
批量设置缓存时,增加随机的缓存过期时间,避免在同一时刻点大批量缓存失效
缓存穿透(缓存和DB都击穿了)
缓存空值,并设置过期时间,增加缓存读延期
布隆过滤器
缓存雪崩
缓存高可用架构,多级缓存,包括JVM级的缓存
限流组件
突发性热点缓存重建
突发性热点缓存重建,导致数据库压力暴增
通过分布式锁(读写锁,确保读读不互斥),确保只有一个线程来重建缓存
使用双重检测机制确保查询尽量走缓存
redis和DB的双写一致性问题
本质问题是从数据库取值到设置redis这个过程不是原子操作,
所以要对DB和redis的操作加分布式锁
所以要对DB和redis的操作加分布式锁
分布式锁性能优化方案
锁粒度:分段锁
读写锁
分布式环境下加分布式锁的问题
主从切换锁丢失
master上加锁成功,在同步给slaver时,master挂了,
导致其他线程可以在slaver上在加相同的锁
导致其他线程可以在slaver上在加相同的锁
Red Lock,加锁时和Zk类似,需要过半加锁成功才算加锁成功
子主题
red lock下添加从结点的时候,可能发生主节点崩溃,所以red lock也不能彻底解决这个问题
如果设置多个从结点的话,会影响redis加锁性能
和zk分布式锁的区别
redis的分布式锁是基于ap的,只要主节点加锁成功,则返回加锁成功
zk的分布式锁是基于cp的,需要过半提交
redisson源码分析
https://mp.weixin.qq.com/s/SRsbscvQo9Y2H0IUM0Sx6Q
热点数据冷热分离
热点数据尽量常驻在缓存中
缓存设置过期时间
缓存命中增加读延期
布隆过滤器
底层是一个bitmap二进制数组和几个无偏hash函数(元素的hash值均匀分布)
布隆过滤器说某个值存在时,这个值可能不存在;当它说某个值不存在时,那就肯定不存在
影响因素
布隆过滤器误差率
布隆过滤器元素个数(布隆过滤器长度)
布隆过滤器不能删除数据,如果要删除,就得重新初始化
简介
历史发展
高性能键值对数据库
特点
共享内存[专门的内存空间]
持久化
特性
存储结构
字符串(String)
哈希/散列/字典(Hash)
列表(List)
集合(Set)
有序集合(sorted set)
内存存储与持久化
redis数据存储在内存中,速度比硬盘快,在程序退出时可将数据持久化到硬盘中防止丢失
功能丰富
数据库(database)
缓存(cache)
消息中间件(message broker)
简单稳定
语法简单
稳定
官方文档:http://doc.redisfans.com/
KEY
keys
模式匹配
keys n?me
keys n*me
keys n[]me
keys n\?me
redis中key全部是以String类型来处理
exists
判断指定的key是否存在,存在返回 1 不存在返回 0
exists name
del
删除key,返回删除的数量
del name
type
查看key类型
type name
randomkey
返回一个随机key
randomkey
expire
设置key过期时间
expire name 60
ttl
查看key剩余过期时间,.不存在时,返回 -2; key 存在但没有
设置剩余生存时间时,返回 -1;以秒为单位,返回 key 的剩余生存时间
设置剩余生存时间时,返回 -1;以秒为单位,返回 key 的剩余生存时间
ttl name
expireat
以某个时间点为过期时间,-毫秒,expireat 时间戳;
expireat 140090898092890
sort
排序
数字
sort name
sort name desc
字符串
sort name alpha
sort name alpha desc
server
flushall
强制清空所有数据库中的所有key
flushdb
清空当前数据库中的所有 key
CLIENT LIST
所有连接到服务器的客户端信息和统计数据
CLIENT KILL
关闭地址为 ip:port 的客户端
CLIENT KILL 127.0.0.1:43501
PING
向 Redis 服务器发送一个 PING ,如果服务器运作正常的话,会返回一个 PONG
ECHO
打印一个特定的信息 message ,测试时使用
QUIT
请求服务器关闭与当前客户端的连接
Transaction(事务)
multi
事务开始
exec
事务结束
discard
取消事务,放弃执行事务块内的所有命令
watch
监视key变化,发生变化时会取消事务
unwatch
取消 WATCH 命令对所有 key 的监视
哨兵 sentinel
sentinel用于监视master是否正常在线,如果master挂掉了,sentinel会在slave中选举出一个master顶上去
开3个Sentinel或者说开奇数个,Sentinel的原理就是一直监视着master是否在线,
如果挂掉,sentinel的集群会选举中一个领头的sentinel,然后由领头的sentinel
执行slaveof命令,让slave作为master,然后sentinel集群继续监视着新的master,
老的master还是会一直监控着,如果启动了,那么老的master会作为新master的slave。
如果挂掉,sentinel的集群会选举中一个领头的sentinel,然后由领头的sentinel
执行slaveof命令,让slave作为master,然后sentinel集群继续监视着新的master,
老的master还是会一直监控着,如果启动了,那么老的master会作为新master的slave。
sentinel判断master下线的两种标准
主观下线:sentinel发现master没有在指定时间返回信息,这种情况被认为主线下线。
客观下线:sentinel询问sentinel的集群,看一下其他的sentinel是否也标记这master是否下线,如果标记sentinel下线的个数达到一个阀值,
sentinel会将master标记为客观下线,这个时候能会选取领头的sentinel。由领头的sentinel选取master下面的一个slave去作为新的master。
sentinel会将master标记为客观下线,这个时候能会选取领头的sentinel。由领头的sentinel选取master下面的一个slave去作为新的master。
配置
你要监视的主服务器的ip地址,以及sentinel标记客观下线的阀值
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel认为“主线下线”的一个阀值
sentinel down-after-milliseconds mymaster 3000
故障恢复期间可以同时slave的个数
sentinel parallel-syncs mymaster 1
故障恢复允许的最大时长
sentinel failover-timeout mymaster 180000
默认会有一个实例ID,注释掉每次启动会自动分配sentinel myid
如果redis设置了密码需要配置密码,不支持主从密码不一致:sentinel auth-pass mymaster pass
集群cluster
简介
redis集群是一个由多个主从节点群组成的分布式服务器群,它具有复制、高可用和分片特性。
Redis Cluster 将所有数据划分为 16384 个 slots(槽位),每个节点负责其中一部分槽位,槽位的信息存储于每个节点中。
Cluster 默认会对 key 值使用 crc16 算法进行 hash 得到一个整数值,然后用这个整数值对 16384 进行取模来得到具体槽位。
HASH_SLOT = CRC16(key) mod 16384
HASH_SLOT = CRC16(key) mod 16384
当客户端向一个错误的节点发出了指令,该节点会发现指令的 key 所在的槽位并不归自己管理,这时它会向客户端发送一个特殊的跳转指令携带目标操作的节点地址,告诉客户端去连这个节点去获取数据。客户端收到指令后除了跳转到正确的节点上去操作,还会同步更新纠正本地的槽位映射表缓存,后续所有 key 将使用新的槽位映射表。
集群中的每个节点都有 1 个至 N 个replica, 其中一个结点为主节点(master), 而其余的 N-1 个结点为从节点(slave),主节点宕机后其他服务器会选举出其中一个slave为新的主节点,否正整个集群将停止运作,因为没有对应的服务器承接相应的哈希槽
Redis集群选举原理
1.slave发现自己的master变为FAIL
2.将自己记录的集群currentEpoch加1,并广播FAILOVER_AUTH_REQUEST 信息
3.其他节点收到该信息,只有master响应,判断请求者的合法性,并发送FAILOVER_AUTH_ACK,对每一个epoch只发送一次ack
4.尝试failover的slave收集master返回的FAILOVER_AUTH_ACK
5.slave收到超过半数master的ack后变成新Master
从节点并不是在主节点一进入 FAIL 状态就马上尝试发起选举,而是有一定延迟
•延迟计算公式:
DELAY = 500ms + random(0 ~ 500ms) + SLAVE_RANK * 1000ms
•SLAVE_RANK表示此slave已经从master复制数据的总量的rank。Rank越小代表已复制的数据越新。
这种方式下,持有最新数据的slave将会首先发起选举
•延迟计算公式:
DELAY = 500ms + random(0 ~ 500ms) + SLAVE_RANK * 1000ms
•SLAVE_RANK表示此slave已经从master复制数据的总量的rank。Rank越小代表已复制的数据越新。
这种方式下,持有最新数据的slave将会首先发起选举
Redis 集群不保证数据的强一致性
创建集群
环境
需要执行redis-trib.rb,必须要有ruby环境
需要redis-*.gem
需要redis-*.gem
集群启动必须保证redis为空,清除appendonly.aof,dump.rdb,和 cluster-config-file配置的文件
配置
daemonize yes
如果有密码需要配置密码
vi /usr/local/rvm/gems/ruby-2.4.4/gems/redis-4.0.1/lib/redis/client.rb
:password => "root",
redis全部启动后,启动集群
./redis-trib.rb create --replicas 1 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 127.0.0.1:7006
过期键清除策略
被动删除
当读/写一个已经过期的key时,会触发惰性删除策略,直接删除掉这个过期的key
优点:避免主动删除导致redis有卡顿
缺点:如果key不在被访问,会一直占内存,相当于过期失效
主动删除
Redis定期主动淘汰一批已过期的key
针对设置了过期时间的key做处理
volatile-ttl
针对设置了过期时间的键值对,根据过期时间的先后进行删除,越早过期的越先被删除
volatile-random
对设置了过期时间的键值对中,进行随机删除
volatile-lru
使用LRU算法筛选设置了过期时间的键值对删除
volatile-lfu
使用LFU算法筛选设置了过期时间的键值对删除
针对所有的key做处理
allkeys-random
从所有键值对中进行随机选择并删除数据
allkeys-lru
使用LRU对所有数据进行筛选删除
allkeys-lfu
使用LFU对所有数据进行筛选删除
LRU
淘汰很久没有被访问过的数据,以最近一次访问时间作为参考
LFU
淘汰最近一段时间被访问次数最少的数据,以访问次数作为参考
热点数据适合用LFU
当内存超过maxMemory(在redis.conf里配置的)限定时,触发主动删除策略
noeviction
不会删除任何数据(不做任何处理,直接报错)
redis连接池
关键参数
maxTotal
redis连接池中最大连接数,不能超过maxTotal
节点数 * maxTotal < redis最大连接数maxClients
maxIdle:最大空闲连接数
业务需要的最大连接数
如果超过了maxIdle,当超过的连接执行完业务后会慢慢被连接池释放掉
maxIdle不要设置的太小,否则会有new 连接的开销,建议maxIdle=maxTotal
minIdle:最小空闲连接数
至少要保持的空闲连接数,如果超过了minIdle,那么继续建立连接
连接池预热
List<Jedis> minIdleJedisList = new ArrayList<Jedis>(jedisPoolConfig.getMinIdle());
for (int i = 0; i < jedisPoolConfig.getMinIdle(); i++) {
Jedis jedis = null;
try {
jedis = pool.getResource();
minIdleJedisList.add(jedis);
jedis.ping();
} catch (Exception e) {
logger.error(e.getMessage(), e);
} finally {
//注意,这里不能马上close将连接还回连接池,否则最后连接池里只会建立1个连接。。
//jedis.close();
}
}
//统一将预热的连接还回连接池
for (int i = 0; i < jedisPoolConfig.getMinIdle(); i++) {
Jedis jedis = null;
try{
jedis = minIdleJedisList.get(i);
//将连接归还回连接池
jedis.close();
} catch (Exception e) {
logger.error(e.getMessage(), e);
} finally {
}
0 条评论
下一页