改造背景
当你的业务只是公司里一块很小的业务时,就要面临一个订单列表的问题,比如我们,假设机票可以每天创建100w订单,火车票500w订单,汽车票10w订单,如果每个用户创单后都想去订单列表去看下自己的订单,那么汽车票的订单列表将面临着610w的访问,其中600w次的访问是没有返回数据的,这600w次直接请求DB的查询是不是可以避免,如何避免,今天我们就来解决这个。
确认问题
INCR
- redis从1.0.0版本就支持这个方法,时间复杂度O(1);
- 将存储在键上的数字增加1。如果键不存在,则在执行操作之前将其设置为0。如果键包含错误类型的值或包含不能表示为整数的字符串,则返回错误。此操作仅限于64位有符号整数。
- 简单的总结下:即使这个key不存在,它会在第一次执行之前先初始一个0值,返回1,如果自己初始化这个key的value,必须是整数,同时它是原子性操作,所以不必考虑线程安全的问题,这个值的修改一定是安全的,用在我们统计接口访问量就很方便了很简单了。
那么我们统计的结果是什么呢?
让时间飞一会,24小时之后......
果然不出我所料,结果是:
。
SETBIT key offset value
- redis从2.2.0版本就支持这个方法,时间复杂度O(1);
- 设置或清除键处存储的字符串值的偏移位。
- 根据值设置或清除位,值可以是0或1。
- 当键不存在时,将创建一个新的字符串值。字符串的增长,以确保它可以保持一点在偏移。偏移量参数必须大于或等于0,并且小于232(这将位图限制为512MB)。当键处的字符串增长时,添加的位被设置为0。
?
GETBIT key offset
- 返回键处存储的字符串值中偏移的位值。
- 当偏移量超过字符串长度时,假定该字符串是一个0位的连续空间。当键不存在时,它被假定为一个空字符串,因此偏移量总是超出范围,并且该值也被假定为一个0位的连续空间。
总结下,假设value值只到1000,那么你get 1001的话,会用0补齐,但并不会增加它的实际长度,这个还是很不错的,我只会根据我的set来增加长度
这个也就避免了布隆过滤器的误判率,那么就按照这样的方式搞起。
历史数据
之前我们已经把6000w的订单写入了ES,那么按照创单时间每小时查询订单,写入我们的redis,直到我创单开始刷新我的value值那一刻,这样就保证了value值里面的数据是完整的。
未来扩展
以后一些查询订单列表的场景如果是大面积的推广,就完全可以用这个先过滤一层了。
结果
节省了数据库的开销,优化了接口性能,完美,结束。
注意
- 对使用大的offset 的 [SETBIT]操作来说,内存分配可能造成 Redis 服务器被阻塞。
当生成一个很长的字符串时,Redis 需要分配内存空间,该操作有时候可能会造成服务器阻塞(block)。在2010年的Macbook Pro上,设置偏移量为 536870911(512MB 内存分配),耗费约 300 毫秒, 设置偏移量为 134217728(128MB 内存分配),耗费约 80 毫秒,设置偏移量 33554432(32MB 内存分配),耗费约 30 毫秒,设置偏移量为 8388608(8MB 内存分配),耗费约 8 毫秒。 注意若首次内存分配成功之后,再对同一个key 调用 [SETRANGE]操作,无须再重新内存。 - 虽然公司的redis是集群部署,高可用的,但是还是要预防redis挂的情况,所以要考虑一旦异常默认返回是1,保证用户能看到自己的订单