Redis 的 Big Key 问题
Big Key 问题
指 key 对应的 value 所占的内存空间比较大
造成的影响
- 内存空间不均匀(在 Redis Cluster 中,bigkey 会造成节点的内存空间使用不均匀)
- 超时阻塞:由于 Redis 单线程的特性,操作 bigkey 比较耗时,意味着阻塞 Redis 可能性增大
- 网络阻塞:每次获取 bigkey 产生的网络流量较大,假设一个 bigkey 为 1MB,每秒访问量为 1000,那么每秒产生 1000MB 的流量,对于普通的千兆网卡(128 MB/s)的服务器来说简直是灭顶之灾,而且一般服务器会采用单机多实例的方式来部署,也就是说一个 bigkey 可能会对其它实例造成影响
解决方案
示例
通过用户 user_id 获取对应用户的宠物信息(假设用户、宠物信息存在 Redis 中,且一个用户只有一只宠物)
方案一
用户信息与宠物信息存在一起
把所有数据存入一个集合(特殊情况下这个集合的数据量可达上万甚至几万)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| 127.0.0.1:6379>HSET user_info 666 { "id": 666, "username": "小明", "avatar": "https://abc.com/cde.png", "birthday": "1996-06-05", "sex": "男", "height": 175, "weight": 120, "hometown": "山东-济南", "education": "本科", "hobby": "跑步", "pet": { "id": 8888, "nickname": "大黄", "color": "yellow", "type": "金毛", "birthday": "2020-03-01", "sex": "公", "weight": 20 } }
# 获取对应用户的信息 127.0.0.1:6379>HGET user_info 666
|
方案二
用户信息与宠物信息分开用不同的 key 存
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
| 127.0.0.1:6379>HSET user_info 666 { "id":666, "username":"小明", "avatar":"https://abc.com/cde.png", "birthday":"1996-06-05", "sex":"男", "height": 175, "weight": 120, "hometown":"山东-济南", "education":"本科", "hobby":"跑步", "pet_id": 8888 }
127.0.0.1:6379>HSET pet_info 8888 { "id": 8888, "nickname": "大黄", "color": "yellow", "type": "金毛", "birthday":"2020-03-01", "sex":"公", "weight": 20 }
# 先获取用户对应的宠物信息 127.0.0.1:6379>HGET user_info 666 # 代码中取出宠物 ID # 再用宠物 ID 获取对应的宠物信息 127.0.0.1:6379>HGET pet_info 888
|
方案三
使用 Redis+Lua 操作
vim demo.lua
1 2 3 4 5 6 7 8 9 10
| local user_key = KEYS[1] local user_id = KEYS[2] local pet_key = KEYS[3]
local user_info = redis.call('hget', user_key, user_id) local user_info_obj = cjson.decode(user_info)
local pet_id = user_info_obj["pet_id"] local pet_info = redis.call('hget', pet_key, pet_id) return pet_info
|
1 2 3 4 5
| redis-cli script load "$(cat demo.lua)" 14d025173dddb3a23a499b61cdf0bc24b42862e4
127.0.0.1:6379>evalsha 14d025173dddb3a23a499b61cdf0bc24b42862e4 3 user_info 666 pet_info
|
执行结果如下:
1 2 3 4 5 6 7 8 9
| { "id": 8888, "nickname": "大黄", "color": "yellow", "type": "金毛", "birthday":"2020-03-01", "sex":"公", "weight": 20 }
|
Redis+Lua 说明
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| #【eval】 # 示例 1:不接收 key 127.0.0.1:6379>eval 'return "hello world";' 0 hello world
# 示例2:接收 2 个 key、1 个 value 127.0.0.1:6379>eval 'return "hello " .. KEYS[1] .. " " .. KEYS[2] .. " " .. ARGV[1]' 2 redis world abc hello redis world abc
#【evalsha】 # 示例 1: $ redis-cli script load 'return "hello world";' 7b3381f971eda3ebbc2206c2765613851930ed87 $ 127.0.0.1:6379>evalsha 0 hello world
# 示例 2: redis-cli script load 'return "hello " .. KEYS[1] .. " " .. KEYS[2] .. " " .. ARGV[1]' 659f0bd43c5320285d26e8cf6999eafdc62ca8f4 $ 127.0.0.1:6379>evalsha 659f0bd43c5320285d26e8cf6999eafdc62ca8f4 2 redis world abc hello redis world abc
|