前言
用Redis作Mysql數(shù)據(jù)庫(kù)緩存,必須解決2個(gè)問(wèn)題。首先,應(yīng)該確定用何種數(shù)據(jù)結(jié)構(gòu)存儲(chǔ)來(lái)自Mysql的數(shù)據(jù);在確定數(shù)據(jù)結(jié)構(gòu)之后,還要考慮用什么標(biāo)識(shí)作為該數(shù)據(jù)結(jié)構(gòu)的鍵。
直觀上看,Mysql中的數(shù)據(jù)都是按表存儲(chǔ)的;更微觀地看,這些表都是按行存儲(chǔ)的。每執(zhí)行一次select查詢(xún),Mysql都會(huì)返回一個(gè)結(jié)果集,這個(gè)結(jié)果集由若干行組成。所以,一個(gè)自然而然的想法就是在Redis中找到一種對(duì)應(yīng)于Mysql行的數(shù)據(jù)結(jié)構(gòu)。Redis中提供了五種基本數(shù)據(jù)結(jié)構(gòu),即字符串(string)、列表(list)、哈希(hash)、集合(set)和有序集合(sorted set)。經(jīng)過(guò)調(diào)研,發(fā)現(xiàn)適合存儲(chǔ)行的數(shù)據(jù)結(jié)構(gòu)有兩種,即string和hash。
要把Mysql的行數(shù)據(jù)存入string,首先需要對(duì)行數(shù)據(jù)進(jìn)行格式化。事實(shí)上,結(jié)果集的每一行都可以看做若干由字段名和其對(duì)應(yīng)值組成的鍵值對(duì)集合。這種鍵值對(duì)結(jié)構(gòu)很容易讓我們想起Json格式。因此,這里選用Json格式作為結(jié)果集每一行的格式化模板。根據(jù)這一想法,我們可以實(shí)現(xiàn)將結(jié)果集格式化為若干Json對(duì)象,并將Json對(duì)象轉(zhuǎn)化為字符串存入Redis的代碼:
view sourceprint?
01.
// 該函數(shù)把結(jié)果集中的每一行轉(zhuǎn)換為一個(gè)Json格式的字符串并存入Redis的STRING結(jié)構(gòu)中,
02.
// STRING鍵應(yīng)該包含結(jié)果集標(biāo)識(shí)符和STRING編號(hào),形式如“cache.string:123456:1”
03.
string Cache2String(sql::Connection *mysql_connection,
04.
redisContext *redis_connection,
05.
sql::ResultSet *resultset,
06.
const string &resultset_id, int ttl) {
07.
if (resultset->rowsCount() == 0) {
08.
throw runtime_error(“FAILURE - no rows”);
09.
}
10.
// STRING鍵的前綴,包含了結(jié)果集的標(biāo)識(shí)符
11.
string prefix(“cache.string:” + resultset_id + “:”);
12.
unsigned int num_row = 1; // STRING編號(hào),附加于STRING鍵的末尾,從1開(kāi)始
13.
sql::ResultSetMetaData *meta = resultset->getMetaData();
14.
unsigned int num_col = meta->getColumnCount();
15.
// 將結(jié)果集中所有行對(duì)應(yīng)的所有STRING鍵存入該SET,SET鍵包含了結(jié)果集的標(biāo)識(shí)符
16.
string redis_row_set_key(“resultset.string:” + resultset_id);
17.
redisReply *reply;
18.
string ttlstr;
19.
stringstream ttlstream;
20.
ttlstream << ttl;
21.
ttlstr = ttlstream.str();
22.
resultset->beforeFirst();
23.
// 將結(jié)果集中的每一行轉(zhuǎn)為Json格式的字符串,將這些Json字符串存入STRING,
24.
// 每個(gè)STRING對(duì)應(yīng)結(jié)果集中的一行
25.
while (resultset->next()) {
26.
string redis_row_key; // STRING鍵名,由前綴和STRING編號(hào)組成
27.
stringstream keystream;
28.
	keystream < 
	29. 
	redis_row_key = keystream.str(); 
	30. 
	Json::Value row; 
	31. 
	for (int i = 1; i <= num_col; ++i) { 
	32. 
	string col_label = meta->getColumnLabel(i); 
	33. 
	string col_value = resultset->getString(col_label); 
	34. 
	row[col_label] = col_value; 
	35. 
	} 
	36. 
	Json::FastWriter writer; 
	37. 
	string redis_row_value = writer.write(row); 
	38. 
	// 將STRING鍵及Json格式的對(duì)應(yīng)值對(duì)存入Redis 
	39. 
	reply = static_cast 
	40. 
	“SET %s %s”, 
	41. 
	redis_row_key.c_str(), 
	42. 
	redis_row_value.c_str())); 
	43. 
	freeReplyObject(reply); 
	44. 
	// 將STRING鍵加入SET中 
	45. 
	reply = static_cast 
	46. 
	“SADD %s %s”, 
	47. 
	redis_row_set_key.c_str(), 
	48. 
	redis_row_key.c_str())); 
	49. 
	freeReplyObject(reply); 
	50. 
	// 設(shè)置STRING的過(guò)期時(shí)間 
	51. 
	reply = static_cast 
	52. 
	“EXPIRE %s %s”, 
	53. 
	redis_row_key.c_str(), 
	54. 
	ttlstr.c_str())); 
	55. 
	freeReplyObject(reply); 
	56. 
	++num_row; 
	57. 
	} 
	58. 
	// 設(shè)置SET的過(guò)期時(shí)間 
	59. 
	reply = static_cast 
	60. 
	“EXPIRE %s %s”, 
	61. 
	redis_row_set_key.c_str(), 
	62. 
	ttlstr.c_str())); 
	63. 
	freeReplyObject(reply); 
	64. 
	return redis_row_set_key; // 返回SET鍵,以便于其他函數(shù)獲取該SET中的內(nèi)容 
	65. 
	} 
	要把Mysql的行數(shù)據(jù)存入hash,過(guò)程要比把數(shù)據(jù)存入string直觀很多。這是由hash的結(jié)構(gòu)性質(zhì)決定的——hash本身就是一個(gè)鍵值對(duì)集合:一個(gè)“父鍵”下面包含了很多“子鍵”,每個(gè)“子鍵”都對(duì)應(yīng)一個(gè)值。根據(jù)前面的分析可知,結(jié)果集中的每一行實(shí)際上也是鍵值對(duì)集合。用Redis鍵值對(duì)集合表示Mysql鍵值對(duì)集合應(yīng)該再合適不過(guò)了:對(duì)于結(jié)果集中的某一行,字段對(duì)應(yīng)于hash的“子鍵”,字段對(duì)應(yīng)的值就是hash“子鍵”對(duì)應(yīng)的值,即結(jié)果集的一行剛好對(duì)應(yīng)一個(gè)hash。這一想法的實(shí)現(xiàn)代碼如下: 
	view sourceprint? 
	01. 
	// 該函數(shù)把結(jié)果集中的每一行都存入一個(gè)HASH結(jié)構(gòu)。HASH鍵應(yīng)當(dāng)包括結(jié)果集標(biāo)識(shí)符和HASH編號(hào), 
	02. 
	// 形如“cache.string:123456:1” 
	03. 
	string Cache2Hash(sql::Connection *mysql_connection, 
	04. 
	redisContext *redis_connection, 
	05. 
	sql::ResultSet *resultset, 
	06. 
	const string &resultset_id, int ttl) { 
	07. 
	if (resultset->rowsCount() == 0) { 
	08. 
	throw runtime_error(“FAILURE - no rows”); 
	09. 
	} 
	10. 
	// HASH鍵的前綴,包含了結(jié)果集的標(biāo)識(shí)符 
	11. 
	string prefix(“cache.hash:” + resultset_id + “:”); 
	12. 
	unsigned int num_row = 1; // HASH編號(hào),附加于HASH鍵的末尾,從1開(kāi)始 
	13. 
	sql::ResultSetMetaData *meta = resultset->getMetaData(); 
	14. 
	unsigned int num_col = meta->getColumnCount(); 
	15. 
	// 將結(jié)果集中所有行對(duì)應(yīng)的所有HASH鍵存入該SET,SET鍵包含了結(jié)果集的標(biāo)識(shí)符 
	16. 
	string redis_row_set_key(“resultset.hash:” + resultset_id); 
	17. 
	redisReply *reply; 
	18. 
	string ttlstr; 
	19. 
	stringstream ttlstream; 
	20. 
	ttlstream << ttl; 
	21. 
	ttlstr = ttlstream.str(); 
	22. 
	// 結(jié)果集中的每一行對(duì)應(yīng)于一個(gè)HASH,將結(jié)果集的所有行都存入相應(yīng)HASH中 
	23. 
	resultset->beforeFirst(); 
	24. 
	while (resultset->next()) { 
	25. 
	string redis_row_key; // HASH鍵名,由前綴和HASH編號(hào)組成 
	26. 
	stringstream keystream; 
	27. 
	keystream << prefix << num_row; 
	28. 
	redis_row_key = keystream.str(); 
	29. 
	for (int i = 1; i <= num_col; ++i) { 
	30. 
	string col_label = meta->getColumnLabel(i); 
	31. 
	string col_value = resultset->getString(col_label); 
	32. 
	// 將結(jié)果集中一行的字段名和對(duì)應(yīng)值存入HASH 
	33. 
	reply = static_cast 
	34. 
	“HSET %s %s %s”, 
	35. 
	redis_row_key.c_str(), 
	36. 
	col_label.c_str(), 
	37. 
	col_value.c_str())); 
	38. 
	freeReplyObject(reply); 
	39. 
	} 
	40. 
	// 將HASH鍵加入SET中 
	41. 
	reply = static_cast 
	42. 
	“SADD %s %s”, 
	43. 
	redis_row_set_key.c_str(), 
	44. 
	redis_row_key.c_str())); 
	45. 
	freeReplyObject(reply); 
	46. 
	// 設(shè)置HASH的過(guò)期時(shí)間 
	47. 
	reply = static_cast 
	48. 
	“EXPIRE %s %s”, 
	49. 
	redis_row_key.c_str(), 
	50. 
	ttlstr.c_str())); 
	51. 
	freeReplyObject(reply); 
	52. 
	++num_row; 
	53. 
	} 
	54. 
	// 設(shè)置SET的過(guò)期時(shí)間 
	55. 
	reply = static_cast 
	56. 
	“EXPIRE %s %s”, 
	57. 
	redis_row_set_key.c_str(), 
	58. 
	ttlstr.c_str())); 
	59. 
	freeReplyObject(reply); 
	60. 
	return redis_row_set_key; // 返回SET鍵,以便于其他函數(shù)獲取該SET中的內(nèi)容 
	61. 
	} 
	至此,我們已經(jīng)給出了兩種存儲(chǔ)Mysql結(jié)果集的方案,這就是我們?cè)谄滋岢龅牡谝粋€(gè)問(wèn)題,即選擇何種數(shù)據(jù)結(jié)構(gòu)存儲(chǔ)Mysql結(jié)果集的答案。 
	 
	把Mysql結(jié)果集緩存到Redis的字符串或哈希結(jié)構(gòu)中以后,我們面臨一個(gè)新的問(wèn)題,即如何為這些字符串或哈希命名,也就是如何確定它們的鍵。因?yàn)檫@些數(shù)據(jù)結(jié)構(gòu)所對(duì)應(yīng)的行都屬于某個(gè)結(jié)果集,假如可以找到一種唯一標(biāo)識(shí)結(jié)果集的方法,那么只需為這些數(shù)據(jù)結(jié)構(gòu)分配一個(gè)唯一的序號(hào),然后把結(jié)果集標(biāo)識(shí)符與該序號(hào)結(jié)合起來(lái),就能唯一標(biāo)識(shí)一個(gè)數(shù)據(jù)結(jié)構(gòu)了。于是,為字符串和哈希命名的問(wèn)題就轉(zhuǎn)化為確定結(jié)果集標(biāo)識(shí)符的問(wèn)題。 
	經(jīng)過(guò)調(diào)研,發(fā)現(xiàn)一種較為通用的確定結(jié)果集標(biāo)識(shí)符的方法。正如我們所知道的,緩存在Redis中的結(jié)果集數(shù)據(jù)都是利用select等sql語(yǔ)句從Mysql中獲取的。同樣的查詢(xún)語(yǔ)句會(huì)生成同樣的結(jié)果集(這里暫時(shí)不討論結(jié)果集中每條記錄的順序問(wèn)題),這一性質(zhì)剛好可以用來(lái)確定結(jié)果集的唯一標(biāo)識(shí)符。當(dāng)然,簡(jiǎn)單地把整個(gè)sql語(yǔ)句作為結(jié)果集標(biāo)識(shí)符是不可取的,一個(gè)顯而易見(jiàn)的理由是,未經(jīng)處理的sql查詢(xún)語(yǔ)句均包含若干空格,而Redis的鍵是不允許存在空格的。這時(shí),我們需要一個(gè)可以把sql語(yǔ)句轉(zhuǎn)換為唯一標(biāo)識(shí)符的函數(shù)。通常,這一功能由散列函數(shù)完成,包括MD5,SHA系列等加密散列函數(shù)在內(nèi)的很多算法均可達(dá)到這一目的。 
	確定結(jié)果集標(biāo)識(shí)符之后,從Redis讀數(shù)據(jù)或向Redis寫(xiě)數(shù)據(jù)的思路就很清晰了。對(duì)于一個(gè)sql語(yǔ)句格式的數(shù)據(jù)請(qǐng)求,首先計(jì)算該語(yǔ)句的MD5并據(jù)此得到結(jié)果集標(biāo)識(shí)符,然后利用該標(biāo)識(shí)符在Redis中查找該結(jié)果集。注意,結(jié)果集中的每一行都有一個(gè)相應(yīng)的鍵,這些鍵都存儲(chǔ)在一個(gè)Redis集合結(jié)構(gòu)中。這個(gè)集合恰好對(duì)應(yīng)了所需的結(jié)果集,所以,該集合的鍵必須包含結(jié)果集標(biāo)識(shí)符。如果Redis中不存在這樣一個(gè)集合,說(shuō)明要找的結(jié)果集不在Redis中,所以需要執(zhí)行相應(yīng)的sql語(yǔ)句,在Mysql中查詢(xún)到相應(yīng)的結(jié)果集,然后按照上面所說(shuō)的辦法把結(jié)果集中的每一行以字符串或哈希的形式存入Redis。在Redis中查找相應(yīng)結(jié)果集的代碼如下: 
	view sourceprint? 
	01. 
	// 該函數(shù)根據(jù)sql語(yǔ)句在Redis中查詢(xún)相應(yīng)的結(jié)果集,并返回結(jié)果集中每一行所對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu)的鍵 
	02. 
	vector 
	03. 
	redisContext *redis_connection, 
	04. 
	const string &sql, int ttl, int type) { 
	05. 
	vector 
	06. 
	string resultset_id = md5(sql); // 計(jì)算sql語(yǔ)句的md5,這是唯一標(biāo)識(shí)結(jié)果集的關(guān)鍵 
	07. 
	// type==1時(shí),該函數(shù)將查詢(xún)相應(yīng)的STRING集合或?qū)⒔Y(jié)果集寫(xiě)入若干STRING 
	08. 
	string cache_type = (type == 1) ? “string” : “hash”; 
	09. 
	// 根據(jù)type信息和結(jié)果集標(biāo)識(shí)符合成SET鍵 
	10. 
	string redis_row_set_key = “resultset.” + cache_type + “:” + resultset_id; 
	11. 
	redisReply *reply; 
	12. 
	// 嘗試從reply中獲取SET中保存的所有鍵 
	13. 
	reply = static_cast 
	14. 
	“SMEMBERS %s”, 
	15. 
	redis_row_set_key.c_str())); 
	16. 
	if (reply->type == REDIS_REPLY_ARRAY) { 
	17. 
	// 如果要找的SET不存在,說(shuō)明Redis中沒(méi)有相應(yīng)的結(jié)果集,需要調(diào)用Cache2String或 
	18. 
	// Cache2Hash函數(shù)把數(shù)據(jù)從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 
	32. 
	“SMEMBERS %s”, 
	33. 
	redis_row_set_key.c_str())); 
	34. 
	delete resultset; 
	35. 
	delete stmt; 
	36. 
	} 
	37. 
	// 把SET中的每個(gè)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. 
	} 
	現(xiàn)在我們已經(jīng)掌握了確定Redis中的結(jié)果集標(biāo)識(shí)符以及各數(shù)據(jù)結(jié)構(gòu)的鍵的方法。下一篇文章將研究結(jié)果集在Redis中的排序和分頁(yè)問(wèn)題。 
	在實(shí)現(xiàn)緩存排序功能之前,必須先明白這一功能的合理性。不妨思考一下,既然可以在數(shù)據(jù)庫(kù)中排序,為什么還要把排序功能放在緩存中實(shí)現(xiàn)呢?這里簡(jiǎn)單總結(jié)了兩個(gè)原因:首先,排序會(huì)增加數(shù)據(jù)庫(kù)的負(fù)載,難以支撐高并發(fā)的應(yīng)用;其次,在緩存中排序不會(huì)遇到表鎖定的問(wèn)題。Redis恰好提供了排序功能,使我們可以方便地實(shí)現(xiàn)緩存排序。 
	Redis中用于實(shí)現(xiàn)排序功能的是SORT命令。該命令提供了多種參數(shù),可以對(duì)列表,集合和有序集合進(jìn)行排序。SORT命令格式如下: 
	view sourceprint? 
	1. 
	SORT key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern 。..]] [ASC | DESC] [ALPHA] [STORE destination] 
	BY參數(shù)用于指定排序字段,功能類(lèi)似于SQL中的order by。對(duì)于列表和集合而言,僅按照它們的值進(jìn)行排序往往沒(méi)有實(shí)際意義。以函數(shù)Cache2Hash返回的集合為例(實(shí)際上返回的是集合鍵),該集合中存儲(chǔ)的是一系列完整的哈希鍵,只按照這些鍵進(jìn)行排序,結(jié)果無(wú)非是按照數(shù)字或字典順序排列,其用處顯然不大。這是因?yàn)檎嬲鎯?chǔ)行數(shù)據(jù)的是哈希結(jié)構(gòu)本身,而非哈希鍵。假設(shè)集合鍵為“resultset.hash:123456”,集合中每個(gè)哈希鍵對(duì)應(yīng)的哈希結(jié)構(gòu)中都有一個(gè)名為“timestamp”的字段,現(xiàn)在要把集合中的所有哈希鍵按照timestamp字段進(jìn)行排序,這時(shí),只需執(zhí)行以下命令: 
	view sourceprint? 
	1. 
	SORT resultset.hash:123456 BY *->timestamp 
	從上例可以看出,BY的真正威力在于它可以讓SORT命令按照一個(gè)指定的外部鍵的外部字段進(jìn)行排序。SORT用集合resultset.hash:123456中的每個(gè)值(即每個(gè)哈希鍵)替換BY參數(shù)后的第一個(gè)“*”,并依據(jù)“->”后面給出的字段獲取其值,最后根據(jù)這些字段值對(duì)哈希鍵進(jìn)行排序。 
	LIMIT參數(shù)用于限制排序以后返回元素的數(shù)量,功能類(lèi)似于SQL中的limit。該參數(shù)接受另外兩個(gè)參數(shù),即offset和count,LIMIT offset count表示跳過(guò)前offset個(gè)元素,返回之后的連續(xù)count個(gè)元素??梢?jiàn),LIMIT參數(shù)可以用于實(shí)現(xiàn)分頁(yè)功能。 
	GET參數(shù)用于返回指定的字段值。以集合resultset.hash:123456為例,使用BY參數(shù)對(duì)集合中的所有哈希鍵按照哈希結(jié)構(gòu)中的timestamp字段排序后,SORT命令返回所有排序之后的哈希鍵。如果某個(gè)請(qǐng)求需要不是鍵而是某些字段值,這時(shí)就要使用GET參數(shù),使SORT命令返回指定字段值。假設(shè)除timestamp字段以外,集合中每個(gè)哈希鍵對(duì)應(yīng)的哈希結(jié)構(gòu)中還有一個(gè)名為“id”的字段,通過(guò)以下命令可以使SORT返回按照timestamp排序以后的每個(gè)哈希鍵對(duì)應(yīng)的哈希結(jié)構(gòu)中的timestamp和id值: 
	view sourceprint? 
	1. 
	SORT resultset.hash:123456 BY *->timestamp GET *->timestamp GET *->id 
	SORT用集合resultset.hash:123456中的每個(gè)值(即每個(gè)哈希鍵)替換GET參數(shù)之后的第一個(gè)“*”,并將其作為返回值。值得注意的是,利用GET #能夠得到集合中的哈希鍵本身。 
	ASC和DESC參數(shù)用于指定排序順序(默認(rèn)為ASC,即從低到高),ALPHA參數(shù)用于按照字典順序排列非數(shù)字元素。 
	STORE參數(shù)用于將SORT命令的返回值,即排序結(jié)果存入一個(gè)指定的列表。加上STORE參數(shù)后,SORT命令的返回值就變?yōu)榕判蚪Y(jié)果的個(gè)數(shù)。 
	下面的代碼實(shí)現(xiàn)了按照哈希的某個(gè)字段對(duì)集合中的哈希鍵排序,并將結(jié)果存入列表的過(guò)程: 
	view sourceprint? 
	01. 
	// 該函數(shù)對(duì)集合中的所有HASH鍵進(jìn)行排序,排序依據(jù)是HASH鍵所對(duì)應(yīng)的HASH中的某個(gè)字段, 
	02. 
	// 排序結(jié)果被存入一個(gè)LIST結(jié)構(gòu),LIST鍵應(yīng)當(dāng)包含結(jié)果集標(biāo)識(shí)符和排序字段標(biāo)識(shí)符, 
	03. 
	// 形如“sorted:123456:1234” 
	04. 
	string SortHash(sql::Connection *mysql_connection, 
	05. 
	redisContext *redis_connection, 
	06. 
	const string &resultset_id, 
	07. 
	const string &sort_field, 
	08. 
	int offset, int count, int order, int ttl) { 
	09. 
	// 只考慮存儲(chǔ)HASH鍵的SET 
	10. 
	string redis_row_set_key = “resultset.hash:” + resultset_id; 
	11. 
	redisReply *reply; 
	12. 
	// 檢測(cè)SET是否存在 
	13. 
	reply = static_cast 
	14. 
	“EXISTS %s”, 
	15. 
	redis_row_set_key.c_str())); 
	16. 
	if (reply->integer == 0) { 
	17. 
	freeReplyObject(reply); 
	18. 
	throw runtime_error(“FAILURE - no resultsets”); 
	19. 
	} else { 
	20. 
	freeReplyObject(reply); 
	21. 
	} 
	22. 
	string field_md5 = md5(sort_field); // 利用MD5排除排序字段中空格造成的影響 
	23. 
	// 將排序結(jié)果存入該LIST 
	24. 
	string redis_sorted_list_key = “sorted:” + resultset_id + “:” + field_md5; 
	25. 
	string by(“*->” + sort_field); //確定排序字段 
	26. 
	string ord = (order == 1) ? “ASC” : “DESC”; //order==1時(shí)按照升序排列;否則為降序 
	27. 
	stringstream ofsstream, cntstream; 
	28. 
	ofsstream << offset; 
	29. 
	cntstream << count; 
	30. 
	// 執(zhí)行排序命令,并把排序結(jié)果存入LIST 
	31. 
	reply = static_cast 
	32. 
	redis_connection, 
	33. 
	“SORT %s BY %s LIMIT %s %s GET %s ALPHA STORE %s”, 
	34. 
	redis_row_set_key.c_str(), 
	35. 
	by.c_str(), 
	36. 
	ofsstream.str().c_str(), 
	37. 
	cntstream.str().c_str(), 
	38. 
	“#”, 
	39. 
	redis_sorted_list_key.c_str())); 
	40. 
	freeReplyObject(reply); 
	41. 
	stringstream ttlstream; 
	42. 
	ttlstream << ttl; 
	43. 
	// 設(shè)置LIST的過(guò)期時(shí)間 
	44. 
	reply = static_cast 
	45. 
	“EXPIRE %s %s”, 
	46. 
	redis_sorted_list_key.c_str(), 
	47. 
	ttlstream.str().c_str())); 
	48. 
	freeReplyObject(reply); 
	49. 
	return redis_sorted_list_key; // 返回LIST鍵,以便于其他函數(shù)獲取該LIST中的內(nèi)容 
	顯然,對(duì)結(jié)果集中的哈希鍵進(jìn)行排序要比對(duì)字符串鍵排序更加直觀和方便。借助于排序函數(shù),可以方便地實(shí)現(xiàn)在Redis中查詢(xún)排序后的結(jié)果集,代碼如下: 
	view sourceprint? 
	01. 
	// 該函數(shù)根據(jù)sql語(yǔ)句和排序參數(shù),在Redis中查詢(xún)相應(yīng)的結(jié)果集并進(jìn)行排序,最后返回 
	02. 
	// 排序之后的HASH鍵 
	03. 
	vector 
	04. 
	redisContext *redis_connection, 
	05. 
	const string &sql, const string &sort_field, 
	06. 
	int offset, int count, int order, int ttl) { 
	07. 
	vector 
	08. 
	redisReply *reply; 
	09. 
	string resultset_id = md5(sql); // 結(jié)果集標(biāo)識(shí)符 
	10. 
	string field_md5 = md5(sort_field); // 排序字段標(biāo)識(shí)符 
	11. 
	// 嘗試獲取LIST中的所有HASH鍵 
	12. 
	string redis_sorted_list_key = “sorted:” + resultset_id + “:” + field_md5; 
	13. 
	// 嘗試獲取LIST中的所有HASH鍵 
	14. 
	reply = static_cast 
	15. 
	“LRANGE %s %s %s”, 
	16. 
	redis_sorted_list_key.c_str(), 
	17. 
	“0”, 
	18. 
	“-1”)); 
	19. 
	if (reply->type == REDIS_REPLY_ARRAY) { 
	20. 
	// 如果LIST不存在,調(diào)用Cache2Hash函數(shù)從Mysql中拉取數(shù)據(jù)到Redis,然后調(diào)用SortHash函數(shù) 
	21. 
	// 對(duì)結(jié)果集進(jìn)行排序并將排序后的HASH鍵存入LIST 
	22. 
	if (reply->elements == 0) { 
	23. 
	freeReplyObject(reply); 
	24. 
	sql::Statement *stmt = mysql_connection->createStatement(); 
	25. 
	sql::ResultSet *resultset = stmt->executeQuery(sql); 
	26. 
	Cache2Hash(mysql_connection, redis_connection, resultset, 
	27. 
	resultset_id, ttl); 
	28. 
	redis_sorted_list_key = SortHash(mysql_connection, redis_connection, 
	29. 
	resultset_id, sort_field, offset, 
	30. 
	count, order, ttl); 
	31. 
	// 再次嘗試獲取LIST中的所有HASH鍵 
	32. 
	reply = static_cast 
	33. 
	redis_connection, 
	34. 
	“LRANGE %s %s %s”, 
	35. 
	redis_sorted_list_key.c_str(), 
	36. 
	“0”, 
	37. 
	“-1”)); 
	38. 
	delete resultset; 
	39. 
	delete stmt; 
	40. 
	} 
	41. 
	// 將LIST中的所有HASH鍵存入redis_row_key_vector中 
	42. 
	string redis_row_key; 
	43. 
	for (int i = 0; i < reply->elements; ++i) { 
	44. 
	redis_row_key = reply->element[i]->str; 
	45. 
	redis_row_key_vector.push_back(redis_row_key); 
	46. 
	} 
	47. 
	freeReplyObject(reply); 
	48. 
	} else { 
	49. 
	freeReplyObject(reply); 
	50. 
	throw runtime_error(“FAILURE - LRANGE error”); 
	51. 
	} 
	52. 
	return redis_row_key_vector; 
	53. 
	} 
	這樣,在Redis中對(duì)結(jié)果集進(jìn)行簡(jiǎn)單排序操作的功能就實(shí)現(xiàn)了。
	
- 
                                MySQL
                                +關(guān)注
關(guān)注
1文章
893瀏覽量
29108 - 
                                Redis
                                +關(guān)注
關(guān)注
0文章
390瀏覽量
11969 
發(fā)布評(píng)論請(qǐng)先 登錄
不同類(lèi)型的自動(dòng)化工具在評(píng)估數(shù)據(jù)緩存效果時(shí)有哪些優(yōu)缺點(diǎn)?
    
深度剖析Redis的兩大持久化機(jī)制
Redis緩存的經(jīng)典問(wèn)題和解決方案
MySQL數(shù)據(jù)備份與恢復(fù)策略
【幸狐Omni3576邊緣計(jì)算套件試用體驗(yàn)】Redis最新8.0.2版本源碼安裝及性能測(cè)試
MySQL數(shù)據(jù)庫(kù)是什么
HarmonyOS5云服務(wù)技術(shù)分享--云緩存快速上手指南
利用dockerfile搭建mysql主從集群和redis集群
    
Redis實(shí)戰(zhàn)筆記
    
Redis Cluster之故障轉(zhuǎn)移
    
基于javaPoet的緩存key優(yōu)化實(shí)踐
    
MySQL數(shù)據(jù)庫(kù)的安裝
    
云服務(wù)器 Flexus X 實(shí)例 MySQL 應(yīng)用加速測(cè)試
    
Redis緩存與Memcached的比較
數(shù)據(jù)庫(kù)數(shù)據(jù)恢復(fù)—Mysql數(shù)據(jù)庫(kù)表記錄丟失的數(shù)據(jù)恢復(fù)流程
    
          
        
        
redis緩存mysql數(shù)據(jù)
                
 
           
            
            
                
            
評(píng)論