本文章介紹redis sorted sets操作學習筆記,有需要了解的同學可參考。
sorted set是set的一個升級版本,它在set的基礎上增加了一個順序屬性,這一屬性在添加
修改元素的時候可以指定,每次指定後,zset會自動重新按新的值調整順序。可以理解為有
兩列的mysql表,一列存value,一列存順序。操作中key理解為zset的名字。
和set一樣sorted set也是string類型元素的集合,不同的是每個元素都會關聯一個double
類型的score。sorted set的實現是skip list和hash table的混合體。
當元素被添加到集合中時,一個元素到score的映射被添加到hash table中,所以給定一個
元素獲取score的開銷是O(1),另一個score到元素的映射被添加到skip list,並按照score排
序,所以就可以有序的獲取集合中的元素。添加,刪除操作開銷都是O(log(N))和skip list的
開銷一致,redis 的skip list實現用的是雙向鏈表,這樣就可以逆序從尾部取元素。sorted set最
經常的使用方式應該是作為索引來使用.我們可以把要排序的字段作為score存儲,對象的id
當元素存儲.
下面講一個使用 Sorted Sets 的例子
mysql中有一張表,假設名字為 summary_data吧,記錄數為30M左右,
有一個字段first_path 為varchar(256),需要找到出現次數最多的10個first_path。
方法一 ) 直接sql語句
sql語句很好寫:
代碼如下 復制代碼 SELECT first_path, COUNT(*) AS c FROM summary_data GROUP BY first_path ORDER BY c DESC LIMIT 10;表上面是有索引的, 但是索引的長度為 KEY `first_path` (`first_path`(255)), 也許是這個原因導致了無法使用索引:
id: 1
select_type: SIMPLE
table: summary_data
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 28136948
Extra: Using temporary; Using filesort
這條sql運行了9分鐘。
把first_path都導出來,生成文件 input/first_path, 每行一條記錄,話說這個導出的過程真的很慢。
方法二 ) sort 與 uniq
sort input/first_path | uniq -c |sort -nr | head -n 10
排好序的狀態,就是分組的狀態了。
uniq -c 用來統計每組的數量。
sort -nr 再對統計結果進行降序
一分半鐘搞定。
方法三 ) redis的sorted set
用這種方法,只因為突然想到sorted set就是為此而生的嘛。
client libary 准備用 hiredis.
安裝很簡單: make && make install
可以看到庫文件安裝到了 /usr/local/lib/ 目錄
頭文件安裝到了 /usr/local/include/hiredis/ 目錄
需要把 /usr/local/lib/ 添加到 /etc/ld.so.conf
然後ldconfig
編譯一下:
代碼如下 復制代碼gcc -I /usr/local/include/hiredis -lhiredis ./example.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <hiredis.h>
int main(int argc, char **argv) {
unsigned int j;
redisContext *c;
redisReply *reply;
const char *hostname = (argc > 1) ? argv[1] : "127.0.0.1";
int port = (argc > 2) ? atoi(argv[2]) : 6379;
struct timeval timeout = { 1, 500000 }; // 1.5 seconds
c = redisConnectWithTimeout(hostname, port, timeout);
if (c == NULL || c->err) {
if (c) {
printf("Connection error: %sn", c->errstr);
redisFree(c);
} else {
printf("Connection error: can't allocate redis contextn");
}
exit(1);
}
FILE * fp;
fp = fopen(argv[3], "r");
if (!fp) exit(1);
char line[256];
while(fgets(line, sizeof(line)-1, fp ) != NULL) {
reply = redisCommand(c, "ZINCRBY myzset_firstpath 1 %s" , line);
freeReplyObject(reply);
}
fclose(fp);
reply = redisCommand(c, "ZREVRANGEBYSCORE myzset_firstpath +inf -inf WITHSCORES LIMIT 0 10");
if (reply->type == REDIS_REPLY_ARRAY) {
for (j = 0; j < reply->elements; j+=2) {
printf("%u) %s %sn", (unsigned int)(j/2), reply->element[j]->str, reply->element[j+1]->str);
}
}
freeReplyObject(reply);
/* Disconnects and frees the context */
redisFree(c);
return 0;
}
16分鐘出結果, not good enough。