Redis 快记

安装

主从

复制延时

master挂掉需要人工干预升级从节点为主节点,每个包含完整数据

哨兵

哨兵是一个特殊服务不参与数据操作,可以部署Sentinel为一个集群,一个哨兵发现Master故障,会与集群中其他Sentinel确认,确认过半则下线Master。哨兵只监控主节点。会执行switch-master操作。下线的节点同步完成后会重现上线。

集群

数据分区,支持主从复制、故障转移

每个master会带一个salve

主节点:负责读写请i去和集群信息维护

从节点:只进行主节点数据和状态信息的复制

cluster-enable yes

环形hash槽位 16384

数据结构

String

key - string

get

set key value 单值缓存

set user:1 ["name":"roy", "balance":"1999" ] // 对象缓存

mget

mset user:1:name roy user:1:balance 1888 能保证原子性

append

incr:自增,可以用来分配key保证不冲突

分布式锁

SETNX product:10001 true // 返回1代表获取锁成功,返回0代表失败,TTL

DEL product:10001 // 多次获取删一次就可以

TTL product:10001

EXPIRE product:10001 10 // 设置过期时间防止阻塞

getset k1 v6 // 获取并重新设置

Hash

key - {field, value}

HSET h1 f1 v1 f2 v2 f3 v3

HGET h1 f1

HSETNX h1 f4 v4

购物车

  • 以用户id为key

  • 商品id为field

  • 商品数量为value

购物车操作:

  • 添加商品 setcart:1001 10088 1

  • 增加数量:hincriby cart:1001 10088 1

  • 商品总数:hlen cart:1001

  • 删除商品:hdel cart:1001 10088

  • 获取购物车所有商品 hgetall:cart:1001

有点:

  • 同类数据归类存储,方便管理

  • 相比string操作消耗内存 cpu更小

  • 相比string存储更省空间

缺点:

  • 过期功能不用用在field上 只能用在key

  • 集群架构不适合大规模使用,可能有大key问题

List

key - [a][b][c][d]

数据结构

Stack 栈 = LPUSH + LPOP

Queue = LPUSH + RPOP

Blocking MQ = LPUSH + BRPOP

使用场景

视频列表、签到列表

排队机

简化版MQ

Action

LPUSH //左推

RPUSH //右推

LPOP

RPOP

LRANGE

BLPOP

BRPOP

一个list容量时2的32次方-1个元素,大约40亿,注意大key的问题

list底层时双端列表,对双端操作效率高,不适合对下标操作

Set

key - (a,b,c,d) 无顺序 无重复

SADD

SREM 删

SCARD 数量

SISMEMBERS 查元素

SRANDMEMBER 随机查看一个 不会拿出

SPOP

Set 运算

SINTER 交集

SINTERSTORE

SUNION 并集

SDIFF 差集

SUNIONSTORE

使用场景

1 加入抽奖资格

SADD key {userID}

2 查看有哪些用户

SMEMBERS key

3 抽取count名中奖者

SPOP key [count]

ZSet

有排序Set,每个元素增加score

key - ([score,value],[score2,v2])

ZSCORE z1 c 查看分数

ZINCRBY z1 5 a 给a加5分

ZCOUNT z1 70 90 查看70-90分的元素

ZRANGE z1

使用场景

热点新闻

ZINCRBY hotNews:20180819 1 守护香港

展示当日排行前十

ZREVRANGE hotNews:20190813 0 ( WITHSCORES

七日搜索榜单计算

ZUNIONSTORE 后台News:20190813-20190819 7

Bitmap

key - [0][1][0][0][0]

SETBIT b1 10 1

SETBIT b1 5 1

BITCOUNT b1

BITCOUNT b1 0 9 bit

可以用于操作String

GETBIT k1 5

使用场景

每日签到

SETBIT dailycheck:1 100 1 用户1第100天签到

BITCOUNT dailycheck:1 统计用户1的签到次数

BITPOS dailycheck:1 统计用户1第一天签到的时间

优点

快速、高效、节省空间

Hyperloglog

统计一个集合中不重复的元素个数.大集合 会有错误率0.8%

pfadd h1 a b c d e

PFCOUNT h1

使用场景

统计UV 不同的独立访客

PFADD visitlog 192.168.1.1 xxx

PFCOUNT visitlog

Geo

地理空间

Stream

Redis版的MQ -- 阻塞队列 + pub/sub

Redis是单线程还是多线程

客户端多线程,服务端单线程

如何保证原子性

  1. 使用复合指令:

GETSET、SETNX、SETEX

  1. 使用事务:
  • MULTI 开启事务

  • set k1 1

  • incr k1

  • get k1

  • EXEC 执行事务

  • WATCH key 某个key被修改则事务执行会失败

  • 报错、有问题不会回滚。需要事务前备份才能回滚

  1. 管道:

--pipe

redis-cli -a 123qqq --pipe | cat cmd.txt

  1. lua
Lua 复制代码
eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first second

redis.call('get',KEYS[1])

redis.call('set',KEYS[1], b)

redis.register_function()

SCRIPT load 将脚本存到服务器(不建议)

EVALSHA asdad213sd21we123 0

BigKey问题

指哪些占用空间非常大的key,容易造成Redis的服务阻塞。200w个元素或存了一篇文章

redis-cli

--bigkeys

--memkeys

总结

Redis的瓶颈在于内存而不是cpu所以核心部分以单线程为主。

Redis压测

redis-benchmark -a 123qweasd -t set -n 1000000 -c 20 一百万数量20个客户端压测

吞吐量10w qps

Redis持久化

  1. 完全关闭持久化

  2. RDB 一定时间快照,快,紧凑,不能实时,可能会丢数据,占内存,fork子线程刷盘,

  3. AOF append only file: 文件大,性能慢,记录的是执行操作可以用来恢复,

建议RDB + AOF

RDB

RDB核心配置save操作:定时,定次,

save会阻塞

bgsave后台执行

LASTSAVE 显示上一次save的时间

AOF

appendonly yes开启

appendfilename 文件名

appenddirname 文件路径

appendfsync everysec 每秒记录

生成文件

appendonly.aof.2.base.rdb 全量备份

appendonly.aof.2.incr.aof 增量操作

appendonly.aof.mainfest 前两个文件的元数据

Replica

配从不配主,对主节点没有影响。

info replicatin 查看信息

从节点默认只读,可以修改为可写。可以禁用配置rename-command CONFIG ""

SLAVEOF no one, 断开从节点,

set k2 v2

SLAVEOF192.168.1.1 6379 重新配置成6379的从节点

数据出现不一致,会自动将slave数据覆盖。

主从复制工作流程

  1. Slave启动后,向master发送一个sync,建立成功后slave会删除自己的数据日志,等待主节点同步。

  2. master接到sync请求后会触发一次RDB。同时收集所有修改数据的指令。然后master将RDB和操作指令全量同步给Slave

  3. 主从关系建立后,master会定期向slave发送心跳包,确认状态默认10s

  4. master会持续将指令传递给slave并记录offset

5.如果slave短暂不回复master的心跳,master就会停止向slave同步数据,知道slave重新上线,master从offset开始继续向slave同步数据。

优缺点

  1. 同步有延迟

  2. 主节点挂掉,从节点不会自动切换

哨兵集群

主节点挂掉会自动选择从节点升级为主节点

replica-priority 100 优先选级别更小的为主节点

集群模式

数据倾斜:

user:k1 -> user{k1} 计算时会根据{}内容计算hash分配

mset需要所有key必须在要给slot上

mset user:{1}:name rror user:{1}:password 123 这样就可以将用户1的数据都放到同一个节点,能保证一致性

gossip协议:ping,pong,meet,fail,集群内同步协议,不保证强一致,默认使用服务端口+10000

总结 数据安全方案

单机 RDB-AOF

Replica 主从

Sentinel 监控

cluster 分担压力

Redis作为分布式锁

案例 秒杀减库存场景

  1. 并发问题

1)

  • setIfAbsent(lockKey,"xx"); // 获取锁,异常情况下,可能锁无法释放

  • expire(lockKey, 10, TimeUnit.SECONDS) // 设置超时时间

2)

  • setIfAbsent(lockKey, "xx", 10, TimeUnit.SECONDS); // 高并发依然有超卖问题,可能被别人解锁

3)

  • setIfAbsent(lockKey, clientId, 10, TimeUnit.SECONDS);

  • finally{

clientId.equals(stringRedisTemplate.opsForValue().get(lockKey)){

string.RedisTemplate.delete(lockKey); // 判断与释放不是原子性操作,如果锁刚好在判断后过期,则会删掉别人的锁

}

}

4)可以锁续命(redisson、jedis等框架提供额外操作)

RLock redissonLock = redisson.getLock(lockKey);

redissonLock.lock()

redissonLock.unLock()

  1. 用lua脚本中原子实现比较和set减库存

等待线程会订阅channel

释放锁的时候发一个消息

6)集群架构下分布式锁

主刚刚设置锁挂掉没同步到从节点

zk过半选举

redission重入锁:加两次也要减两次

Redis缓存架构

  1. 缓存要设置过期时间,查到的数据做缓存超时的延期,冷门数据自动过期删除。

  2. 缓存失效(击穿):大批量缓存在同一时间失效,导致大量请求打到数据库。解决办法:超时时间随机。

  3. 缓存穿透:

  • 将商品全部误删,缓存没有,数据库也没有,请求对缓存和数据库压力都很大。

  • 使用不存在的商品id发送请求进行攻击。

解决办法:当数据库查询为空时,给缓存也设置一个空缓存。空缓存也设置过期时间,否则会大量空缓存。

布隆过滤器:注意布隆过滤器不能删数据 只能重新初始化

  1. 突发数据热点
  • 分布式锁DCL

  • 查缓存

  • 加锁

  • 查缓存

  • 查数据库更新缓存

  1. 双写不一致
  • 加分布式锁,写缓存的时候不允许其他人读

  • 延迟双删,删除后sleep一点时间后再删除一次,防止被其他线程错误的更新缓存

  • 使用canal通过监听数据库binlog来更新缓存,不需要业务代码更新缓存

  1. 读多写少
  • 读写锁(写写有并发,写读有并发,读读没并发)

  • tryLock 大量线程重建缓存,重建也比较快,用tryLock没有锁竞争。但如果限定时间内没有重建完成,则失败。

  1. 缓存雪崩------明星热点事件
  • 大量请求到redis,redis扛不住。

  • 限流

  • 多级缓存,自己维护一个Map做缓存防止Redis崩溃时无法服务。

设计规范

  1. key名设计

业务名为前缀防止冲突,用冒号分隔

trade:order:1

不包含空格、换行、引号

  1. value设计

拒绝bigkey 字符串大于512MB,value集合不超过5000个元素

非字符串不要使用del删除,使用hscan\sscan\zscan,删除也是耗时操作

网络堵塞,

过期删除,过期异步删除

产生bingkey的原因:粉丝列表,统计类用户集合,缓存类数据库load出来都放到redis

如何优化:拆,不要一次取出所有db数据,删除也不要一次删除所有hmget代替hgetall

  1. 控制key的生命周期

  2. 线上禁止使用keys, flushall, flushdb, 通过redis rename机制禁掉,或者使用scan的方式渐进处理

  3. 使用批量操作mget, mset,使用pipeline提高效率

连接池优化

maxTotal:

Redis并发量

不超过redis的maxclient默认10000,nodes(应用个数) * maxTotal不超过 maxclient

资源开销:maxTotal=maxIdle

连接池预热,redis连接池初始化时没有任何链接。循环创建连接并ping()注意在循环外close()确保能够创建链接。

  1. Key的删除策略
  • 被动删除,key过期时触发惰性删除策略,可能get的时候检查是否过期

  • 主动删除,定期淘汰过期的key

  • 当前内存超过maxmemory,触发主动清除策略

主动清除策略

a) 过期时间的key

  • volatile-ttl: 根据过期时间

  • volatile-random

  • volatile-lru:最近最少使用 Least Recently Used

  • volatile-lfu:最不经常使用 Least Frequently Used 热点数据使用LFU可能更好

b) 对所有key做处理(没空间了):

  • allkeys-random:

  • allkeys-lru

  • allkeys-lfu

c) 不处理

  • noevication: 不会提出任何数据但拒绝写入 OOM command not allow

JDHotkey

多级缓存一般时本地一个HashMap做缓存

优点

减少网络请求,提高性能

分布式系统中,天然分布式缓存

减少远程缓存的读压力

缺点

进程空间大小有限,不支持大数据量

重启程序会丢失

分布式场景下,系统之间不一致

和远程缓存不一致

什么场景使用多级缓存

热点商品详情页

热搜

热点帖子

热门用户主页

热点探测服务原理

什么是热点:有限时间,流量高聚集

有预期的热点,提前进行扩容、降级、缓存

无预期的热点,攻击、爬虫、突发新闻

根据滑动窗口计算key的热点次数

相关推荐
h***34632 小时前
【MySQL】表的基本操作
数据库·mysql·oracle
SelectDB2 小时前
为什么实时更新场景下 Doris 查询性能是 ClickHouse 的 34 倍
数据库
百***66172 小时前
linux上redis升级
linux·运维·redis
Felix_XXXXL3 小时前
mysql查看binlog日志
java·后端
n***63273 小时前
MySQL数据库的数据文件保存在哪?MySQL数据存在哪里
数据库·mysql
leonardee3 小时前
Plugin ‘mysql_native_password‘ is not loaded`
java·后端
SelectDB3 小时前
从 Flink 到 Doris 的实时数据写入实践——基于 Flink CDC 构建更实时高效的数据集成链路
数据库
普通网友3 小时前
使用Flask快速搭建轻量级Web应用
jvm·数据库·python
珹洺3 小时前
Java-Spring入门指南(三十一)Android意图(Intent)
android·java·spring