談到資料結構,一定會談到 「時間複雜度」。
在電腦科學中,演算法的時間複雜度是乙個函式,它定性描述該演算法的執行時間。在 redis 中,用它來表示,基於我們處理的資料的數量,命令執行的速度將會如何。時間複雜度常用大o符號表述。
時間複雜度可被稱為是漸近的,即考察輸入值大小趨近無窮時的情況。
最快的應該是 o(1) ,乙個常量。sismember 命令,用於查詢乙個值是否屬於乙個集合,就是 o(1)。
sismember 是個強力的命令,很大乙個原因就是快。redis 中的大多數命令都是 o(1)。
o(log(n)), 是第二快的,因為它需要掃瞄的區間範圍越來越小。通過使用這種型別的切分和處理方法,乙個非常大的集合僅需要做幾次迭代就會被迅速的分解。
zadd 是乙個 o(log(n)) 命令,n 表示在有序集合中的元素個數。
o(n) 在表中查詢沒有做索引的列就是乙個 o(n) 操作。就像用 ltrim 命令一樣。但是,在 ltrim 中,n 不是列表的元素個數,而是要移除的元素的個數。
zremrangebyscore 用來從有序列表中刪除那些權重在最小值和最高值之間的元素,擁有複雜度 o(log(n)+m)。
sort 命令,的複雜度 o(n+m*log(m))
還有兩個比較常用的是 o(n^2) 和 o(c^n)。n 越大,效能越差。redis 沒有這種複雜度的命令
假如說,乙個使用者叫 users:leto 的使用者,他的 基本資訊 有 ''
如果,想通過使用者 id, 和 email 都能快速檢索到要怎麼做?
方案1:使用 string 結構,存兩份
即用 id ,和 email 作為鍵,儲存兩次 。示例:
set users:[email protected] ''
set users:9001 ''
當使用者很多時,這樣的方式絕對是個噩夢,並且它們會占用你兩倍記憶體空間。
方案2:使用雜湊結構,用字段作為偽二階索引
先用 string 結構儲存基本資訊,再用雜湊結構 將 email 作為字段,id 作為值
set users:9001 ''
hset users:lookup:email [email protected] 9001
那麼,通過 id 獲取 使用者,我們可以用普通的 get:
get users:9001
想要通過 email 來獲取使用者,我們用 hget 配合 get (ruby):
id = redis.hget('users:lookup:email', '[email protected]')
user = redis.get("users:#")
這樣的方式很節約 key 的數量。使用 偽二階索引 建立了 對映。
上面的查詢優化的例子,其實是 手工維護你的 value 之間的索引和
引用。這樣的方式很常見。
在集合中維護索引:
sadd friends:leto ghanima paul chani jessica
上面標示了 leto 有 ghanima ,paul ,chani等幾個 朋友。而 ghanima 這樣的又可以指向具體的 使用者資訊。
再跟蹤一下反向關係:
sadd friends_of:chani leto paul
上面標示了 chani 是 leto, paul的 朋友。
這些額外的索引值的處理和記憶體開銷會讓人嚇到,我們通過使用額外的查詢次數降低效能開銷。其實關係型資料庫也有一樣的開銷。
新增乙個或多個記錄
sadd 命令向集合中新增乙個或多個記錄:
sadd friends:vladimir piter
sadd friends:paul jessica leto "leto ii" chani
接收多個引數
許多命令都可以接收乙個或者多個引數,或者有乙個帶有多個引數的子查詢
ids = redis.lrange('newusers', 0, 9)
redis.mget(*ids.map "})
redis 也支援管道
通常,乙個客戶端向 redis 傳送乙個請求,然後在下次請求之前會一直等待返回。而用管道你可以傳送一堆請求卻不用等待它們的響應。這不單降低了網路開銷,也在效能上有顯著提高。
···redis.pipelined do
9001.times do
redis.incr('powerlevel')
endend
批處理會被管道加速。
redis 所有的命令都是原子性的,包括那些一次可以執行多項操作的命令也一樣。此外,在使用多命令的時候,redis 支援事務。
redis 確實是單執行緒的,這就是為什麼每個命令都是原子性的原因。事務的使用:一次只能執行乙個命令
示例:
multi
hincrby groups:1percent balance -9000000000
hincrby groups:99percent balance 9000000000
exec
** watch**
redis.watch('powerlevel')
current = redis.get('powerlevel')
redis.multi()
redis.set('powerlevel', current + 1)
redis.exec()
如果另乙個客戶端在呼叫 watch 之後,改變了 powerlevel 的話,我們的事務將會失敗。
keys 命令。該命令通過指定模式返回所有匹配的 key。這個命令看起來在某些情況下很適用,但是它絕對不應當用在產品**中。因為它為了查詢匹配的 key 會對所有的 key 做乙個線性掃瞄,它很慢。
於是,考慮用雜湊結構。就像我們可以用雜湊來暴露二級索引那樣,所以我們也可以用它來組織我們的資料:
hset bugs:1233 1 ''
hset bugs:1233 2 ''
為了取得乙個賬戶下的所有 bug 識別符號,我們只需要呼叫 hkeys bugs:1233。要刪除指定 bug 我們可以 hdel bugs:1233 2,要刪除賬戶的話我們可以通過 del bugs:1233 來刪除 key。
end
Redis學習筆記 Redis內部資料結構
redis內部資料結構 redis和其他key value資料庫的很大區別是它支援非字串型別的value值。它支援的value值的型別如下 sds dynamic string 簡單動態字串 雙端鍊錶 字典 dictionary map associative array 跳躍表 skiplist ...
《資料結構》學習筆記(4)
include using namespace std c 中有兩種函式 常規函式和成員函式 定義都包括四個部分 函式名,形式參數列,返回型別和函式體。引數傳遞的兩種方式 傳值 預設的引數傳遞方式 引用型別。使用引用方式將大大節省傳遞引數時間,並可節省儲存引數物件的副本空間。int squareby...
資料結構筆記(4)
棧與佇列 一 棧1.順序棧的實現 template class seqstack seqstack void push datatype x datatype pop datatype gettop int empty private datatype data stacksize int top ...