您好,欢迎访问一九零五行业门户网

初学Redis(4)简单实现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(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(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 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(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 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中对结果集进行简单排序操作的功能就实现了。
其它类似信息

推荐信息