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. }
// 该函数根据sql语句在Redis中查询相应的结果集,并返回结果集中每一行所对应的数据结构的键
vector<string> GetCache(sql::Connection *mysql_connection,
redisContext *redis_connection,
const string &sql, int ttl, int type) {
vector<string> redis_row_key_vector;
string resultset_id = md5(sql); // 计算sql语句的md5,这是唯一标识结果集的关键
// type==1时,该函数将查询相应的STRING集合或将结果集写入若干STRING
string cache_type = (type == 1) ? "string" : "hash";
// 根据type信息和结果集标识符合成SET键
string redis_row_set_key = "resultset." + cache_type + ":" + resultset_id;
redisReply *reply;
// 尝试从reply中获取SET中保存的所有键
reply = static_cast<redisReply*>(redisCommand(redis_connection,
"SMEMBERS %s",
redis_row_set_key.c_str()));
if (reply->type == REDIS_REPLY_ARRAY) {
// 如果要找的SET不存在,说明Redis中没有相应的结果集,需要调用Cache2String或
// Cache2Hash函数把数据从Mysql拉取到Redis中
if (reply->elements == 0) {
freeReplyObject(reply);
sql::Statement *stmt = mysql_connection->createStatement();
sql::ResultSet *resultset = stmt->executeQuery(sql);
if (type == 1) {
redis_row_set_key = Cache2String(mysql_connection, redis_connection,
resultset, resultset_id, ttl);
} else {
redis_row_set_key = Cache2Hash(mysql_connection, redis_connection,
resultset, resultset_id, ttl);
}
// 再次尝试从reply中获取SET中保存的所有键
reply = static_cast<redisReply*>(redisCommand(redis_connection,
"SMEMBERS %s",
redis_row_set_key.c_str()));
delete resultset;
delete stmt;
}
// 把SET中的每个STRING或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 - SMEMBERS error");
}
return redis_row_key_vector;
}

现在我们已经掌握了确定Redis中的结果集标识符以及各数据结构的键的方法。下一篇文章将研究结果集在Redis中的排序和分页问题。

redis(三)--用Redis作为Mysql数据库的缓存的更多相关文章

  1. 用Redis作为Mysql数据库的缓存【转】

    用Redis作Mysql数据库缓存,必须解决2个问题.首先,应该确定用何种数据结构存储来自Mysql的数据:在确定数据结构之后,还要考虑用什么标识作为该数据结构的键. 直观上看,Mysql中的数据都是 ...

  2. 三种方法查看MySQL数据库的版本

    1.使用-V参数 首先我们想到的肯定就是查看版本号的参数命令,参数为-V(大写字母)或者--version 使用方法: D:\xampp\mysql\bin>mysql -V 或者 D:\xam ...

  3. nodejs学习(三)--express连接mysql数据库,mysql查询封装

    一.说一下 连接不同的数据库需要安装相应的插件,此demo使用mysql数据库,需自行安装mysql数据库软件. 新建数据库webapp, 新建表users: 二.直接开码 npm install m ...

  4. mysql数据库查询缓存总结

    概述 查询缓存(Query Cache,简称QC),存储SELECT语句及其产生的数据结果.闲来无事,做一下这块的总结,也做个备忘! 工作原理 查询缓存工作原理如下: 缓存SELECT操作的结果集和S ...

  5. 用Redis作为Mysql数据库的缓存

    看到一篇不错的博文,记录下: http://blog.csdn.net/qtyl1988/article/details/39553339 http://blog.csdn.net/qtyl1988/ ...

  6. JavaEE系列之(三)JDBC操作MySQL数据库

    一.JDBC简介        JDBC(Java Data Base Connectivity)java数据库连接        SUN公司为了简化.统一对数据库的操作,定义了一套Java操作数据库 ...

  7. php三种方式操作mysql数据库

    php可以通过三种方式操作数据库,分别用mysql扩展库,mysqli扩展库,和mysqli的预处理模式分别举案例加以说明 1.通过mysql方式操作数据库 工具类核心代码: <?php cla ...

  8. 《闲扯Redis三》Redis五种数据类型之List型

    一.前言 Redis 提供了5种数据类型:String(字符串).Hash(哈希).List(列表).Set(集合).Zset(有序集合),理解每种数据类型的特点对于redis的开发和运维非常重要. ...

  9. 三、Redis基本操作——List

    小喵的唠叨话:前面我们介绍了Redis的string的数据结构的原理和操作.当时我们提到Redis的键值对不仅仅是字符串.而这次我们就要介绍Redis的第二个数据结构了,List(链表).由于List ...

随机推荐

  1. Excel分组快速自动填充编号

    在Excel自动填充很简单,但如果按分组等条件进行填充就有点麻烦了 说麻烦可能是你并没有搞清楚到底如何才能实现你的需求   下图是客户提供的Excel数据,我需要将下面的数据导入到数据库中,因为客户在 ...

  2. aglio报错解决

    Cannot write or read cache for themes (ENOENT on cache folder) aglio -i ./api.md -o api.html >> ...

  3. python emoji 表情过滤

    http://my.oschina.net/jiemachina/blog/189460 注意替换的这些emoji是标准的表情字符,每个表情本来是2个字节,替换成字符串后,每个表情就变成12个字符了, ...

  4. JAVA通过oshi获取系统和硬件信息

    一.引入jar包 本项目主要使用第开源jar包:https://github.com/oshi/oshi <dependency> <groupId>junit</gro ...

  5. fiddler抓包参数乱码的解决方法

    解决方法: 1.win+R 2.打开注册表编辑器:输入regedit +回车+是 3.HKEY_CURRENT_USER\Software\Microsoft\Fiddler2 4.右键新建,选字符串 ...

  6. nodeJS服务器的创建和重新启动

    一: 首先在nodejs项目里创建一个server.js文件,输入下面代码 var http = require("http"); http.createServer(functi ...

  7. 11g新特性 -- Virtual Private Catalogs

    在oracle 11g之前版本中,如果用户只是需要访问recovery catalog 中的某个数据库,但是我们却只能将整个recovery catalog授权给他,无法做到严格限制. 现在,virt ...

  8. Spark 核心篇-SparkEnv

    本章内容: 1.功能概述 SparkEnv是Spark的执行环境对象,其中包括与众多Executor执行相关的对象.Spark 对任务的计算都依托于 Executor 的能力,所有的 Executor ...

  9. 3D建模软件的选择(UG,Solidworks,ProE)

    转自:3D建模软件的选择(UG,Solidworks,ProE) 自述 咱是一个码农,和web.软件.控制台打交道太多了,很想玩玩炫的东西,于是学了点点PS,结果发现完全没有美术细胞TT.最近有碰到对 ...

  10. 透彻理解Spring事务设计思想之手写实现

    前言 事务,是描述一组操作的抽象,比如对数据库的一组操作,要么全部成功,要么全部失败.事务具有4个特性:Atomicity(原子性),Consistency(一致性),Isolation(隔离性),D ...