一: String 字符串
字符串是 Redis 中最基础的数据类型,它具有以下特点:所有键的类型都是字符串,其它数据结构(如列表和集合)的元素也基于字符串,因此字符串类型是理解其它数据结构的基础。字符串的值可以是普通字符串(如 JSON 或 XML 格式)、数字(整型或浮点型)、或二进制流数据(如图片、音频、视频),但单个字符串的最大值不得超过 512 MB。由于 Redis 内部以二进制流的形式存储字符串,因此不处理字符集编码问题,客户端使用什么字符集编码传入数据,Redis 就以相同的编码方式存储。
1.1 常见命令
1.1.1 SET
SET 命令用于将字符串类型的值设置到指定的 key 中。如果 key 已存在,无论原有的数据类型是什么都会被覆盖,同时清除该 key 的 TTL。返回值为 OK 表示设置成功;如果使用了 NX 或 XX 参数但条件不满足,SET 将不会执行,返回 (nil)。
SET key value [expiration EX seconds|PX milliseconds] [NX|XX]
参数 | 描述 |
---|---|
EX seconds | 使用秒作为单位设置 key 的过期时间。 |
PX milliseconds | 使用毫秒作为单位设置 key 的过期时间。 |
NX | 仅在 key 不存在时才执行设置操作,如果 key 已存在,则不执行设置。 |
XX | 仅在 key 存在时才执行设置操作,如果 key 不存在,则不执行设置。 |
| | 表示二选一,即只能在 EX seconds 和 PX milliseconds 之间选择一个,或者在 NX 和 XX 之间选择一个。 |
[ ] | 表示该部分为可选参数,可以根据需要选择性添加,不是必须的。 |
# 情况一:
EXISTS mykey
(integer) 0
SET mykey "Hello"
OK
GET mykey
"Hello"
# 情况二:
SET mykey "World" NX
(nil)
DEL mykey
(integer) 1
EXISTS mykey
(integer) 0
SET mykey "World" XX
(nil)
GET mykey
(nil)
# 情况三:
SET mykey "World" NX
OK
GET mykey
"World"
SET mykey "Will expire in 10s" EX 10
OK
GET mykey
"Will expire in 10s"
GET mykey
(nil)
1.1.2 GET
GET 命令用于获取指定 key 的值。如果 key 存在且值为字符串类型,则返回对应的 value;如果 key 不存在,则返回 nil;如果 key 的值不是字符串类型,则会返回错误。
redis> GET nonexisting
(nil)
redis> SET mykey "Hello"
"OK"
redis> GET mykey
"Hello"
redis> DEL mykey
(integer) 1
redis> EXISTS mykey
(integer) 0
redis> HSET mykey name Bob
(integer) 1
redis> GET mykey
(error) WRONGTYPE Operation against a key holding the wrong kind of value
1.1.3 MGET
MGET 命令用于一次性获取多个 key 的值。对于不存在的 key 或数据类型不是字符串的 key,返回结果为 nil。返回值是对应 key 的 value 列表。
MGET key [key ...]
redis> SET key1 "Hello"
"OK"
redis> SET key2 "World"
"OK"
redis> MGET key1 key2 nonexisting
1) "Hello"
2) "World"
3) (nil)
1.1.4 MSET
MSET 命令用于一次性设置多个 key 的值,返回值始终为 OK。
MSET key value [key value ...]
redis> MSET key1 "Hello" key2 "World"
"OK"
redis> GET key1
"Hello"
redis> GET key2
"World"
1.1.5 多次 get vs 单次 mget
使用 MGET 和 MSET 命令可以有效减少网络传输时间从而提升性能。通过批量操作能够显著提高业务处理效率,但需要注意,批量操作的键数量不宜过多,否则可能导致单条命令执行时间过长,引发 Redis 阻塞问题。
1.1.6 SETNX
SETNX 命令用于在 key 不存在时设置 key-value 对。返回值:1 表示设置成功,0 表示未设置(因 key 已存在)。
SETNX key value
redis> SETNX mykey "Hello"
(integer) 1
redis> SETNX mykey "World"
(integer) 0
redis> GET mykey
"Hello"
1.1.7 INCR
INCR 命令用于将 key 对应的字符串形式的数字加一。如果 key 不存在,则默认将其值视为 0 再进行加一操作。如果 key 的值不是整型字符串或超出 64 位有符号整型范围,则会报错。返回值为加一后的整数值。
INCR key
redis> EXISTS mykey
(integer) 0
redis> INCR mykey
(integer) 1
redis> SET mykey "10"
"OK"
redis> INCR mykey
(integer) 11
redis> SET mykey "234293482390480948029348230948"
"OK"
redis> INCR mykey
(error) value is not an integer or out of range
redis> SET mykey 'not a number'
"OK"
redis> INCR mykey
(error) value is not an integer or out of range
1.1.8 INCRBY
INCRBY 命令用于将 key 对应的字符串形式的数字加上指定的值。如果 key 不存在,则默认将其值视为 0 再进行加法操作。如果 key 的值不是整型字符串或超出 64 位有符号整型范围,则会报错。返回值为加法运算后的整数值。
INCRBY key decrement
EXISTS mykey
(integer) 0
INCRBY mykey 3
(integer) 3
SET mykey "10"
"OK"
INCRBY mykey 3
(integer) 13
INCRBY mykey "not a number"
(error) ERR value is not an integer or out of range
SET mykey "234293482390480948029348230948"
"OK"
INCRBY mykey 3
(error) value is not an integer or out of range
SET mykey "not a number"
"OK"
INCRBY mykey 3
(error) value is not an integer or out of range
1.1.9 DECR
DECR 命令用于将 key 对应的字符串形式的数字减一。如果 key 不存在,则默认将其值视为 0 再进行减一操作。如果 key 的值不是整型字符串或超出 64 位有符号整型范围,则会报错。返回值为减一后的整数值。
DECR key
EXISTS mykey
(integer) 0
DECR mykey
(integer) -1
SET mykey "10"
"OK"
DECR mykey
(integer) 9
SET mykey "234293482390480948029348230948"
"OK"
DECR mykey
(error) value is not an integer or out of range
SET mykey 'not a number'
"OK"
DECR mykey
(error) value is not an integer or out of range
1.1.10 DECYBY
DECRBY 命令用于将 key 对应的字符串形式的数字减去指定的值。如果 key 不存在,则默认将其值视为 0 再进行减法操作。如果 key 的值不是整型字符串或超出 64 位有符号整型范围,则会报错。返回值为减法运算后的整数值。
DECRBY key decrement
redis> EXISTS mykey
(integer) 0
redis> DECRBY mykey 3
(integer) -3
redis> SET mykey "10"
"OK"
redis> DECRBY mykey 3
(integer) 7
redis> DECRBY mykey "not a number"
(error) ERR value is not an integer or out of range
redis> SET mykey "234293482390480948029348230948"
"OK"
redis> DECRBY mykey 3
(error) value is not an integer or out of range
redis> SET mykey 'not a number'
"OK"
redis> DECRBY mykey 3
(error) value is not an integer or out of range
1.1.11 INCRBYFLOAT
INCRBYFLOAT 命令用于将 key 对应的字符串形式的浮点数加上指定的值。如果指定的值为负数,则视为减去该值。如果 key 不存在,则默认其值为 0。如果 key 的值不是字符串形式的浮点数,或格式不正确,则会报错。命令支持使用科学计数法表示浮点数。返回值为加/减运算后的浮点数值。
INCRBYFLOAT key increment
redis> SET mykey 10.50
"OK"
redis> INCRBYFLOAT mykey 0.1
"10.6"
redis> INCRBYFLOAT mykey -5
"5.6"
redis> SET mykey 5.0e3
"OK"
redis> INCRBYFLOAT mykey 2.0e2
"5200"
许多存储系统和编程语言通过 CAS 机制实现计数功能,这通常会带来一定的 CPU 开销。而在 Redis 中,由于采用单线程架构,所有命令在服务端都按顺序执行,因此无需引入 CAS 机制,从而避免了额外的性能开销。
1.1.12 APPEND
如果 key 存在且对应的是一个字符串,APPEND 命令会将指定的 value 追加到原有字符串的末尾;如果 key 不存在,则该命令的效果等同于 SET 命令。返回值为追加操作完成后字符串的长度。
APPEND KEY VALUE
redis> EXISTS mykey
(integer) 0
redis> APPEND mykey "Hello"
(integer) 5
redis> GET mykey
"Hello"
redis> APPEND mykey " World"
(integer) 11
redis> GET mykey
"Hello World"
1.1.13 GETRANGE
GETRANGE 命令用于返回 key 对应字符串的子串,子串范围由 start 和 end 参数确定(左闭右闭)。支持使用负数表示倒数位置,例如,-1 表示倒数第一个字符,-2 表示倒数第二个字符,依此类推。超出范围的偏移量会根据字符串的长度自动调整为有效范围。返回值为截取后的子串。
GETRANGE key start end
SET mykey "This is a string"
"OK"
GETRANGE mykey 0 3
"This"
GETRANGE mykey -3 -1
"ing"
GETRANGE mykey 0 -1
"This is a string"
GETRANGE mykey 10 100
"string"
1.1.13 SETRANGE
SETRANGE 命令用于从指定偏移量开始覆盖字符串的一部分内容。返回值为替换后字符串的长度。
SETRANGE key offset value
redis> SET key1 "Hello World"
"OK"
redis> SETRANGE key1 6 "Redis"
(integer) 11
redis> GET key1
"Hello Redis"
1.1.14 STRLEN
STRLEN 命令用于获取 key 对应字符串的长度。如果 key 的值不是字符串类型,则返回错误;如果 key 不存在,则返回 0。返回值为字符串的长度。
STRLEN key
SET mykey "Hello world"
"OK"
STRLEN mykey
(integer) 11
STRLEN nonexisting
(integer) 0
1.1.15 命令小结
命令 | 执行效果 | 时间复杂度 |
---|---|---|
set key value [key value…] | 设置 key 的值为 value | O(k),k 是键的个数 |
get key | 获取 key 的值 | O(1) |
del key [key …] | 删除指定的 key | O(k),k 是键的个数 |
mset key value [key value…] | 批量设置多个 key 和 value | O(k),k 是键的个数 |
mget key [key …] | 批量获取多个 key 的值 | O(k),k 是键的个数 |
incr key | 将指定 key 的值加 1 | O(1) |
decr key | 将指定 key 的值减 1 | O(1) |
incrby key n | 将指定 key 的值加 n | O(1) |
decrby key n | 将指定 key 的值减 n | O(1) |
incrbyfloat key n | 将指定 key 的值加 n(浮点数) | O(1) |
append key value | 将 value 追加到指定 key 的值后面 | O(1) |
strlen key | 获取指定 key 的值的长度 | O(1) |
setrange key offset value | 从指定偏移位置开始覆盖 key 的值 | O(n),n 是字符串长度,通常视为 O(1) |
getrange key start end | 获取指定 key 的从 start 到 end 的部分值 | O(n),n 是字符串长度,通常视为 O(1) |
1.2 内部编码
字符串类型的内部编码有 3 种:
内部编码类型 | 描述 | 适用场景 |
---|---|---|
int | 8 字节的长整型。 | 当值是可以转换为 64 位整数的字符串时。 |
embstr | 小于等于 39 字节的字符串。 | 当值是长度小于等于 39 字节的字符串时。 |
raw | 大于 39 字节的字符串。 | 当值是长度大于 39 字节的字符串时。 |
127.0.0.1:6379> set key 6379
OK
127.0.0.1:6379> object encoding key
"int"
127.0.0.1:6379> set key "hello"
OK
127.0.0.1:6379> object encoding key
"embstr"
127.0.0.1:6379> set key "one string greater than 39 bytes ........"
OK
127.0.0.1:6379> object encoding key
"raw"
1.3 字符串典型使用场景
1.3.1 缓存功能
在典型的缓存使用场景中,Redis 通常作为缓存层,MySQL 作为存储层,大部分请求数据直接从 Redis 获取。由于 Redis 支持高并发访问,缓存层能够显著加速读写操作,同时有效降低后端数据库的压力。
在根据用户 uid 获取用户信息的场景中,系统首先从 Redis 中获取用户信息(假设用户信息存储在键 “user:info:” 中)。如果 Redis 未命中(缓存 miss),则从 MySQL 中查询对应信息,并将查询结果写入 Redis 缓存后返回。
// 根据 uid 得到 Redis 的键
String key = "user:info:" + uid;
// 尝试从 Redis 中获取对应的值
String value = Redis 执行命令:get key;
// 如果缓存命中(hit)
if (value != null) {
// 假设用户信息按照 JSON 格式存储
UserInfo userInfo = JSON 反序列化(value);
return userInfo;
}
// 如果缓存未命中(miss)
if (value == null) {
// 从数据库中根据 uid 获取用户信息
UserInfo userInfo = MySQL 执行 SQL:select * from user_info where uid = <uid>;
// 如果表中没有 uid 对应的用户信息
if (userInfo == null) {
响应 404;
return null;
}
// 将用户信息序列化成 JSON 格式
String jsonValue = JSON 序列化(userInfo);
// 写入缓存,并设置过期时间为 1 小时(3600 秒)
Redis 执行命令:set key jsonValue ex 3600;
// 返回用户信息
return userInfo;
}
与 MySQL 等关系型数据库不同,Redis 没有表和字段这样的命名空间,对键名也没有强制要求(除了一些特殊字符不能使用)。但是合理的键名设计可以有效防止键冲突并提升项目的可维护性。推荐的方式是使用类似 “业务名:对象名:唯一标识:属性” 的格式。例如,如果 MySQL 数据库名为 vs,用户表名为 user_info,对应的 Redis 键名可以设计为 “vs:user_info:6379” 或 “vs:user_info:6379:name”。如果 Redis 只被单一业务使用,可以省略业务名,如直接使用 “user_info:6379”。
同时,为了避免键名过长影响 Redis 性能,可以使用团队内部认可的缩写替代。例如,将 “user:6379:friends:messages:5217” 简化为 “u:6379🇫🇷m:5217”。合理缩短键名不仅能提高性能,还能减少存储空间的浪费,从而优化 Redis 的整体效率。
1.3.2 计数功能
许多应用会使用 Redis 作为计数的基础工具,因为他能够实现快速计数和查询缓存功能,同时支持数据异步处理或持久化到其他数据源。例如在视频网站中,视频播放次数的统计可以通过 Redis 实现:每当用户播放一次视频,对应的视频播放数便会自动加 1。
// 在 Redis 中统计某视频的播放次数
long incrVideoCounter(long vid) {
String key = "video:" + vid;
long count = Redis 执行命令:incr key;
return count;
}
1.3.3 共享会话
在分布式 Web 服务中,用户的 Session 信息通常保存在各自的服务器上,但这种方式会带来一个问题:由于负载均衡的存在,用户的访问请求会被分配到不同的服务器上,且无法保证每次请求都分配到同一台服务器。这可能导致用户在刷新页面后需要重新登录,而这种体验是用户无法接受的。
为了解决这个问题,可以使用 Redis 对用户的 Session 信息进行集中管理。如图 2-13 所示,在这种模式下,只需保证 Redis 的高可用性和可扩展性,无论用户被负载均衡到哪台 Web 服务器上,都可以集中从 Redis 中查询和更新 Session 信息,从而避免重复登录的问题。
1.3.4 手机验证码
// 发送验证码
String sendValidationCode(String phoneNumber) {
String key = "shortMsg:limit:" + phoneNumber;
// 设置过期时间为 1 分钟(60 秒),使用 NX 仅在不存在时设置成功
boolean r = Redis 执行命令:set key 1 ex 60 nx;
if (!r) {
// 说明之前已经设置过该手机的验证码
long c = Redis 执行命令:incr key;
if (c > 5) {
// 超过一分钟内 5 次的限制,禁止发送
return null;
}
}
// 如果之前没有设置验证码,或者次数没有超过 5 次
String validationCode = 生成随机的 6 位数验证码();
String validationKey = "validation:" + phoneNumber;
// 验证码 5 分钟(300 秒)内有效
Redis 执行命令:set validationKey validationCode ex 300;
// 返回验证码,用于通过短信发送给用户
return validationCode;
}
// 验证用户输入的验证码是否正确
boolean validateCode(String phoneNumber, String validationCode) {
String validationKey = "validation:" + phoneNumber;
// 获取 Redis 中存储的验证码
String value = Redis 执行命令:get validationKey;
if (value == null) {
// 没有该手机的验证码记录,验证失败
return false;
}
// 验证输入的验证码是否正确
return value.equals(validationCode);
}
二: Hash 哈希
几乎所有主流编程语言都支持哈希类型,名称可能不同,例如哈希、字典、关联数组或映射等。在 Redis 中,哈希类型的值本身也是一个键值对结构,形式为 key = “key”,value = { {field1, value1}, …, {fieldN, valueN} }。其中,哈希类型的映射关系通常被称为 field-value,用于区分 Redis 整体的键值对(key-value)。需要注意,此处的 value 是指 field 对应的值,而非 key 对应的值,不同上下文中 value 的含义可能会有所不同。
2.1 常用命令
2.1.1 HSET
HSET 命令用于设置哈希表中指定字段(field)的值(value)。返回值为新增字段的数量。
HSET key field value [field value ...]
redis> HSET myhash field1 "Hello"
(integer) 1
redis> HGET myhash field1
"Hello"
2.1.2 HGET
获取哈希表中指定字段的值。返回值为字段对应的值,若字段不存在则返回 nil。
HGET key field
redis> HSET myhash field1 "foo"
(integer) 1
redis> HGET myhash field1
"foo"
redis> HGET myhash field2
(nil)
2.1.3 HEXISTS
HEXISTS 判断哈希表中是否存在指定字段。返回 1 表示存在,0 表示不存在。
HEXISTS key field
redis> HSET myhash field1 "foo"
(integer) 1
redis> HEXISTS myhash field1
(integer) 1
redis> HEXISTS myhash field2
(integer) 0
2.1.4 HDEL
HDEL 命令用于删除哈希表中指定的字段。返回值为成功删除的字段数量。
HDEL key field [field ...]
redis> HSET myhash field1 "foo"
(integer) 1
redis> HDEL myhash field1
(integer) 1
redis> HDEL myhash field2
(integer) 0
2.1.5 HKEYS
HKEYS 命令用于获取哈希表中所有字段的名称。返回值为字段名称列表。
HKEYS key
redis> HSET myhash field1 "Hello"
(integer) 1
redis> HSET myhash field2 "World"
(integer) 1
redis> HKEYS myhash
1) "field1"
2) "field2"
2.1.6 HVALS
HVALS 命令用于获取哈希表中所有字段的值。返回值为值的列表。
HVALS key
redis> HSET myhash field1 "Hello"
(integer) 1
redis> HSET myhash field2 "World"
(integer) 1
redis> HVALS myhash
1) "Hello"
2) "World"
2.1.7 HGETALL
HGETALL 命令用于获取哈希表中所有字段及其对应的值。返回值为字段和值的集合。
HGETALL key
redis> HSET myhash field1 "Hello"
(integer) 1
redis> HSET myhash field2 "World"
(integer) 1
redis> HGETALL myhash
1) "field1"
2) "Hello"
3) "field2"
4) "World"
2.1.8 HMGET
HMGET 命令用于一次获取哈希表中多个字段的值。返回值为字段对应的值列表,若字段不存在则返回 nil。
HMGET key field [field ...]
HSET myhash field1 "Hello"
(integer) 1
HSET myhash field2 "World"
(integer) 1
HMGET myhash field1 field2 nofield
1) "Hello"
2) "World"
3) (nil)
2.1.9 HLEN
HLEN 命令用于获取哈希表中字段的总数。返回值为字段的数量。
HLEN key
redis> HSET myhash field1 "Hello"
(integer) 1
redis> HSET myhash field2 "World"
(integer) 1
redis> HLEN myhash
(integer) 2
2.1.10 HSETNX
HSETNX 命令用于在字段不存在时,为哈希表设置指定字段和值。返回值:1 表示设置成功,0 表示设置失败(字段已存在)。
HSETNX key field value
redis> HSETNX myhash field "Hello"
(integer) 1
redis> HSETNX myhash field "World"
(integer) 0
redis> HGET myhash field
"Hello"
2.1.11 HINCRBY
HINCRBY 命令用于为哈希表中指定字段的数值增加指定值。返回值为字段更新后的值。
HINCRBY key field increment
redis> HSET myhash field 5
(integer) 1
redis> HINCRBY myhash field 1
(integer) 6
redis> HINCRBY myhash field -1
(integer) 5
redis> HINCRBY myhash field -10
(integer) -5
2.1.12 HINCRBYFLOAT
HINCRBYFLOAT 命令是 HINCRBY 的浮点数版本,用于为哈希表中指定字段的数值增加指定的浮点数值。返回值为字段更新后的值。
HINCRBYFLOAT key field increment
redis> HSET mykey field 10.50
(integer) 1
redis> HINCRBYFLOAT mykey field 0.1
"10.6"
redis> HINCRBYFLOAT mykey field -5
"5.6"
redis> HSET mykey field 5.0e3
(integer) 0
redis> HINCRBYFLOAT mykey field 2.0e2
"5200"
2.1.13 命令小结
命令 | 执行效果 | 时间复杂度 |
---|---|---|
hset key field value | 设置指定字段的值 | O(1) |
hget key field | 获取指定字段的值 | O(1) |
hdel key field [field…] | 删除指定字段 | O(k),k 是字段个数 |
hlen key | 计算字段总数 | O(1) |
hgetall key | 获取所有字段及其对应的值 | O(k),k 是字段个数 |
hmget key field [field…] | 批量获取多个字段的值 | O(k),k 是字段个数 |
hmset key field value [field value…] | 批量设置多个字段的值 | O(k),k 是字段个数 |
hexists key field | 判断指定字段是否存在 | O(1) |
hkeys key | 获取所有字段 | O(k),k 是字段个数 |
hvals key | 获取所有值 | O(k),k 是字段个数 |
hsetnx key field value | 设置值,但仅在字段不存在时设置成功 | O(1) |
hincrby key field n | 将字段的值增加指定的整数 | O(1) |
hincrbyfloat key field n | 将字段的值增加指定的浮点数 | O(1) |
hstrlen key field | 计算字段对应值的字符串长度 | O(1) |
2.2 内部编码
哈希的内部编码有两种:
内部编码类型 | 使用条件 | 优势 |
---|---|---|
ziplist | 当哈希类型元素个数小于 hash-max-ziplist-entries(默认 512 个),且所有值小于 hash-max-ziplist-value(默认 64 字节)时使用。 | 采用紧凑结构,节省内存。 |
hashtable | 当哈希类型不满足 ziplist 条件(如元素过多或值过大)时使用。 | 读写效率高,时间复杂度为 O(1)。 |
- 当 field 个数比较少且没有大的 value 时,内部编码为 ziplist:
127.0.0.1:6379> hmset hashkey f1 v1 f2 v2
OK
127.0.0.1:6379> object encoding hashkey
"ziplist"
- 当有 value 大于 64 字节时,内部编码会转换为 hashtable:
127.0.0.1:6379> hset hashkey f3 "one string is bigger than 64 bytes ... 省略 ..."
OK
127.0.0.1:6379> object encoding hashkey
"hashtable"
- 当 field 个数超过 512 时,内部编码也会转换为 hashtable:
127.0.0.1:6379> hmset hashkey f1 v1 h2 v2 f3 v3 ... 省略 ... f513 v513
OK
127.0.0.1:6379> object encoding hashkey
"hashtable"
2.3 Hash 典型使用场景
2.3.1 缓存信息
相比使用 JSON 格式的字符串来缓存用户信息,哈希类型更加直观,并且在更新操作上更加灵活。通过将每个用户的 ID 作为键的后缀,并使用多个 field-value 对来表示用户的各个属性,可以实现如下的设计:
UserInfo getUserInfo(long uid) {
// 根据 uid 得到 Redis 的键
String key = "user:" + uid;
// 尝试从 Redis 中获取对应的值
Map<String, String> userInfoMap = Redis 执行命令:hgetall key;
// 如果缓存命中(hit)
if (userInfoMap != null && !userInfoMap.isEmpty()) {
// 将映射关系还原为对象形式
UserInfo userInfo = 利用映射关系构建对象(userInfoMap);
return userInfo;
}
// 如果缓存未命中(miss)
// 从数据库中,根据 uid 获取用户信息
UserInfo userInfo = MySQL 执行 SQL:select * from user_info where uid = <uid>;
// 如果表中没有 uid 对应的用户信息
if (userInfo == null) {
// 响应 404
return null;
}
// 将缓存以哈希类型保存
Redis 执行命令:hmset key name userInfo.name age userInfo.age city userInfo.city;
// 设置缓存过期时间为 1 小时(3600 秒)
Redis 执行命令:expire key 3600;
// 返回用户信息
return userInfo;
}
区别 | 描述 |
---|---|
稀疏性与结构化 | 哈希类型是稀疏的,每个键可以有不同的 field;关系型数据库是完全结构化的,新增列时所有行必须设置值(即使为 null)。 |
复杂查询能力 | 关系型数据库支持复杂的关系查询,如联表查询和聚合查询;而 Redis 模拟这些查询几乎不可能,且维护成本高。 |
截至目前,我们已经掌握了三种方法来缓存用户信息。以下将展示这三种方案的实现方式,并对其优缺点进行分析。
缓存方式 | 优点 | 缺点 |
---|---|---|
原生字符串类型 | 实现简单,针对个别属性变更非常灵活。 | 占用过多的键,内存消耗大,用户信息在 Redis 中分散,缺乏内聚性,实际使用场景较少。 |
序列化字符串类型(如 JSON) | 适合整体操作的信息,编程简单;选择合适的序列化方案可以提高内存使用效率。 | 序列化和反序列化需要一定的开销,对个别属性的操作非常不灵活。 |
哈希类型 | 简单、直观、灵活,特别适合信息的局部变更或获取操作。 | 需要管理 ziplist 和 hashtable 两种内部编码的转换,可能导致内存消耗较大。 |
- 原生字符串类型
set user:1:name James
set user:1:age 23
set user:1:city Beijing
- 序列化字符串类型
set user:1 经过序列化后的⽤⼾对象字符串
- 哈希类型
hmset user:1 name James age 23 city Beijing