初學Redis(4)——簡單實現Redis緩衝中的排序功能,redis排序功能

來源:互聯網
上載者:User

初學Redis(4)——簡單實現Redis緩衝中的排序功能,redis排序功能

        在實現緩衝排序功能之前,必須先明白這一功能的合理性。不妨思考一下,既然可以在資料庫中排序,為什麼還要把排序功能放在緩衝中實現呢?這裡簡單總結了兩個原因:首先,排序會增加資料庫的負載,難以支撐高並發的應用;其次,在緩衝中排序不會遇到表鎖定的問題。Redis恰好提供了排序功能,使我們可以方便地實現緩衝排序。

        Redis中用於實現排序功能的是SORT命令。該命令提供了多種參數,可以對列表,集合和有序集合進行排序。SORT命令格式如下:

SORT key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern ...]] [ASC | DESC] [ALPHA] [STORE destination]

        BY參數用於指定排序欄位,功能類似於SQL中的order by。對於列表和集合而言,僅按照它們的值進行排序往往沒有實際意義。以函數Cache2Hash返回的集合為例(實際上返回的是集合鍵),該集合中儲存的是一系列完整的雜湊鍵,只按照這些鍵進行排序,結果無非是按照數字或字典順序排列,其用處顯然不大。這是因為真正儲存行資料的是雜湊結構本身,而非雜湊鍵。假設集合鍵為"resultset.hash:123456",集合中每個雜湊鍵對應的雜湊結構中都有一個名為“timestamp”的欄位,現在要把集合中的所有雜湊鍵按照timestamp欄位進行排序,這時,只需執行以下命令:

SORT resultset.hash:123456 BY *->timestamp

        從上例可以看出,BY的真正威力在於它可以讓SORT命令按照一個指定的外部鍵的外部欄位進行排序。SORT用集合resultset.hash:123456中的每個值(即每個雜湊鍵)替換BY參數後的第一個“*”,並依據“->”後面給出的欄位擷取其值,最後根據這些欄位值對雜湊鍵進行排序。

        LIMIT參數用於限制排序以後返回元素的數量,功能類似於SQL中的limit。該參數接受另外兩個參數,即offset和count,LIMIT offset count表示跳過前offset個元素,返回之後的連續count個元素。可見,LIMIT參數可以用於實現分頁功能。

        GET參數用於返回指定的欄位值。以集合resultset.hash:123456為例,使用BY參數對集合中的所有雜湊鍵按照雜湊結構中的timestamp欄位排序後,SORT命令返回所有排序之後的雜湊鍵。如果某個請求需要不是鍵而是某些欄位值,這時就要使用GET參數,使SORT命令返回指定欄位值。假設除timestamp欄位以外,集合中每個雜湊鍵對應的雜湊結構中還有一個名為“id”的欄位,通過以下命令可以使SORT返回按照timestamp排序以後的每個雜湊鍵對應的雜湊結構中的timestamp和id值:

SORT resultset.hash:123456 BY *->timestamp GET *->timestamp GET *->id

        SORT用集合resultset.hash:123456中的每個值(即每個雜湊鍵)替換GET參數之後的第一個“*”,並將其作為返回值。值得注意的是,利用GET #能夠得到集合中的雜湊鍵本身。

        ASC和DESC參數用於指定排序次序(預設為ASC,即從低到高),ALPHA參數用於按照字典順序排列非數字元素。

        STORE參數用於將SORT命令的返回值,即排序結果存入一個指定的列表。加上STORE參數後,SORT命令的返回值就變為排序結果的個數。

        下面的代碼實現了按照雜湊的某個欄位對集合中的雜湊鍵排序,並將結果存入列表的過程:

// 該函數對集合中的所有HASH鍵進行排序,排序依據是HASH鍵所對應的HASH中的某個欄位,// 排序結果被存入一個LIST結構,LIST鍵應當包含結果集標識符和排序欄位標識符,// 形如“sorted:123456:1234”string SortHash(sql::Connection *mysql_connection,                redisContext *redis_connection,                 const string &resultset_id,                 const string &sort_field,                 int offset, int count, int order, int ttl) {  // 只考慮儲存HASH鍵的SET  string redis_row_set_key = "resultset.hash:" + resultset_id;  redisReply *reply;  // 檢測SET是否存在  reply = static_cast<redisReply*>(redisCommand(redis_connection,                                                "EXISTS %s",                                               redis_row_set_key.c_str()));  if (reply->integer == 0) {    freeReplyObject(reply);    throw runtime_error("FAILURE - no resultsets");  } else {    freeReplyObject(reply);  }  string field_md5 = md5(sort_field);  // 利用MD5排除排序欄位中空格造成的影響   // 將排序結果存入該LIST  string redis_sorted_list_key = "sorted:" + resultset_id + ":" + field_md5;  string by("*->" + sort_field);  //確定排序欄位  string ord = (order == 1) ? "ASC" : "DESC";  //order==1時按照升序排列;否則為降序  stringstream ofsstream, cntstream;  ofsstream << offset;  cntstream << count;  // 執行排序命令,並把排序結果存入LIST  reply = static_cast<redisReply*>(redisCommand(                                      redis_connection,                                       "SORT %s BY %s LIMIT %s %s GET %s ALPHA STORE %s",                                      redis_row_set_key.c_str(),                                       by.c_str(),                                       ofsstream.str().c_str(),                                       cntstream.str().c_str(),                                       "#",                                       redis_sorted_list_key.c_str()));  freeReplyObject(reply);  stringstream ttlstream;  ttlstream << ttl;  // 設定LIST的到期時間  reply = static_cast<redisReply*>(redisCommand(redis_connection,                                                "EXPIRE %s %s",                       redis_sorted_list_key.c_str(),                                                ttlstream.str().c_str()));  freeReplyObject(reply);  return redis_sorted_list_key;  // 返回LIST鍵,以便於其他函數擷取該LIST中的內容

        顯然,對結果集中的雜湊鍵進行排序要比對字串鍵排序更加直觀和方便。藉助於排序函數,可以方便地實現在Redis中查詢排序後的結果集,代碼如下:

// 該函數根據sql語句和排序參數,在Redis中查詢相應的結果集並進行排序,最後返回// 排序之後的HASH鍵vector<string> GetSortedCache(sql::Connection *mysql_connection,                              redisContext *redis_connection,                              const string &sql, const string &sort_field,                               int offset, int count, int order, int ttl) {  vector<string> redis_row_key_vector;  redisReply *reply;  string resultset_id = md5(sql);  // 結果集標識符  string field_md5 = md5(sort_field);  // 排序欄位標識符  // 嘗試擷取LIST中的所有HASH鍵  string redis_sorted_list_key = "sorted:" + resultset_id + ":" + field_md5;  // 嘗試擷取LIST中的所有HASH鍵  reply = static_cast<redisReply*>(redisCommand(redis_connection,                                                "LRANGE %s %s %s",                                               redis_sorted_list_key.c_str(),                                                "0",                                                "-1"));  if (reply->type == REDIS_REPLY_ARRAY) {    // 如果LIST不存在,調用Cache2Hash函數從Mysql中拉取資料到Redis,然後調用SortHash函數    // 對結果集進行排序並將排序後的HASH鍵存入LIST    if (reply->elements == 0) {       freeReplyObject(reply);      sql::Statement *stmt = mysql_connection->createStatement();      sql::ResultSet *resultset = stmt->executeQuery(sql);      Cache2Hash(mysql_connection, redis_connection, resultset,                  resultset_id, ttl);      redis_sorted_list_key = SortHash(mysql_connection, redis_connection,                                        resultset_id, sort_field, offset,                                        count, order, ttl);      // 再次嘗試擷取LIST中的所有HASH鍵      reply = static_cast<redisReply*>(redisCommand(                                          redis_connection,                                           "LRANGE %s %s %s",                                          redis_sorted_list_key.c_str(),                                           "0",                                           "-1"));      delete resultset;      delete stmt;    }    // 將LIST中的所有HASH鍵存入redis_row_key_vector中    string redis_row_key;    for (int i = 0; i < reply->elements; ++i) {      redis_row_key = reply->element[i]->str;      redis_row_key_vector.push_back(redis_row_key);    }    freeReplyObject(reply);  } else {    freeReplyObject(reply);    throw runtime_error("FAILURE - LRANGE error");  }  return redis_row_key_vector;}

        這樣,在Redis中對結果集進行簡單排序操作的功能就實現了。


用redis來讀取快取資料怎做

是用C編程嗎?可以用hiredis庫。
 
在使用Redis 做緩衝的時,set寫入中文,get 出來是\xe6\xb5\x8b\xe8\xaf\x95問怎

沒碰到過,估計是轉碼有誤,
Redis寫入預設是用js序列化,如果寫入的是byte[],就不序列化直接存入,
建議你寫入前自己序列化為byte[],
讀取時,再自己還原序列化

我目前就是這麼做的
 

相關文章

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.