以前有两篇文章介绍了Redis中BitMap的用途和用法,有些小伙伴说这个东西好像没太大的用途,今天我给你们分享一个在实际场景中常常会碰到的状况,那就是多属性筛选
拿京东举例,以下图php
咱们要找一款电子琴,牌子有:雅马哈、卡西欧,价格有各类区间,各类颜色、不一样的音色数。mysql
现现在动不动就得整点高并发啥的,直接用mysql咱们是否是真的扛不住?在前面加一层cache?怎么加?各类属性的组合存到一个属性组合成的key中?如何相对实时的更新属性?redis
以前的文章我有介绍过redis
中setbit
和bitop
的使用方法,就是将某一位标记为1或者0表明存在不存在,而后利用bitop
进行AND
或者OR
计算,获得咱们想要的结果,今天咱们就从零开始打造一个“高性能”的属性筛选器!sql
假设如今咱们有三款电子琴,一款雅马哈、两款卡西欧,具体的属性表格为:小程序
ID | 品牌 | 颜色 | 价格 | 音色 |
---|---|---|---|---|
1 | 雅马哈 | 红色 | 1000 | 100 |
2 | 卡西欧 | 黑色 | 2000 | 150 |
3 | 卡西欧 | 白色 | 2000 | 200 |
咱们将属性+属性值组合为key,ID为对应的某位偏移量,这样使用下面的语句初始化数据到redis并发
//初始化品牌 $redis->setBit('brand-雅马哈', 1, 1); $redis->setBit('brand-卡西欧', 2, 1); $redis->setBit('brand-卡西欧', 3, 1); //初始化颜色 $redis->setBit('color-红色', 1, 1); $redis->setBit('color-黑色', 2, 1); $redis->setBit('color-白色', 3, 1); //初始化价格 $redis->setBit('price-1000', 1, 1); $redis->setBit('price-2000', 2, 1); $redis->setBit('price-2000', 3, 1); ......
我想要搜一下,2000元的白色卡西欧,只须要这样函数
$redis->bitop('AND', 'cacheKey', 'brand-卡西欧', 'color-白色'); $redis->bitop('AND', 'cacheKey1', 'cacheKey', 'price-2000');
结果cacheKey1的二进制形式为001
,这样咱们就知道搜索的结果是ID为3的商品。高并发
然而redis并无提供查询哪些位位1的方法,咱们只能经过get方法将内容获取出来,本身处理。提供一段参考代码:性能
$bit = $redis->get($cacheKey); $bitLength = strlen($bit); //redis返回的数据长度可能不是8的倍数,为了方便解包,咱们将它补齐 while($bitLength % 8 != 0) { $bitLength++; } $bit = str_pad($bit, $bitLength, pack('N', 0)); $bit = unpack('N*', $bit); $bit = array_filter($bit); $ids = []; foreach($bit as $k => $b) { $bitPos = []; while($b) { $bin = sprintf('%032s', decbin($b)); $bitPos[] = strrpos($bin, '1'); $b &= ($b - 1); } foreach($bitPos as $pos) { $ids[] = ($k - 1) * 32 + $pos; } }
我在本地试了一下,20W的数据(单个属性-属性值redis占用大概24k),同时搜索4个属性只须要不到10ms,固然现实中确定没这么理想,但效果必定不会太差。优化
若是商品和属性过多,对redis的写入压力是至关大的(商品数属性数属性值数的写入数),咱们能够先自行组合成字符串,而后单个属性-属性值对写入,具体实现细节就不写了,就是利用pack函数打包。