1.什么是BigKey和HotKey
1.1.Big Key
Redis big key problem,实际上不是大Key问题,而是Key对应的value过大,因而严格来说是Big Value问题,Redis value is too large (key value is too large)。
究竟多大的value会导致big key问题,并没有统一的规范。
例如,对于String类型的value,有时分超越5M归于big key,有时分保险起见,超越10K就能够算作Bigey。
Big Key会导致哪些问题呢?
1、因为value值很大,序列化和反序列化时刻过长,网络时延也长,然后导致操作Big Key的时分耗时很长,降低了Redis的功能。
2、在集群模式下无法做到负载均衡,导致负载倾斜到某个实例上,单实例的QPS会比较高,内存占用比较多。
3、因为Redis是单线程,假如要对这个大Key进行删去操作,被操作的实例或许会被block住,然后导致无法响应恳求。
Big Key是怎么发生的呢?
一般是程序规划者对于数据的规划意料不妥,或规划考虑遗漏导致的Big Key的发生。
在某些事务场景下,很简单发生Big Key,例如KOL或者流量明星的粉丝列表、投票的统计信息、大批量数据的缓存,等等。
1.2.Hot Key
Hot Key,也叫Hotspot Key,即热门Key。假如某个特定Key突然有许多恳求,流量集中到某个实例,甚至导致这台Redis服务器因为到达物理网卡上线而宕机,这个时分其实便是遇到了热门Key 问题。
热门key会导致许多系统问题:
1、流量过度集中,无法发挥集群优势,假如到达该实例处理上限,导致节点宕机,进而冲击数据库,有导致缓存雪崩,让整个系统挂掉的危险。
2、因为Redis是单线程操作,热门Key会影响地点示例其他Key的操作。
2.怎么发现BigKey和HotKey
2.1.发现BigKey
1、经过Redis指令查询BigKey。
以下指令能够扫描Redis的整个Key空间不同数据类型中最大的Key。-i 0.1 参数能够在扫描的时分每100次指令执行sleep 0.1 秒。
Redis自带的bigkeys的指令能够很便利的在线扫描大key,对服务的功能影响很小,单缺陷是信息较少,只要每个类型最大的Key。
$ redis-cli -p 999 --bigkeys -i 0.1
2、经过开源东西查询BigKey。
运用开源东西,长处在于获取的key信息具体、可选参数多、支持定制化需求,后续处理便利,缺陷是需要离线操作,获取成果时刻较长。
比方,redis-rdb-tools 等等。
$ git clone https://github.com/sripathikrishnan/redis-rdb-tools
$ cd redis-rdb-tools
$ sudo python setup.py install
$ rdb -c memory dump-10030.rdb > memory.csv
2.2.发现HotKey
1、hotkeys 参数
Redis 在 4.0.3 版本中添加了 hotkeys (github.com/redis/redis…)查找特性,能够直接运用 redis-cli –hotkeys 获取当前 keyspace 的热门 key,完成上是经过 scan + object freq 完成的。
2、monitor 指令
monitor 指令能够实时抓取出 Redis 服务器接收到的指令,经过 redis-cli monitor 抓取数据,一起结合一些现成的剖析东西,比方 redis-faina,统计出热 Key。
3.BigKey问题的处理办法
发现和处理BigKey问题,能够参阅以下思路:
1、在规划程序之初,预估value的巨细,在事务规划中就防止过大的value的出现。
2、经过监控的方式,尽早发现大Key。
3、假如真实无法防止大Key,那么能够将一个Key拆分为多个部分别离存储到不同的Key里。
下面以List类型的value为例,演示一下拆分处理大Key问题的办法。
有一个User Id列表,有1000万数据,假如全部存储到一个Key下面,会非常大,能够经过分页拆分的方式存取数据。
下面是存取数据的代码完成:
/**
* 将用户数据写入Redis缓存
*
* @param userIdList
*/
public void pushBigKey(List<Long> userIdList) {
// 将数据1000个一页进行拆分
int pageSize = 1000;
List<List<Long>> userIdLists = Lists.partition(userIdList, pageSize);
// 遍历所有分页,每页数据存到1个Key中,经过后缀index进行区别
Long index = 0L;
for (List<Long> userIdListPart : userIdLists) {
String pageDataKey = "user:ids:data:" + (index++);
// 运用管道pipeline,削减获取衔接次数
redisTemplate.executePipelined((RedisCallback<Long>) connection -> {
for (Long userId : userIdListPart) {
connection.lPush(pageDataKey.getBytes(), userId.toString().getBytes());
}
return null;
});
redisTemplate.expire(pageDataKey, 1, TimeUnit.DAYS);
}
// 存完数据,将数据的页数存到一个独自的Key中
String indexKey = "user:ids:index";
redisTemplate.opsForValue().set(indexKey, index.toString());
redisTemplate.expire(indexKey, 1, TimeUnit.DAYS);
}
/**
* 从Redis缓存读取用户数据
*
* @return
*/
public List<Long> popBigKey() {
String indexKey = "user:ids:index";
String indexStr = redisTemplate.opsForValue().get(indexKey);
if (StringUtils.isEmpty(indexStr)) {
return null;
}
List<Long> userIdList = new ArrayList<>();
Long index = Long.parseLong(indexStr);
for (Long i = 1L; i <= index; i++) {
String pageDataKey = "user:ids:data:" + i;
Long currentPageSize = redisTemplate.opsForList().size(pageDataKey);
List<Object> dataListFromRedisOnePage = redisTemplate.executePipelined((RedisCallback<Long>) connection -> {
for (int j = 0; j < currentPageSize; j++) {
connection.rPop(pageDataKey.getBytes());
}
return null;
});
for (Object data : dataListFromRedisOnePage) {
userIdList.add(Long.parseLong(data.toString()));
}
}
return userIdList;
}
4.HotKey问题的处理办法
假如出现了HotKey,能够考虑以下处理方案:
1、运用本地缓存。比方在服务器缓存需要恳求的热门数据,这样经过服务器集群的负载均衡,能够防止将大流量恳求到Redis。
但本地缓存会引入数据一致性问题,一起糟蹋服务器内存。
2、HotKey将复制多份,随机打散,运用代理恳求。
/**
* 将HotKey数据复制20份存储
*
* @param key
* @param value
*/
public void setHotKey(String key, String value) {
int copyNum = 20;
for (int i = 1; i <= copyNum; i++) {
String indexKey = key + ":" + i;
redisTemplate.opsForValue().set(indexKey, value);
redisTemplate.expire(indexKey, 1, TimeUnit.DAYS);
}
}
/**
* 随机从一个复制中获取一个数据
*
* @param key
* @return
*/
public String getHotKey(String key) {
int startInclusive = 1;
int endExclusive = 21;
String randomKey = key + ":" + RandomUtils.nextInt(startInclusive, endExclusive);
return redisTemplate.opsForValue().get(randomKey);
}
5.参阅资料
- 《Redis value is too large (key value is too large)》www.itworkman.com/redis-value…
- 《7 Redis Worst Practices》redis.com/blog/7-redi…
- 《Redis Hotspot Key Discovery and Common Solutions》dzone.com/articles/re…
- 《edis中什么是Big Key(大key)问题?怎么处理Big Key问题?》blog.csdn.net/Weixiaohuai…
- 《redis之bigkey(看这一篇就够)》www.cnblogs.com/szq95716/p/…
- 《Redis hot Key 发现以及处理办法》zhuanlan.zhihu.com/p/473743398
- 《京东零售 / hotkey》gitee.com/jd-platform…