利用redis實現多屬性快速查詢

以前有兩篇文章介紹了Redis中BitMap的用途和用法,有些小夥伴說這個東西好像沒太大的用途,今天我給你們分享一個在實際場景中常常會碰到的狀況,那就是多屬性篩選

前言

拿京東舉例,以下圖php

咱們要找一款電子琴,牌子有:雅馬哈、卡西歐,價格有各類區間,各類顏色、不一樣的音色數。mysql

現現在動不動就得整點高併發啥的,直接用mysql咱們是否是真的扛不住?在前面加一層cache?怎麼加?各類屬性的組合存到一個屬性組合成的key中?如何相對實時的更新屬性?redis

以前的文章我有介紹過redissetbitbitop的使用方法,就是將某一位標記爲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,固然現實中確定沒這麼理想,但效果必定不會太差。優化

優化setbit

若是商品和屬性過多,對redis的寫入壓力是至關大的(商品數屬性數屬性值數的寫入數),咱們能夠先自行組合成字符串,而後單個屬性-屬性值對寫入,具體實現細節就不寫了,就是利用pack函數打包。

開發了一個數獨小程序「惟一數獨」,歡迎掃描玩起來~

圖片描述

相關文章
相關標籤/搜索