1. 程式人生 > >初學Redis(三)用Redis作為MySQL資料庫的快取

初學Redis(三)用Redis作為MySQL資料庫的快取

        把MySQL結果集快取到Redis的字串或雜湊結構中以後,我們面臨一個新的問題,即如何為這些字串或雜湊命名,也就是如何確定它們的鍵。因為這些資料結構所對應的行都屬於某個結果集,假如可以找到一種唯一標識結果集的方法,那麼只需為這些資料結構分配一個唯一的序號,然後把結果集識別符號與該序號結合起來,就能唯一標識一個數據結構了。於是,為字串和雜湊命名的問題就轉化為確定結果集識別符號的問題。

        經過調研,發現一種較為通用的確定結果集識別符號的方法。正如我們所知道的,快取在Redis中的結果集資料都是利用select等sql語句從Mysql中獲取的。同樣的查詢語句會生成同樣的結果集(這裡暫時不討論結果集中每條記錄的順序問題),這一性質剛好可以用來確定結果集的唯一識別符號。當然,簡單地把整個sql語句作為結果集識別符號是不可取的,一個顯而易見的理由是,未經處理的sql查詢語句均包含若干空格,而Redis的鍵是不允許存在空格的。這時,我們需要一個可以把sql語句轉換為唯一識別符號的函式。通常,這一功能由雜湊函式完成,包括MD5,SHA系列等加密雜湊函式在內的很多

演算法均可達到這一目的。

        確定結果集識別符號之後,從Redis讀資料或向Redis寫資料的思路就很清晰了。對於一個sql語句格式的資料請求,首先計算該語句的MD5並據此得到結果集識別符號,然後利用該識別符號在Redis中查詢該結果集。注意,結果集中的每一行都有一個相應的鍵,這些鍵都儲存在一個Redis集合結構中。這個集合恰好對應了所需的結果集,所以,該集合的鍵必須包含結果集識別符號。如果Redis中不存在這樣一個集合,說明要找的結果集不在Redis中,所以需要執行相應的sql語句,在Mysql中查詢到相應的結果集,然後按照上面所說的辦法把結果集中的每一行以字串或雜湊的形式存入Redis。在Redis中查詢相應結果集的程式碼如下:

  1. // 該函式根據sql語句在Redis中查詢相應的結果集,並返回結果集中每一行所對應的資料結構的鍵
  2. vector<string> GetCache(sql::Connection *mysql_connection,  
  3.                       redisContext *redis_connection,  
  4.                       const string &sql, int ttl, int type) {  
  5.   vector<string> redis_row_key_vector;  
  6.   string resultset_id = md5(sql);  // 計算sql語句的md5,這是唯一標識結果集的關鍵
  7.   // type==1時,該函式將查詢相應的STRING集合或將結果集寫入若干STRING
  8.   string cache_type = (type == 1) ? "string" : "hash";  
  9.   // 根據type資訊和結果集標識符合成SET鍵
  10.   string redis_row_set_key = "resultset." + cache_type + ":" + resultset_id;  
  11.   redisReply *reply;  
  12.   // 嘗試從reply中獲取SET中儲存的所有鍵
  13.   reply = static_cast<redisReply*>(redisCommand(redis_connection,   
  14.                                                "SMEMBERS %s",  
  15.                                                redis_row_set_key.c_str()));  
  16.   if (reply->type == REDIS_REPLY_ARRAY) {  
  17.     // 如果要找的SET不存在,說明Redis中沒有相應的結果集,需要呼叫Cache2String或
  18.     // Cache2Hash函式把資料從Mysql拉取到Redis中
  19.     if (reply->elements == 0) {  
  20.       freeReplyObject(reply);  
  21.       sql::Statement *stmt = mysql_connection->createStatement();  
  22.       sql::ResultSet *resultset = stmt->executeQuery(sql);  
  23.       if (type == 1) {  
  24.         redis_row_set_key = Cache2String(mysql_connection, redis_connection,  
  25.                                          resultset, resultset_id, ttl);  
  26.       } else {  
  27.         redis_row_set_key = Cache2Hash(mysql_connection, redis_connection,   
  28.                                        resultset, resultset_id, ttl);  
  29.       }  
  30.       // 再次嘗試從reply中獲取SET中儲存的所有鍵
  31.       reply = static_cast<redisReply*>(redisCommand(redis_connection,   
  32.                                                    "SMEMBERS %s",  
  33.                                                    redis_row_set_key.c_str()));  
  34.       delete resultset;  
  35.       delete stmt;  
  36.     }  
  37.     // 把SET中的每個STRING或HASH鍵存入redis_row_key_vector中
  38.     string redis_row_key;  
  39.     for (int i = 0; i < reply->elements; ++i) {  
  40.       redis_row_key = reply->element[i]->str;  
  41.       redis_row_key_vector.push_back(redis_row_key);  
  42.     }  
  43.     freeReplyObject(reply);  
  44.   } else {  
  45.     freeReplyObject(reply);  
  46.     throw runtime_error("FAILURE - SMEMBERS error");  
  47.   }  
  48.   return redis_row_key_vector;  
  49. }  

        現在我們已經掌握了確定Redis中的結果集識別符號以及各資料結構的鍵的方法。下一篇文章將研究結果集在Redis中的排序和分頁問題。