阿里雲redisCRDT產品支援說明

索酒發表於2018-09-06

CRDT支援概述

CRDT天然支援redis的幾種資料結構,下表給出一個簡單對映:

redis資料結構 CRDT資料結構
string(int或double型別編碼) counter
string register
set set
基本kv set + register
hash set + register
zset set + register
GEO set + register
hyperloglog set

然而對於redis來說,同一資料型別可能既存在CRDT中register的SET操作,又存在CRDT中counter的INCR操作。所以設計上我們考慮根據同一資料型別的不同命令劃分命令集,不同命令集CRDT的實現方式不同。

CRDT支援原則

  • 目前僅支援redis 4.0版本
  • 所有CRDT操作均針對寫(update)而言,所有的讀(query)邏輯均保持不變
  • 對同一key或同一field同時進行同一命令集內的操作才能保證key或field最終一致

    • 對同一個key同時進行不同資料型別的操作不保證最終一致

      > 例如例項A上做SET key value,例項B上做SADD key a b,無法保證最終一致
      
    • 對同一個key同時進行同一資料型別不同命令集內的操作不保證最終一致

      > 例如例項A上做SET key 1,例項B上做INCR key 2,同步之後A的key為2,B的key為1,無法保證最終一致
      
    • 除了string型別, 對其它型別的key做DEL不做最終一致保證

      > 例如例項A上做DEL setkey,例項B上SADD setkey a b,無法保證最終一致
      

CRDT演算法選擇

  • 若命令集內所有命令間均具備交換律、結合律的,直接回放操作(op-based CRDT)
  • 若命令集對應的資料型別是set,使用基於時間戳做tag的OR-Set策略
  • 其它情況使用LWW(Last write wins)策略

CRDT命令集

string

分為string,計數器(counter),位操作(bit)三類命令進行討論

string

  • CRDT典型使用場景:基礎資料型別, 應用廣泛
  • 保證命令集1: SET族 MSET MSETNX PSETEX GETSET DEL

    • 實現方式:LWW

      > 例項A上SET key a,然後在例項B上SET key b,同步之後以最新的操作為準,A和B上key均為b。
      > 例項A上SET key a,然後在例項B上SET key b;DEL key,同步之後以最新的操作為準,A和B上key均不存在。
      
  • 暫不保證: MOVE RENAME

    例項A上SET key a,同時例項B上MOVE key keyext,同步之後A上有keyext,B上有keyext和key。

  • 暫不保證: SETRANGE APPEND

    例項A進行APPEND key hello,同時例項B進行APPEND key world,最終在例項A和B上key的值可能分別為helloworld和worldhello。

counter

  • CRDT典型使用場景:計算全域性pv, 轉發數, 點贊數等
  • 保證命令集2: INCR DECR INCRBY DECRBY [INCRBYFLOAT]

    • 實現方式: op-based

      > 例項A上INCR k, 同時例項B上INCR k, 同步之後A和B上的k值均為2
      
  • 不保證:DEL SET

    在例項A上執行INCR k, 同時在例項B上執行SET k 2, 最終結果可能是A上k的值為2, B上k的值為3
    在例項A上執行INCR k, 同時在例項B上執行DEL k, 最終結果可能是A上k不存在, B上k的值為1

  • INCRBYFLOAT 會存在浮點數計算本身的精度差異

bit

  • 暫不保證

set

  • CRDT典型使用場景: 購物車,收藏夾
  • 保證命令集3: SADD SREM SPOP

    • 實現方式:OR-Set

      > 例項A上SADD key a, 同時例項B上SADD key b, 同步之後A和B上key均有a, b兩個fields
      
  • 暫不保證:SMOVE SINTERSTORE SUNIONSTORE SDIFFSTORE

hash

  • CRDT典型使用場景: 使用者,網站或應用的全域性session資訊
  • 保證命令集4:HSET HMSET HSETNX HDEL

    • 實現方式: OR-Set
  • 保證命令集5:HINCRBY

    • 實現方式:op-based

      > 同理, HSET或HDEL和HINCRBY在不同例項上同時操作不能保證最終一致
      
  • HINCRBYFLOAT 同樣會存在浮點數精度差異

list

  • 暫不支援

hyperloglog

  • CRDT典型使用場景:統計全域性的近似uv
  • 保證命令集6:PFADD

    • 實現方式:op-based
  • 暫不保證:PFMERGE
  • hll在redis中儲存為string型別的物件, 所以原則上string型別的所有操作均可作用於hll之上, 但不建議對hll使用string型別的操作, 不僅保證不了最終一致, 還會破壞hll本身的正確性

zset

  • CRDT典型使用場景:使用者帶有時間序列資訊, 如timeline
  • 保證命令集7:ZADD NX|XX|CH ZREM

    • 實現方式:OR-Set
  • 保證命令集8:ZADD INCR ZINCRBY

    • 實現方式:op-based
  • 暫不保證:ZINTERSTORE ZUNIONSTORE ZREMRANGEBYRANK ZREMRANGEBYSCORE ZREMRANGEBYLEX

geo

  • CRDT典型使用場景:全域性地理位置資訊
  • 保證命令集9:GEOADD

    • 實現方式:OR-Set
  • geo型別資料底層在redis中使用zset實現,所以原則上zset型別的所有操作均可作用於geo之上,但不建議對geo使用zset型別操作

其它redis特性支援

rdb

  • 在rdb中儲存crdt相關元資訊,保證下次載入之後滿足一致性,同時對開源redis 4.0及阿里雲redis 4.0其它產品形態保持相容

expire

  • 對於expire的時間設定不保證最終一致,原則上以設定的最短過期時間為準,會分發DEL操作。

evict

  • 記憶體處於高水位的特殊情況,直接根據具體設定策略逐出,目前不做最終一致保證

lua

  • 支援lua指令碼中執行的命令

實現代價

  • 效能上無影響
  • 每個key或field將多佔用8個位元組儲存crdt相關元資訊,未來可以壓縮到4個位元組
  • set和hash型別只支援 OBJ_ENCODING_HT 編碼,zset型別只支援 OBJ_ENCODING_SKIPLIST 編碼,以下幾個配置項將不再起作用:
    set_max_intset_entries

hash_max_ziplist_entries hash_max_ziplist_value
zset_max_ziplist_entries zset_max_ziplist_value

  • rdb將額外佔用空間儲存crdt元資訊,但保證相容性


相關文章