缓存数据库redis

Redis

特性

1.速度快 (基于内存)

10w OPS

单线程 (非阻塞IO)

2.持久化

Redis所有数据保持在内存中,对数据的更新将异步地保存到磁盘上。

方式RDB,AOF

3.多数据结构

string list hash set zset

bitmap 位图(0,1)

HyperLogLog 超小内存唯一值计数

GEO:地理信息定位

4.支持多语言

java php ruby nodejs …

5.功能丰富

发布订阅 Lua脚本 事务 pipeline

6.主从复复制

7.高可用 分布式

Redis典型应用场景

缓存系统

排行榜
计数器
社交网络
消息队列系统
实时系统

安装

1
2
3
4
5
6
# linux
wget http://download.redis.io/releases/redis-3.0.7.tar.gz
tar -xzf redis-3.0.7.tar.gz
ln -s redis-3.0.7 redis
cd redis
make && make install

连接

1
redis-cli -h 10.10.79.150 -p 6384

命令

keys [pattern]

dbszie

exists key

del key

expire key second

type key

ttl key (-2 key不存,-1存在但没有设置时间)

persist key

数据结构

string

上限512M

缓存 计数 分布式锁

get/set/del/incr/decr/incrby/decrby

记录网站每个用户个人主页的访问量?

incr userid:pageview(单线程∶无竞争)

缓存视频的基本信息(数据源在MySQL中)伪代码

1
2
3
4
5
6
7
8
9
10
11
public VideoInfo get(long id) {
String redisKey = redisPrefix + id;
VideoInfo videoInfo = redis.get(redisKey);
if (videoInfo == null) {
videoInfo = mysql.get(id);
if (videoInfo != null){
//序列化
redis.set(redisKey, serialize(videoInfo));}
}
return videoInfo;
}

分布式id生成器

incr id(原子操作)

set

setnx 不存在,才设置

setxx 存在,才设置

mget/mset 多值设置

getset key newvaule 设置新值返回旧值

append key value 追加到旧值

strlen key

getrange from to

hash

hget key field

hset key field val

hdel key field

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
127.0.0.1:6379> hset user:1:info age 23
(integer) 1
127.0.0.1:6379> hget user:1:info age"23"
127.0.0.1:6379>hset user:1:info name ronaldo
(integer) 1
127.0.0.1:6379> hgetall user:1:info1)
"age"
"23"
"name"
"ronaldo"
127.0.0.1:6379> hdel user:1:info age
(integer) 1
127.0.0.1:6379> hgetall user:1:info1)
"name"
"ronaldo"

hexist key field

hlen key

hmget key field1,field2….

hmset key field1 value1 field2 value2…fieldN valueN

hgetall key

hvals key 返回key所有val

hkeys 返回key所有filed

记录网站每个用户个人主页的访问量?

hincrby user:1:info pageview count

list

有序 重复 左右传输

rpush key val1 val2….

lpush

linsert key before|after value newvalue 在value 前|后 插入新值newvalue

lpop /rpop

lrem key count value
#根据count值,从列表中删除所有value相等的项

(1) count>0,从左到右,删除最多count个value相等的项
(2) count<0,从右到左,删除最多Math.abs(count)个value相等的项

(3) count=O,删除所有value相等的项

ltrim key start end

lrange key start end

lset key index newValue
#设置列表指定索引值为newValue

blpop key timeout
#lpop阻塞版本, timeout是阻塞超时时间,timeout=0为永远不阻塞

brpop key timeout

set

无序 不重复

sadd key element 。。。

srem key element 。。。

scard 集合大小

sismember key element 是否存在

srandmember 随机

smembers 所有

spop

抽奖系统 /tag标签

sdiff key1 key2 差集

sinter key1 key2 交集

sunion key1 key2 并集

共同关注好友、喜欢

SADD = Tagging
SPOP/SRANDMEMBER = Random item
SADD + SINTER = Social Graph

zset

有序集合

key score val
user:ranking 1 lisi
44 wuli
50 sss

zadd key score element (nlogn)

zrem key element …

zscore key element

zincrby key increscore element 增分

zcard key 个数

zrange key start end [withscore]

zrangebyscore key minScore maxScore[WITHSCORES]
#返回指定分数范围内的升序元素[分值]

zcount key minScore maxScore
#返回有序集合内在指定分数范围内的个数

zremrangebyrank key start end

#删除指定排名内的升序元素

zremrangebyscore key minScore maxScore

#删除指定分数内的升序元素

zrevrank
zrevrange
zrevrangebyscore
zinterstore
zunionstore

排行榜单 score: timeStamp、 saleCount、 followCount

Jedis/Jedispool

redis连接工具

maxTotal
资源池最大连接数
maxIdle
资源池允许最大空闲连接数
minIdle
资源池确保最少空闲连接数
jmxEnabled
是否开启jmx监控,可用于监控

blockWhenEx当资源池用尽后,调用者是否要等truehausted
待。只有当为true时,下面的maxWaitMillis才会生效

maxWaitMillis当资源池连接用尽后,调用者的最大等待时间(单位为毫秒)
-1:表示永不超时

testOnBorrow
向资源池借用连接时是否做连接有 效性检测(ping),无效连接会被移除

testOnReturn

向资源池归还连接时是否做连接有效性检测(ping),无效连接会被移除

比较难确定的,举个例子∶
1.命令平均执行时间0.1ms = 0.001s。

2.业务需要50000 QPS。

3.maxTotal理论值= 0.001* 50000 = 50个。实际值要偏大一些。

建议maxIdle = maxTotal (减少创建新连接的开销。)

建议预热minIdle ( 减少第一次启动后的新连接开销。)

解决方案
1.慢查询阻塞:池子连接都被hang住。
2.资源池参数不合理∶例如QPS高、池子小。
3.连接泄露(没有close()):此类问题比较难定位,例如client list、netstat等,最重要的是代码。

慢查询

1.client发生命令

2.redis 命令排队(队列)

3.执行命令

4.返回结果给client

说明:

(1)慢查询发生在第3阶段

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

配置:

slowlog-max-len 队列最大长度 默认128

slowlog-log-slower-than 默认10000

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

  1. slowlog-log-slower-than=0,记录所有命令
  2. slowlog-log-slower-than<0,不记录任何命令。
1
2
3
config set slowlog-max-len 1000
config set slowlog-log-slower-than 1000

slowlog get [n]:获取慢查询队列

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

slowlog reset:清空慢查询队列

slowlog-log-slower-than不要设置过大,默认10ms,通常设置1ms

slowlog-max-len不要设置过小,通常设置1000左右。

pipeline

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

命令 N个命令 1次1次pipeline(n条命令)
时间 n次网络时间+n次命令时间 1次网络时间+n次命令时间
数据量 1条命令 n条命令

1.Redis的命令时间是微秒级别。

  1. pipeline每次条数要控制(网络)。

注意每次pipeline携带数据量

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

M操作与pipeline区别

发布订阅

publish channel msg

subscribe [channel] # ─个或多个

unsubscribe [channel] # ─个或多个

psubscribe [pattern…]#订阅模式。
punsubscribe [pattern…]#退订指定的模式。
pubsub channels #列出至少有一个订阅者的频道。
pubsub numsub [channel…] #列出给定频道的订阅者数量

Bitmap

setbit key offset val

getbit key offset

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

bitop op destkey key [key…]
做多个Bitmap的and(交集)、or(并集)、not(非)、xor(异或)操作并将结果保存在destkey中

bitpos key targetBit [start] [end]
计算位图指定范围(start到end,单位为字节,如果不指定就是获取全部)第一个偏移量对应的值等于targetBit的位置

用户在线人数统计等。

hyperloglog

pfadd key element [element …]:向hyperloglog添加元素
pfcount key [key …]:计算hyperloglog的独立总数
pfmerge destkey sourcekey [sourcekey …]:合并多个hyperloglog

geo

redis 3.2+

geo key longitude latitude member[longitude latitude member …]
#增加地理位置信息

如:geoadd cities:locations 116.28 39.55 beijing

geopos key member [member …]#获取地理位置信息

geodist key member1 member2 [unit]

#获取两个地理位置的距离
#unit: m(米)、km(千米)、mi(英里)、ft(尺)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
georadius key longitude latitude radiusm|kmlft|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:指定返回结果的数量。
ascldesc :返回结果按照距离中心节点的距离做升序或者降序。store key :将返回结果的地理位置信息保存到指定键。
storedist key :将返回结果距离中心节点的距离保存到指定键

如:距离北京150km
georadiusbymember cities:locations beijing 150 km


持久化

RDB:

1.命令方式

save (同步)(O(n)) 会阻塞客户端命令

bgsave(异步) fork函数 由子进程生成RDB文件 需要fork,消耗内存

2.配置文件redis.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
save 900 1

save 300 10

save 60 10000

dbfilename dump.rdb

dir ./

stop-writes-on-bgsave-error yes 错误停止写入

yesrdbcompression yes 压缩

rdbchecksum yes 校验和

触发机制-不容忽略方式
1.全量复制

  1. debug reload
  2. shutdown

RDB是Redis内存到硬盘的快照,用于持久化。

save通常会阻塞Redis。
bgsave不会阻塞Redis,但是会fork新进程。

save自动配置满足任一就会被执行。

有些触发机制不容忽视

AOF:

always 不丢失数据,IO开销大

everysec 每秒一闪fsync ,丢1秒数据

no 不可控

AOF重写:命令优化,删除重复,过期数据优化

1.bgrewriteaof fork子进程 内存中 AOF重写

2.配置方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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




1
2
3
4
5
6
7
8
9
10
11
12
 appendonly yes

appendonly yes
appendfilename "appendonly-${port}.aof"
appendfsync everysec
dir /bigdiskpath
no-appendfsync-on-rewrite yes
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

aof-load-truncated yes

RDB AOF
启动优先级
体积
恢复
轻重

fork 操作

改善fork
优先使用物理机或者高效支持fork操作的虚拟化技术

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

合理配置Linux内存分配策略: vm.overcommit_memory=1

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

主从复制

一主一从

一主多从

1.一个master可以有多个slave
2.一个slave只能有一个master
3.数据流向是单向的,master到slave

slaveof 主ip

slaveof no one 取消

配置方式

slaveof ip port
slave-read-only yes

全量复制开销

  1. bgsave时间
    2.RDB文件网络传输时间3.从节点清空数据时间
    4.从节点加载RDB的时间
    5.可能的AOF重写时间

Master挂掉 :【手动故障转移】选slave作为master

读写分离
1.读写分离:读流量分摊到从节点。
2.可能遇到问题:
·复制数据延迟
·读到过期数据
·从节点故障

配置不一致
例如maxmemory不一致:丢失数据

规避全量复制
1.第一次全量复制
·第一次不可避免·小主节点、低峰

2.节点运行ID不匹配。

主节点重启

(运行ID变化)·故障转移,例如哨兵或集群

3.复制积压缓冲区不足。

网络中断,部分复制无法满足
。增大复制缓冲区配置rel_backlog_size,网络“增强”。

1.单主节点复制风暴︰
问题:主节点重启,多从节点复制解决︰更换复制拓扑
2.单机器复制风暴
如右图∶机器宕机后,大量全量复制主节点分散多机器

redis sentinel

默认端口 26379

故障转移:

1.多个sentinel发现并确认master有问题。

⒉选举出一个sentinel作为领导。
3.选出一个slave作为master。
4.通知其余slave成为新的master的slave。

5.通知客户端主从变化
6.等待老的master复活成为新master的slave。

1
2
3
4
5
6
7
8
9
10
11
12
#sentinel配置
port ${port}
dir "/opt/soft/redis/data/"logfile"${port}.log"
sentinel monitor mymaster 127.0.0.1 7000 2 #2 表2个sentinel认为master有问题,才故障转移
sentinel down-after-milliseconds mymaster 30000 #毫秒
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000

#启动命令
dis-sentinel redis-sent-26379.conf


客户端接入流程
1.Sentinel地址集合

  1. masterName

  2. 不是代理模式

    1
    2
    JedisSentinelPool sentinelPool = new JedisSentinelPool(masterName, sentinelSet, poolConfig, timeout);

    三个定时任务
    1.每10秒每个sentinel对master和slave执行info
    发现slave节点
    确认主从关系
    2.每2秒每个sentinel通过master节点的channel交换信息(pub/sub)·

    通过_sentinel_:hello频道交互
    。交互对节点的“看法”和自身信息
    3.每1秒每个sentinel对其他sentinel和redis执行ping

    ·心跳检测,失败判定依据

1
2
3
4
5
6
7
8
9
sentinel monitor <masterName> <ip><port> <quorum>
sentinel monitor myMaster 127.0.0.1 6379 2
sentinel down-after-milliseconds <masterName> <timeout>
sentinel down-after-milliseconds mymaster 30000

#主观下线:每个sentinel节点对Redis节点失败的'偏见”
#客观下线:所有sentinel节点对Redis节点失败“达成共识”(超过quorum个统一)


领导者选举
原因:只有一个sentinel节点完成故障转移

选举:通过sentinel is-master-down-by-addr命令都希望成为领导者
·
1.每个做主观下线的Sentinel节点向其他Sentinel节点发送命令,要求将它设置为领导者。

  1. 收到命令的Sentinel节点如果没有同意通过其他Sentinel节点发送的命令,那么将同意该请求,否则拒绝

  2. 如果该Sentinel节点发现自己的票数已经超过Sentinel集合半数且超过quorum,那么它将成为领导者。

4.如果此过程有多个Sentinel节点成为了领导者,那么将等待一段时间重新进行选举。

故障转移( sentinel领导者节点完成)
1.从slave节点中选出一个“合适的”节点作为新的master节点
2.对上面的slave节点执行slaveof no one命令让其成为master节点。
3.向剩余的slave节点发送命令,让它们成为新master节点的slave节点,复制规则和parallel-syncs参数有关。
4.更新对原来master节点配置为slave,并保持着对其“关注“,当其恢复后命令它去复制新的master节点。

选择“合适的”slave节点
1选择slave-priority(slave节点优先级)最高的slave节点,如果存在则返回,不存在则继续。
2选择复制偏移量最大的slave节点(复制的最完整),如果存在则返回,不存在则继续。
3选择runId最小的slave节点。

redis cluster

节点取余
客户端分片∶哈希+取余
节点伸缩︰数据节点关系变化,导致数据迁移
迁移数量和添加节点数量有关∶建议翻倍扩容

一致性哈希
客户端分片∶哈希+顺时针(优化取余)
节点伸缩∶只影响邻近节点,但是还是有数据迁移
翻倍伸缩∶保证最小迁移数据和负载均衡

虚拟槽分区
预设虚拟槽:每个槽映射一个数据子集,一般比节点数大
良好的哈希函数∶例如CRC16
服务端管理节点、槽、数据︰例如Redis Cluster

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
#配置开启Redis
port ${port}
daemonize yes
dir "lopt/redis/redis/data/"
dbfilename "dump-${port}.rdb"
logfile "${port}.log"
cluster-enabled yes
cluster-config-file nodes-${port}.conf

#meet
cluster meet ip port

redis-cli -h 127.0.0.1 -p 7000 cluster meet 127.0.0.1 7001
redis-cli -h 127.0.0.1 -p 7000 cluster meet 127.0.0.1 7002
redis-cli -h 127.0.0.1 -p 7000 cluster meet 127.0.0.1 7003
redis-cli -h 127.0.0.1-p 7000 cluster meet 127.0.0.1 7004
redis-cli -h 127.0.0.1-p 7000 cluster meet 127.0.0.1 7005

#Cluster节点主要配置
cluster-enabled yes
cluster-node-timeout 15000
cluster-config-file "nodes.conf"
cluster-require-full-coverage no

#分配槽
cluster addslots slot [slot ...]
redis-cli -h 127.0.0.1 -p 7000 cluster addslots {0..5461}
redis-cli -h 127.0.0.1 -p 7001 cluster addslots {5462...10922}
redis-cli -h 127.0.0.1 -p 7002 cluster addslots {10923...16383}


#设置主从
cluster replicate node-id
redis-cli -h 127.0.0.1 -p 7003
cluster replicate ${node-id-7000}




moved和ask
两者都是客户单重定向
moved:槽已经确定迁移
ask:槽还在迁移中

jediscluster

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//JedisCluster基本使用
Set<HostAndPort> nodeList = new HashSet<HostAndPort>();
nodeList.add(new HostAndPort(HOST1, PORT1));
nodeList.add(new HostAndPort(HOST2,PORT2));
nodeList.add(new HostAndPort(HOST3, PORT3));
nodeList.add(new HostAndPort(HOST4, PORT4));
nodeList.add(new HostAndPort(HOST5, PORT5));
nodeList.add(new HostAndPort(HOST6, PORT6));
JedisCluster redisCluster = new JedisCluster(nodeList, timeout, poolConfig////);
redisCluster.command...

// 多节点命令实现

//获取所有节点的JeidsPool
Map<String, JedisPool> jedisPoolMap = jedisCluster.getClusterNodes();
for (Entry<String, JedisPool> entry : jedisPoolMap.entrySet()){
//获取每个节点的Jedis连接
Jedis jedis = entry.getValue().getResource();/只删除主节点数据
if (!isMaster(jedis)) {continue;
}
}


集群完整性:

cluster-require-full-coverage默认为yes
集群中16384个槽全部可用∶保证集群完整性
节点故障或者正在故障转移︰
(error) CLUSTERDOWN The cluster is down
大多数业务无法容忍,cluster-require-full-coverage建议设置为no

数据倾斜
节点和槽分配不均
不同槽对应键值数量差异较大
包含bigkey
内存相关配置不一致

请求倾斜
热点key:重要的key或者bigkey优化:
-避免bigkey
-热键不要用hash_tag
-当一致性不高时,可以用本地缓存+MQ

集群读写分离
只读连接︰集群模式的从节点不接受任何读写请求。
-重定向到负责槽的主节点

-readonly命令可以读:连接级别命令

离线/在线迁移
官方迁移工具:redis-trib.rb import

-只能从单机迁移到集群
-不支持在线迁移: source需要停写-不支持断点续传
-单线程迁移∶影响速度
在线迁移∶
-唯品会redis-migrate-tool

-豌豆荚:redis-port

思考-分布式Redis不一定好
.
1.Redis Cluster:满足容量和性能的扩展性,很多业务”不需要
大多数时客户端性能会”降低”。
命令无法跨节点使用: mget、keys、scan、flush、sinter等。

Lua和事务无法跨节点使用。
客户端维护更复杂:SDK和应用本身消耗(例如更多的连接池)。

2.很多场景Redis Sentinel已经足够好。

缓存使用和优化

缓存更新策略
1.LRU/LFU/FIFO算法剔除︰例如maxmemory-policy。
2.超时剔除∶例如expire。
3.主动更新:开发控制生命周期

两条建议
1.低一致性:最大内存和淘汰策略
2.高一致性:超时剔除和主动更新结合,最大内存和淘汰策略兜底。

缓存粒度控制-三个角度
1.通用性:全量属性更好。
2.占用空间︰部分属性更好。
3.代码维护:表面上全量属性更好。

缓存穿透

原因
1.业务代码自身问题
2.恶意攻击、爬虫等等

如何发现
1.业务的相应时间
2.业务本身问题
3.相关指标∶总调用数、缓存层命中数、存储层命中数

解决方案:

1.缓存空对象,并设置过期时间

两个问题
i.需要更多的键。
ii.缓存层和存储层数据“短期”不一致。

2.布隆过滤器拦截

问题
现有50亿个电话号码,现有10万个电话号码,要快速准确判断这些电话号码是否已经存在?
.通过数据库查询:实现快速有点难。
.数据预放在集合中:50亿*8字节~ 40GB(内存浪费或不够)

类似问题很多
垃圾邮件过滤
文字处理软件(例如word )错误单词检测
网络爬虫重复url检测。

布隆过滤器构建
参数:m个二进制向量,n个预备数据,k个hash函数
构建布隆过滤器:n个预备数据走一遍上面过程
判断元素存在∶走一遍上面过程∶如果都是1,则在表明存在,反之不存在。

误差率
·肯定存在误差︰恰好都命中了。
·直观因素:m/n的比率,hash函数的个数。

m/n与误差率成反比,k与误差率成反比。

本地布隆过滤器
·现有库:guava
·本地布隆过滤器的问题︰
(1)容量受限制。
(2)多个应用存在多个布隆过滤器,构建同步复杂。

基于Redis单机实现存在的问题
速度慢︰比本地慢,输在网络
-解决:单独部署,与应用同机房甚至机架部署。
容量受限:Redis最大字符串为512MB、Redis单机容量。
-解决︰基于Redis Cluster实现。

雪崩

雪崩问题︰缓存层高可用、客户端降级、提前演练是解决雪崩问题的重要方法。

无底洞

优化IO的几种方法
1.命令本身优化∶例如慢查询keys、hgetall bigkey
2.减少网络通信次数
3.降低接入成本︰例如客户端长连接/连接池、NIO等

热点key重新问题

产生原因:多个线程对key访问,前面的线程并没有完全建立缓存,后面线程再次建立缓存。

三个目标和两个解决
1.三个目标︰
减少重缓存的次数

数据尽可能一致

减少潜在危险

2.两个解决︰
·互斥锁(mutex key)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//伪代码
String get(String key){
String value =redis.get(key);
if (value == null) {
String mutexKey = "mutex:key:" + key;
if (redis.set(mutexKey, "1","ex 180", "nx")){
value = db.get(key);
redis.set(key, value);
redis.delete(mutexKey);
}else {
//其他线程休息50毫秒后重试Thread.sleep(50);
get(key);
}
}

·永远不过期

存在问题:不保证一致性

1.缓存层面∶没有设置过期时间(没有用expire)。
2.功能层面:为每个value添加逻辑过期时间,但发现超过逻辑过期时间后,会使用单独的线程去构建、缓存。

设计规范

Key名设计

三大建议

1 可读性和可管理性

以业务名(或数据库名)为前缀(防止key冲突),用冒号分割,比如业务名:表名:id,如: ugc:video:1

2简洁性

1
保证语义的前提下,控制key的长度,当key较多时,内存占用也不容忽视( redis3:39字节embstr),如:user:{uid }:friends:messages:{mid}简化为u:{uid}: fr:m:{mid}

3不要包含特殊字符

反例:包含空格、换行、单双引号以及其他转义字符

Value设计

1拒绝bigkey

强制
string类型控制在10KB以内
hash、list、set、zset元素个数不要超过5000

反例:一个包含几百万个元素的list、hash等,一个巨大的json字符串

bigkey的危害:

网络阻塞
集群节点数据不均衡
Redis阻塞
频繁序列化∶应用服务器CPU消耗

bigkey发现:
◆应用异常
redis-cli –bigkeys
scan + debug object
主动报警∶网络流量监控、客户端监控
内核热点key问题优化

2选择合适的数据结构

例如:实体类型(数据结构内存优化:例如ziplist,注意内存和性能的平衡)

反例:
set user:1:name tom;
set user:1:age 19;
set user:1:favor football

正例:
hmset user:1 name tom age 19 favor football

3过期设计

周期数据需要设置过期时间,object idle time可以找垃圾key-value
过期时间不宜集中∶缓存穿透和雪崩等问题

1.惰性删除∶访问key -> expired dict -> del key

2.定时删除︰每秒运行10次,采样删除。

超过maxmemory后触发相应策略,由maxmemory-policy控制。
Noeviction:默认策略,不会删除任何数据,拒绝所有写入操作并返回端错误信息“ ( error ) OOM command not allowed when usedmemory”此时Redis只响应读操作由maxmemory-policy控制。

Volatile-Iru:根据LRU算法删除设置了超时属性( expire )的键,直到腾出足够空间为止。如果没有可删除的键对象,回退到noeviction策略。

Allkeys-lru:根据LRU算法删除键,不管数据有没有设置超时属性,直到腾出足够空间为止。

Allkeys-random:随机删除所有键,直到腾出足够空间为止。
volatile-random :随机删除过期键,直到腾出足够空间为止。
volatile-ttl:根据键值对象的ttl属性,删除最近将要过期数据。如果没有,回退到noeviction策略。

文章作者: 夜雨
文章链接: https://yeyu1024.xyz/2022/03/17/redis/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 夜雨小屋