概述

在实际开发中,我们经常会遇到需要选择存储方案的情况。Redis、KV存储(如RocksDB)和MySQL是三种常见的存储系统,它们各有特点,适用于不同的场景。本文将从架构、性能、使用场景等多个维度进行详细对比。

一、Redis vs KV存储 vs MySQL 核心对比

1.1 基本定位

特性 Redis KV存储(RocksDB) MySQL
类型 内存型键值数据库 磁盘型键值存储引擎 关系型数据库
数据模型 键值对 + 丰富数据结构 键值对 表(行和列)
存储介质 内存为主 磁盘为主 磁盘为主
主要用途 缓存、会话存储 大容量持久化存储 结构化数据存储

1.2 架构对比

Redis 架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
┌─────────────────────────────────────┐
│ Redis Server │
│ ┌──────────────────────────────┐ │
│ │ 内存数据结构 (Hash Table) │ │
│ │ - String, Hash, List, Set │ │
│ │ - Sorted Set, Bitmap, etc. │ │
│ └──────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────┐ │
│ │ 持久化层 (可选) │ │
│ │ - RDB (快照) │ │
│ │ - AOF (追加日志) │ │
│ └──────────────────────────────┘ │
└─────────────────────────────────────┘


磁盘存储(持久化文件)

特点

  • 数据主要存储在内存中
  • 单线程事件循环(6.0+ 多线程I/O)
  • 支持丰富的数据类型和命令
  • 持久化是辅助机制

KV存储(RocksDB)架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
┌─────────────────────────────────────┐
│ RocksDB Engine │
│ ┌──────────────────────────────┐ │
│ │ MemTable (内存表) │ │
│ │ - 写入缓冲区 │ │
│ └──────────────────────────────┘ │
│ │ │
│ ▼ (Flush) │
│ ┌──────────────────────────────┐ │
│ │ L0 (Level 0) │ │
│ │ - 最新的 SSTable 文件 │ │
│ └──────────────────────────────┘ │
│ │ │
│ ▼ (Compaction) │
│ ┌──────────────────────────────┐ │
│ │ L1, L2, L3... (多层级) │ │
│ │ - 按大小和年龄分层 │ │
│ │ - 压缩合并 │ │
│ └──────────────────────────────┘ │
└─────────────────────────────────────┘


磁盘存储(SSTable 文件)

特点

  • 基于 LSM-Tree(Log-Structured Merge Tree)
  • 写入先到 MemTable,再刷到磁盘
  • 多层级存储,定期 Compaction
  • 磁盘为主,内存作为缓存

MySQL 架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
┌─────────────────────────────────────┐
│ MySQL Server │
│ ┌──────────────────────────────┐ │
│ │ SQL解析器 + 优化器 │ │
│ └──────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────┐ │
│ │ 存储引擎层 (InnoDB) │ │
│ │ - Buffer Pool (缓冲池) │ │
│ │ - Redo Log (重做日志) │ │
│ │ - Undo Log (回滚日志) │ │
│ └──────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────┐ │
│ │ B+树索引 + 数据页 │ │
│ └──────────────────────────────┘ │
└─────────────────────────────────────┘


磁盘存储(.ibd 文件)

特点

  • 关系型数据模型(表、行、列)
  • 支持 SQL 查询语言
  • ACID 事务保证
  • B+树索引结构

二、详细对比分析

2.1 数据模型对比

Redis

  • 数据结构:String、Hash、List、Set、Sorted Set、Bitmap、HyperLogLog、GEO
  • 数据组织:键值对形式,支持复杂数据结构
  • 示例
    1
    2
    3
    SET user:1001 "张三"
    HSET user:1001:info name "张三" age 25
    ZADD leaderboard 100 "user:1001"

KV存储(RocksDB)

  • 数据结构:纯键值对
  • 数据组织:简单的 key-value 映射
  • 示例
    1
    2
    rocksdb::Put(options, "user:1001", "张三");
    rocksdb::Get(options, "user:1001", &value);

MySQL

  • 数据结构:表(Table)、行(Row)、列(Column)
  • 数据组织:关系型结构,支持外键、索引
  • 示例
    1
    2
    3
    4
    5
    6
    CREATE TABLE users (
    id INT PRIMARY KEY,
    name VARCHAR(100),
    age INT
    );
    INSERT INTO users VALUES (1001, '张三', 25);

2.2 性能对比

维度 Redis KV存储(RocksDB) MySQL
读取延迟 微秒级(内存) 毫秒级(磁盘I/O) 毫秒级(磁盘I/O)
写入延迟 微秒级(内存) 毫秒级(顺序写入) 毫秒级(随机写入+日志)
写入吞吐 受内存带宽限制 极高(LSM-Tree顺序写) 中等(需要维护索引)
读取吞吐 极高(内存访问) 高(需要磁盘I/O) 高(需要磁盘I/O)
并发能力 极高(单线程+多路复用) 高(多线程) 高(多线程+连接池)

2.3 容量与成本

维度 Redis KV存储(RocksDB) MySQL
存储容量 受内存限制(通常GB级) 受磁盘限制(TB级) 受磁盘限制(TB级)
成本 高(内存昂贵) 低(磁盘便宜) 中等(磁盘+内存缓冲)
扩展性 集群模式(Redis Cluster) 单机或分布式 主从复制、分库分表

2.4 持久化与可靠性

Redis

  • 持久化方式
    • RDB:定时快照
    • AOF:追加日志
  • 数据丢失风险:可能丢失最后一次快照后的数据
  • 适用场景:缓存数据,允许少量数据丢失

KV存储(RocksDB)

  • 持久化方式:写入即持久化(WAL + SSTable)
  • 数据丢失风险:极低(写入即落盘)
  • 适用场景:需要持久化的大容量存储

MySQL

  • 持久化方式:Redo Log + Binlog + 数据页刷盘
  • 数据丢失风险:极低(ACID保证)
  • 适用场景:需要强一致性的业务数据

2.5 事务与一致性

Redis

  • 事务支持:支持 MULTI/EXEC,但非 ACID
  • 一致性:最终一致性
  • 隔离性:单线程保证命令原子性

KV存储(RocksDB)

  • 事务支持:支持批量写入和 WriteBatch
  • 一致性:单机强一致性
  • 隔离性:写入顺序保证

MySQL

  • 事务支持:完整的 ACID 事务
  • 一致性:强一致性
  • 隔离性:MVCC + 锁机制(4种隔离级别)

2.6 查询能力

Redis

  • 查询方式:键值查询 + 数据结构操作
  • 范围查询:Sorted Set 支持范围查询
  • 复杂查询:不支持 SQL,需要应用层实现

KV存储(RocksDB)

  • 查询方式:键值查询 + 前缀查询
  • 范围查询:支持迭代器范围扫描
  • 复杂查询:不支持 SQL,需要应用层实现

MySQL

  • 查询方式:SQL 查询语言
  • 范围查询:支持 WHERE、JOIN、GROUP BY 等
  • 复杂查询:支持复杂的 SQL 查询和聚合

三、KV存储 vs MySQL 详细对比

3.1 数据模型差异

KV存储

  • 无模式(Schema-less):不需要预定义表结构
  • 扁平化存储:键值对形式,无关系概念
  • 灵活性高:可以存储任意结构的数据

MySQL

  • 有模式(Schema):需要预定义表结构
  • 关系型存储:支持表之间的关联(外键)
  • 结构化强:数据类型、约束、索引都需要定义

3.2 查询能力差异

KV存储

1
2
3
4
5
6
7
8
// 只能通过 key 查询
rocksdb::Get(options, "user:1001", &value);

// 支持前缀查询和范围扫描
rocksdb::Iterator* iter = db->NewIterator(options);
for (iter->Seek("user:"); iter->Valid() && iter->key().starts_with("user:"); iter->Next()) {
// 处理数据
}

限制

  • 无法进行复杂的条件查询
  • 无法进行多表关联
  • 无法进行聚合统计

MySQL

1
2
3
4
5
6
7
8
-- 支持复杂的 SQL 查询
SELECT u.name, COUNT(o.id) as order_count
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.age > 18
GROUP BY u.id
HAVING order_count > 5
ORDER BY order_count DESC;

优势

  • 支持复杂的 WHERE 条件
  • 支持多表 JOIN
  • 支持聚合函数(COUNT、SUM、AVG等)
  • 支持子查询和窗口函数

3.3 事务能力差异

KV存储

  • 事务语义:支持批量写入(WriteBatch)
  • ACID特性:不完整,主要是原子性
  • 隔离级别:无标准隔离级别概念
1
2
3
4
5
rocksdb::WriteBatch batch;
batch.Put("key1", "value1");
batch.Put("key2", "value2");
batch.Delete("key3");
db->Write(options, &batch); // 原子性写入

MySQL

  • 事务语义:完整的 ACID 事务
  • ACID特性:全部支持
  • 隔离级别:READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ、SERIALIZABLE
1
2
3
4
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT; -- 完整的 ACID 保证

3.4 索引能力差异

KV存储

  • 索引方式:基于 key 的自然排序
  • 索引类型:LSM-Tree 内部索引
  • 索引维护:自动维护,写入时自动更新

MySQL

  • 索引方式:B+树索引
  • 索引类型:主键索引、唯一索引、普通索引、联合索引、全文索引
  • 索引维护:需要手动创建和维护
1
2
3
4
5
6
-- 创建索引
CREATE INDEX idx_user_age ON users(age);
CREATE INDEX idx_user_name_age ON users(name, age);

-- 查看执行计划
EXPLAIN SELECT * FROM users WHERE age > 18;

3.5 适用场景对比

KV存储适合:

  1. 时序数据存储:监控数据、日志数据
  2. 大容量键值存储:用户画像、配置信息
  3. 嵌入式数据库:作为应用内存储引擎
  4. 写多读少场景:LSM-Tree 顺序写入性能高

MySQL适合:

  1. 结构化数据存储:用户信息、订单信息
  2. 复杂查询需求:需要 JOIN、聚合、子查询
  3. 事务性业务:需要 ACID 保证的业务
  4. 关系型数据:需要维护表之间关系的场景

四、Redis vs KV存储 详细对比

4.1 存储介质差异

Redis

  • 主要存储:内存
  • 持久化:可选的 RDB/AOF
  • 数据特点:重启可能丢失数据(取决于持久化配置)

KV存储

  • 主要存储:磁盘
  • 持久化:写入即持久化
  • 数据特点:数据持久化,重启不丢失

4.2 数据结构差异

Redis

  • 丰富的数据结构:String、Hash、List、Set、Sorted Set等
  • 原子操作:支持对数据结构的原子操作
  • 示例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # 列表操作
    LPUSH mylist "item1"
    RPOP mylist

    # 集合操作
    SADD myset "member1"
    SINTER myset1 myset2

    # 有序集合操作
    ZADD leaderboard 100 "user1"
    ZRANGE leaderboard 0 10

KV存储

  • 简单键值对:只有 key-value
  • 无原子操作:需要应用层实现复杂逻辑
  • 示例
    1
    2
    3
    // 只能进行基本的 put/get 操作
    rocksdb::Put(options, "key", "value");
    rocksdb::Get(options, "key", &value);

4.3 性能差异

Redis

  • 优势:内存访问,延迟极低(微秒级)
  • 劣势:容量受内存限制,成本高

KV存储

  • 优势:容量大,成本低,写入吞吐高
  • 劣势:磁盘I/O,延迟较高(毫秒级)

4.4 使用场景差异

Redis适合:

  1. 缓存热点数据:减轻数据库压力
  2. 会话存储:用户登录状态
  3. 实时排行榜:Sorted Set
  4. 分布式锁:SETNX 命令
  5. 消息队列:List 或 Stream
  6. 计数器:INCR 命令

KV存储适合:

  1. 时序数据存储:监控指标、日志
  2. 大容量存储:用户画像、配置中心
  3. 嵌入式存储:作为应用内存储引擎
  4. 冷数据存储:访问频率低的数据

五、实际应用中的组合使用

在实际项目中,这三种存储系统通常会组合使用,发挥各自的优势:

1
2
3
4
5
6
7
8
9
10
11
12
┌─────────────────────────────────────┐
│ 应用层 │
└─────────────┬───────────────────────┘

┌─────────┼─────────┐
│ │ │
▼ ▼ ▼
┌────────┐ ┌──────────┐ ┌────────┐
│ Redis │ │ RocksDB │ │ MySQL │
│ (热数据)│ │ (冷数据) │ │ (主数据)│
│ 内存 │ │ 磁盘 │ │ 磁盘 │
└────────┘ └──────────┘ └────────┘

5.1 典型架构示例

场景:用户系统

  1. MySQL:存储用户基本信息(id、name、email等)
  2. Redis:缓存热点用户信息,存储用户会话
  3. RocksDB:存储用户行为日志、历史数据

数据流转

1
2
用户请求 → 先查 Redis → 未命中 → 查 MySQL → 写回 Redis
用户行为 → 写入 RocksDB(异步)

5.2 缓存策略

Cache-Aside 模式

1
2
3
1. 读取:先查 Redis,未命中查 MySQL,写回 Redis
2. 更新:更新 MySQL,删除 Redis 缓存
3. 写入:写入 MySQL,可选写入 Redis

六、选择建议

6.1 选择 Redis 的场景

适合

  • 需要极低延迟(微秒级)
  • 数据量不大(GB级)
  • 需要丰富的数据结构
  • 缓存、会话、排行榜等场景

不适合

  • 大容量数据存储(TB级)
  • 需要复杂的 SQL 查询
  • 需要强事务保证

6.2 选择 KV存储的场景

适合

  • 大容量数据存储(TB级)
  • 写多读少的场景
  • 时序数据、日志数据
  • 需要持久化但不需要复杂查询

不适合

  • 需要极低延迟(微秒级)
  • 需要复杂查询和关联
  • 需要 ACID 事务

6.3 选择 MySQL 的场景

适合

  • 结构化数据存储
  • 需要复杂 SQL 查询
  • 需要 ACID 事务
  • 需要维护表之间的关系

不适合

  • 需要极低延迟(微秒级)
  • 简单的键值存储场景
  • 大容量非结构化数据

七、总结

维度 Redis KV存储(RocksDB) MySQL
定位 内存缓存数据库 磁盘键值存储引擎 关系型数据库
优势 低延迟、丰富数据结构 大容量、高写入吞吐 强一致性、复杂查询
劣势 容量受限、成本高 延迟较高、查询简单 延迟较高、写入性能一般
核心场景 缓存、会话、排行榜 时序数据、大容量存储 业务数据、复杂查询

在实际项目中,这三种存储系统通常会组合使用

  • Redis 作为缓存层,提供低延迟访问
  • MySQL 作为主数据库,存储业务数据
  • KV存储 作为辅助存储,处理大容量数据

选择合适的存储方案,需要根据具体的业务场景、性能要求、数据特点来综合考虑。