跳至正文
来两杯美式
返回

Redis 知识系列(一):数据结构与单机原理

By 来两杯美式
发布于

Redis 为什么这么快(10w+ QPS)

官方给出的单点 QPS 上限在 10w 以上,这个数字背后是四件事:

关于单线程的常见疑问:单线程是指核心业务处理是单线程的,可开其他线程用于持久化等操作,并不是整个 Redis 进程都是单线程。

Redis 6.0 引入的多线程 I/O

Redis 6.0 之前,“单线程”包含网络 I/O + 命令解析 + 命令执行。Redis 6.0 之后:

启用 I/O 多线程后,Redis 在超高并发场景下(单机 QPS 超过 10w 接近极限时)的吞吐能力有明显提升,但同时也会带来命令执行顺序的不确定性问题——这点需要权衡。


Redis 的数据类型

Redis 不是简单的 key-value 存储,它提供了 9 种常用数据类型,对应不同的业务场景。

string

Redis 没有使用 C 语言的 char*,而是定义了简单动态字符串(Simple Dynamic String,SDS)作为默认字符串表示:

struct sdshdr {
    int len;     // buf 中存储的字符串长度
    int free;    // buf 中空闲空间的长度
    char buf[];  // 用于存储字符串
};

SDS 的好处:

hash

特别适合存储对象,例如用户信息 user:{id} → 多个 field。

list

底层是双向链表 + 压缩列表(ziplist)的组合,能存储约 40 亿个成员。提供栈(LPUSH/LPOP)、队列(RPUSH/LPOP)的功能,可以实现最新消息排行榜等功能。

set

带去重的无序集合,添加成功返回 1,添加重复元素返回 0。

应用场景:在微博中,将一个用户所有的关注人存于 set 中,Redis 提供交集、并集、差集等操作,可以轻松获取到两个用户的共同关注、共同喜好等。

zset

同 set 类似,但每个元素关联一个 double 类型的分数。集合中元素唯一,但分数可以重复

应用:排行榜(充值榜、主播房间人数榜)。

HyperLogLog

非精准统计,标准误差率为 0.81%。

典型场景:网站每个网页每天的 UV 数据。

误差参考:插入 10 万条数据,统计出来少近 300,误差率 0.3%。

存储开销:HyperLogLog 不是免费的,占据一定 12k 的存储空间,不适合统计单个用户相关的数据。如果用户上亿,可以算算这个空间成本有多惊人。

但是相比 set 存储方案,HyperLogLog 在百万级以上的去重场景里内存节省是非常可观的。Redis 内部做了优化:计数较小时用稀疏矩阵存储(占用很小),计数变大超过阈值时才一次性转变成稠密矩阵(占用 12k)。

Geo

支持存储地理位置信息,可用于”附近的人”、地图标注等场景。

bitmap

bitmap 不是新的数据结构,而是基于 string 的衍生类型

典型场景:公司有 5000w 用户,可用 bitmap 做活跃用户统计(每天一个 key,第 N 位表示 ID=N 的用户是否活跃)。

Stream(Redis 5.0+)

Redis 5.0 引入的日志型数据结构,用于消息队列场景:

和 pub/sub 的区别:

特性pub/subStream
持久化不持久化持久化
离线消息不保留保留
多消费者不支持消费者组支持消费者组
消息回溯不支持支持

键过期与内存淘汰

键过期与内存淘汰是 Redis 两个不同层级的清理机制:

键过期命令

EXPIRE key seconds          # 设置过期时间(秒)
PEXPIRE key milliseconds    # 设置过期时间(毫秒)
TTL key                      # 查看剩余过期时间(秒)
PTTL key                     # 查看剩余过期时间(毫秒)
PERSIST key                  # 取消过期时间

删除策略

Redis 同时使用两种策略删除过期 key:

两种策略配合,保证不会让过期 key 永远占用内存,同时也不会在 key 过期瞬间消耗大量 CPU。

内存淘汰策略

当 Redis 内存达到 maxmemory 上限,且无法立即释放时(所有 key 都未过期),必须按策略淘汰部分 key:

# 配置上限
config set maxmemory 2gb

# 配置淘汰策略
config set maxmemory-policy <>
策略含义适用场景
noeviction不淘汰,写入报错不允许丢数据的场景
allkeys-lru所有 key 中淘汰最近最少使用通用缓存
allkeys-lfu所有 key 中淘汰最不经常使用(Redis 4.0+)热点变化慢的场景
allkeys-random所有 key 中随机淘汰数据无差别场景
volatile-lru过期 key 中 LRU既有持久数据又有缓存
volatile-lfu过期 key 中 LFU同上
volatile-random过期 key 中随机淘汰同上
volatile-ttl过期 key 中淘汰剩余 TTL 最小的同上

选型建议:纯缓存用 allkeys-lruallkeys-lfu;混合持久和缓存数据用 volatile-*

LRU 和 LFU 都是近似算法(用采样+链表模拟),不是严格实现。Redis 默认采样 5 个 key(maxmemory-samples),可调到 10 提升精度但会消耗更多 CPU。


bigkey 排查与治理

bigkey 的危害

排查方法

# 找出最大的 key(抽样统计,不是精确)
redis-cli --bigkeys

# 精确查看单个 key 占用内存
redis-cli memory usage <key>

# 找出占用内存最大的 N 个 key(需要 rdbtools 工具集)
rdb -c memory dump.rdb --bytes 10240 -f memory.csv

治理建议


redis 和 memcache 区别

维度RedisMemcached
数据类型丰富(9 种)简单(string)
持久化支持(RDB/AOF)不支持
主从复制支持不支持
数据分片支持(Cluster / Codis)客户端分片
内存管理多种淘汰策略LRU
线程模型6.0 后部分多线程多线程

Redis 相对 Memcached 的核心优势:数据类型丰富、可持久化、可主从、可分片存储,更适合作为系统的”事实主存储 + 缓存”统一层。


命令文档

redisdoc.com


分享这篇文章:
通过邮件分享这篇文章✓ 链接已复制
所属专题
Redis
第 1 / 4 篇
查看系列全部文章
  1. 01.Redis 知识系列(一):数据结构与单机原理
  2. 02.Redis 知识系列(二):高效使用
  3. 03.Redis 知识系列(三):持久化与高可用
  4. 04.Redis 知识系列(四):集群与缓存架构

上一篇
MySQL 知识系列(四):日志系统:redo log、undo log、binlog
下一篇
MySQL 知识系列(三):锁机制与并发控制