redis 使用GETRANGE 來獲取一組bitmap狀態

luw2007發表於2017-05-22

在使用 redis 的 bitmap 來儲存業務狀態。經常需要順序獲取一個範圍內 bitmap。 業務程式碼裡,一般會使用 pipeline 來優化查詢邏輯。虛擬碼如下

def get_bits_pipe(pipe, key, cur, num=10):
    """ 使用pipeline gitbit"""
    for i in xrange(num + 1):
        pipe.getbit(key, cur + i)
    return [cur + i for i, v in enumerate(pipe.execute()) if v]

這裡減少了和 redis 的資料交換。提高了查詢效能,但是隨著查詢數量的增加,效能急劇下降。

其實 redis 有更高效的方式來獲取順序的 bitmap。就是通過 getrange 來獲取 bitmap 所在的字串,然後計算出每位的值。需要注意:由於 redis 的 bit 並非按照自然二進位制位增加, 比如:'\x01' 對應的 ascii 為 1。其二進位制表示'1', 在 redis 中表示 offset 為 7。感興趣可以看看 redis 的實現邏輯。

以下提供 golang 和 python 版本的樣例。 實現程式碼: https://gist.github.com/luw2007/692d4a615dd71aa2bfa42190ad6a12e3

var nums = [8]uint8{1, 2, 4, 8, 16, 32, 64, 128}

// BitRange 計算下標表
// str: 計算的字串
// start: 開始的座標
// offset: 偏移值
// size: 查詢個數
func BitRange(str []byte, start int, offset int, size int) []int {
    bits := []int{}
    k := 0
    for i, b := range str {
        for j, num := range nums {
            if b&num != num {
                continue
            }
            k = int(i*8 + 7 - j)
            if offset <= k && k < offset+size {
                bits = append(bits, start*8+k)
            }

        }
    }
    return bits
}

// GetBitRange 按位查詢 返回bit位為1的下標
// client: redis的client
// key: redis 儲存的key
// cur: 開始位置
// size: 查詢個數
func (p *Pool) BitRange(key string, cur int, size int) ([]int, error) {
    start := cur / 8
    // end必須按8取整
    end := (cur+size+7)/8
    str, err := r.Bytes(p.ExecuteCMD("GETRANGE", key, start, end))
    if err != nil {
        return nil, err
    }
    bits := BitRange(str, start, cur%8, size)
    return bits, nil
}
更多原創文章乾貨分享,請關注公眾號
  • redis 使用GETRANGE 來獲取一組bitmap狀態
  • 加微信實戰群請加微信(註明:實戰群):gocnio

相關文章