A)故事背景
创建 人群包,每一个用户访问系统时,需要判断用户是否存在 人群包中。
场景条件1:人群包数量在10W~6亿数据不等(这个人群包数据大的情况下有50G左右了)。
场景条件2:每次请求处理10毫秒(ms)以内。
场景条件3:高峰每秒处理能力最大达到8W左右QPS。
B)处理方案
1、关系型数据库存储查询肯定是不能满足要求了。存储,查询性能都有问题,还容易影响别的业务。
2、最终采用Redis的 SET 数据结构来实现。
伪代码示例:
存数据:
jedisCluster.sadd(entry.getKey(), entry.getValue().toArray(new String[entry.getValue().size()]));
验证数据:
boolean flag = jedisCluster.sismember(redisKey, deviceId.toUpperCase());
C)大Key存储带来的问题
1、带来的问题1:redis 单Key是存储在固定槽位,也就是存储在集群中某个节点之中。由于单Key存储数据量比较大,随机到某个节点,一旦内存占满,【对外】直接影响其他业务对当前节点读写数据,【对内】集群之间的通信 同步数据 自然有问题,直接会影响整个集群运行。
2、带来的问题2:在3.X的版本中,由于Redis是单线程 直接删除大Key 耗时较长,占用线程资源,影响其他业务使用,拖慢整体业务,在性能要求比较高的系统 是比较可怕的事件。
D)解决方案
1、拆解大Key,分片到不同节点存储。
伪代码:
String key ="data:clean:" + Math.abs(deviceId).hashCode()%10);
这里是每个请求过来,根据时间用户唯一表示(deviceId) 随机1~10个 Key来存值,取值验证也是如此。
2、对于大Key删除,把Redis升级到4.X 使用新特性 UNLINK 命令用于执行大KEY异步删除,Redis会创建一个独立的线程删除大Key ,与其他用户使用Redis互不干扰。
网友评论