初学Redis(2)——用Redis作为Mysql数据库的缓存

转载 2015年11月17日 16:06:48

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

        直观上看,Mysql中的数据都是按表存储的;更微观地看,这些表都是按行存储的。每执行一次select查询,Mysql都会返回一个结果集,这个结果集由若干行组成。所以,一个自然而然的想法就是在Redis中找到一种对应于Mysql行的数据结构。Redis中提供了五种基本数据结构,即字符串(string)、列表(list)、哈希(hash)、集合(set)和有序集合(sorted set)。经过调研,发现适合存储行的数据结构有两种,即string和hash。

        要把Mysql的行数据存入string,首先需要对行数据进行格式化。事实上,结果集的每一行都可以看做若干由字段名和其对应值组成的键值对集合。这种键值对结构很容易让我们想起Json格式。因此,这里选用Json格式作为结果集每一行的格式化模板。根据这一想法,我们可以实现将结果集格式化为若干Json对象,并将Json对象转化为字符串存入Redis的代码:

  1. // 该函数把结果集中的每一行转换为一个Json格式的字符串并存入Redis的STRING结构中,  
  2. // STRING键应该包含结果集标识符和STRING编号,形式如“cache.string:123456:1”  
  3. string Cache2String(sql::Connection *mysql_connection,  
  4.                     redisContext *redis_connection,  
  5.                     sql::ResultSet *resultset,  
  6.                     const string &resultset_id, int ttl) {  
  7.   if (resultset->rowsCount() == 0) {  
  8.     throw runtime_error("FAILURE - no rows");  
  9.   }  
  10.   // STRING键的前缀,包含了结果集的标识符  
  11.   string prefix("cache.string:" + resultset_id + ":");  
  12.   unsigned int num_row = 1;  // STRING编号,附加于STRING键的末尾,从1开始  
  13.   sql::ResultSetMetaData *meta = resultset->getMetaData();  
  14.   unsigned int num_col = meta->getColumnCount();  
  15.   // 将结果集中所有行对应的所有STRING键存入该SET,SET键包含了结果集的标识符  
  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.   // 将结果集中的每一行转为Json格式的字符串,将这些Json字符串存入STRING,  
  24.   // 每个STRING对应结果集中的一行  
  25.   while (resultset->next()) {  
  26.     string redis_row_key;  // STRING键名,由前缀和STRING编号组成  
  27.     stringstream keystream;  
  28.     keystream << prefix << num_row;  
  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格式的对应值对存入Redis  
  39.     reply = static_cast<redisReply*>(redisCommand(redis_connection,   
  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<redisReply*>(redisCommand(redis_connection,   
  46.                                                  "SADD %s %s",  
  47.                                                  redis_row_set_key.c_str(),   
  48.                                                  redis_row_key.c_str()));  
  49.     freeReplyObject(reply);  
  50.     // 设置STRING的过期时间  
  51.     reply = static_cast<redisReply*>(redisCommand(redis_connection,   
  52.                                                  "EXPIRE %s %s",  
  53.                                                  redis_row_key.c_str(),   
  54.                                                  ttlstr.c_str()));  
  55.     freeReplyObject(reply);  
  56.     ++num_row;  
  57.   }  
  58.   // 设置SET的过期时间  
  59.   reply = static_cast<redisReply*>(redisCommand(redis_connection,   
  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键,以便于其他函数获取该SET中的内容  
  65. }  


        要把Mysql的行数据存入hash,过程要比把数据存入string直观很多。这是由hash的结构性质决定的——hash本身就是一个键值对集合:一个“父键”下面包含了很多“子键”,每个“子键”都对应一个值。根据前面的分析可知,结果集中的每一行实际上也是键值对集合。用Redis键值对集合表示Mysql键值对集合应该再合适不过了:对于结果集中的某一行,字段对应于hash的“子键”,字段对应的值就是hash“子键”对应的值,即结果集的一行刚好对应一个hash。这一想法的实现代码如下:

  1. // 该函数把结果集中的每一行都存入一个HASH结构。HASH键应当包括结果集标识符和HASH编号,  
  2. // 形如“cache.string:123456:1”  
  3. string Cache2Hash(sql::Connection *mysql_connection,  
  4.                   redisContext *redis_connection,  
  5.                   sql::ResultSet *resultset,  
  6.                   const string &resultset_id, int ttl) {  
  7.   if (resultset->rowsCount() == 0) {  
  8.     throw runtime_error("FAILURE - no rows");  
  9.   }  
  10.   // HASH键的前缀,包含了结果集的标识符  
  11.   string prefix("cache.hash:" + resultset_id + ":");  
  12.   unsigned int num_row = 1;  // HASH编号,附加于HASH键的末尾,从1开始  
  13.   sql::ResultSetMetaData *meta = resultset->getMetaData();  
  14.   unsigned int num_col = meta->getColumnCount();  
  15.   // 将结果集中所有行对应的所有HASH键存入该SET,SET键包含了结果集的标识符  
  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.   // 结果集中的每一行对应于一个HASH,将结果集的所有行都存入相应HASH中  
  23.   resultset->beforeFirst();  
  24.   while (resultset->next()) {  
  25.     string redis_row_key;  // HASH键名,由前缀和HASH编号组成  
  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.       // 将结果集中一行的字段名和对应值存入HASH  
  33.       reply = static_cast<redisReply*>(redisCommand(redis_connection,  
  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<redisReply*>(redisCommand(redis_connection,   
  42.                                                  "SADD %s %s",  
  43.                                                  redis_row_set_key.c_str(),   
  44.                                                  redis_row_key.c_str()));   
  45.     freeReplyObject(reply);  
  46.     // 设置HASH的过期时间  
  47.     reply = static_cast<redisReply*>(redisCommand(redis_connection,   
  48.                                                  "EXPIRE %s %s",  
  49.                                                  redis_row_key.c_str(),   
  50.                                                  ttlstr.c_str()));  
  51.     freeReplyObject(reply);  
  52.     ++num_row;  
  53.   }  
  54.   // 设置SET的过期时间  
  55.   reply = static_cast<redisReply*>(redisCommand(redis_connection,   
  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键,以便于其他函数获取该SET中的内容  
  61. }  

        至此,我们已经给出了两种存储Mysql结果集的方案,这就是我们在篇首提出的第一个问题,即选择何种数据结构存储Mysql结果集的答案。下一篇文章将研究第二个问题,即数据结构键的标识符选择问题。

相关文章推荐

初学Redis(2)——用Redis作为Mysql数据库的缓存

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

初学Redis(3)——用Redis作为Mysql数据库的缓存

把MySQL结果集缓存到Redis的字符串或哈希结构中以后,我们面临一个新的问题,即如何为这些字符串或哈希命名,也就是如何确定它们的键。因为这些数据结构所对应的行都属于某个结果集,假如可以找到一种唯一...

centos7环境配置haproxy实现mysql数据库和redis代理服务器

centos7环境配置haproxy实现mysql数据库代理 我们通常会碰到这样的业务场景: b主机和c数据库在同一个内网,a主机不能直接访问c数据库,我们可以通过在b主机上搭建代理让a访问c数据...

支持连接池和结果集缓存的MySQL数据库JDBC通用框架的轻量级封装(一)——粗略实现

支持连接池和结果集缓存的MySQL数据库JDBC通用框架的轻量级封装(一)——粗略实现 (1)数据库连接池构建方法 1、利用Apache的Commons Pool通过继承BasePooledO...

支持连接池和结果集缓存的MySQL数据库JDBC通用框架的轻量级封装(二)——向上封装

“SQL对象”级别的封装方案,即以一条SQL语句的粒度为单位,将其包装成一个DBObject,利用@DBCondition来声明SQL的输入条件、@DBResult来声明查询所返回的结果,Connec...

Spring Boot——缓存支持2(Redis)

EhCache能够适用很多应用场景,但是由于EhCache是进程内的缓存框架,在集群模式下时,各应用服务器之间的缓存都是独立的,因此在不同服务器的进程间会存在缓存不一致的情况。即使EhCache提供了...

Redis缓存数据库

  • 2017年08月17日 19:43
  • 6.68MB
  • 下载

缓存数据库redis

  • 2016年05月02日 16:37
  • 1.21MB
  • 下载

Python Web框架之Flask(2)——mysql数据库操作

1.测试mysql数据库连接# -*- coding: utf-8 -*- """ Created on Tue Jun 27 10:39:14 2017@author: Administrator ...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:初学Redis(2)——用Redis作为Mysql数据库的缓存
举报原因:
原因补充:

(最多只允许输入30个字)