1 Redis 是什么

Redis 是一种开源的非关系型数据库。起源于负载较大时,当前关系型数据库无法承载的情况。
到目前为止,Redis 可以用作数据库、缓存、消息处理。Redis 可以存储键和5种不同数据结构类型之间的映射,分别为 string(字符串)、hash(散列)、list(列表)、set(集合)、zset(有序集合)。
除此之外,Redis 还可以将存储在内存的键值对持久化到硬盘;使用复制以扩展读性能;还可以使用客户端分片扩展写性能。到目前为止,最新的稳定版本已经更新到  6.0.10。

在本文中,主要围绕 Redis 用作缓存的功能展开。

2 两种服务器缓存数据特点

2.1 常规服务器

  1. 访问频率高,且更新频率较低
  2. 数据量较大,每次从 db 中读取耗时大

2.2 高并发服务器

  1. 访问频率高,且更新频率高

例如秒杀、抢购等活动,商品的库存量在短时间内一直变动

3 缓存内容

针对常规服务器,一般缓存查询条件和查询结果。针对高并发服务器,一般缓存查询/更新频率高的数据行。

4 高并发服务器的设计方案

高并发服务器下,要求数据实时性较高,因此设计了一种重复调度、自动缓存的方案。该算法一直在服务器后台运行,当秒杀活动开始时,算法能够自动的读取数据行,并且每隔一段时间对缓存进行更新(允许不定期)。同时,为达到持续运行的目的,编写守护进程进行缓存。

4.1 使用的数据结构

高并发服务器下的缓存方案借助了 Redis 内置的有序集合(zset)和 string,具体的数据结构分别为 schedule 集合、delay 集合、缓存 string。

  • schedule 集合

    • 作用:决定了缓存调度的顺序。
    • score :执行调度的时间戳
    • 键 :表名+id
      schedule 集合定义了缓存调度的顺序。重要的数据行先调度,不重要的后调度。即可以根据数据行的优先级进行调度。开发人员可以根据业务优先级灵活的调度数据行。
  • delay 集合

    • 作用 :表示每一个键的更新频率。
    • score :延迟值。延迟值 >0 时,表明允许缓存对应的键。延迟值 <=0时,表明不允许缓存对应的键,对应键应从缓存 string 中删除。
    • 键 :表名+id
      如果 schedule 集合中的时间戳到来,并且 delay 集合中延迟值 >0,那么键对应的内容需要与数据库进行同步,同时 schedule 集合中的时间戳更新为当前时间+延迟值。
  • 缓存 string

    • 作用:存放表中 id 和数据行
    • 键:表名+id
    • 值:db 中对应的数据行
      缓存 string 存储 id 和值的数据结构。通过 schedule 集合和 delay 集合决定缓存是否删除、或者与数据库同步。

4.2 调度算法

高并发服务器下Redis缓存的调度算法

4.3 代码实现

  1. 创建守护进程
var spawn = require("child_process").spawn;var process = require("process");const cache_row = require("./cache_rows");

var child = spawn("node",["./cache_rows.js"], { detached: true,stdio: [process.stdin,process.stdout,process.stderr]}); console.log("父进程id:" + process.pid, "守护进程id:" + child.pid);console.log("父进程退出!");process.exit(0);
  1. 初始化 schedule、delay 集合,延迟默认值为 10ms
/*** 构造cacheList  * @return {*} 表名:[id]*/var getCachelist = async function () {let cachelist = {};for (var item of tablelist) {  await getByTable(item).then((docList) => {    var idList = [];    for (var doc of docList) idList.push(doc["id"]);    cachelist[item] = idList;  });}return cachelist;};

/*** 初始化delay和schedule集合  * @return {*} 无*/var Init_delay_schedule = async function () {var cachelist = await getCachelist();for (var key in cachelist) {  var idList = cachelist[key];  for (var id of idList) {    let currentTime = Date.parse(new Date()) / 1000;    client.zadd("delay", delay.default, key + ":id:" + id, redis.print);    client.zadd("schedule", currentTime + delay.default, key + ":id:" + id, redis.print);  }}
  1. 持续进行调度
/*** 使用守护进程,确定是否缓存数据行* @param {*} table 修改的表名 */var cache_row = async function () {  var count = 0;  while (true) {      count++;      client = bluebird.promisifyAll(client);      var currentTime = Date.parse(new Date()) / 1000;

      //取首先到期的数据      var next = await client.zrangeAsync("schedule", 0, -1, "withscores").then((res) => { return res; });      if (next && parseInt(next[1]) > currentTime) {          await sleep(50);      } else {          var item = next[0];          var table = item.split(":")[0];          var row_id = item.split(":")[1];

          var delay = await client.zscoreAsync('delay', item).then((res) => { return res; });          if (delay <= 0)  //移除数据行          {              client.zrem("delay", item);              client.zrem("schedule", item);              client.delete(item);          } else {          //更新数据行              let data = await getById(table, row_id);              data = JSON.stringify(data[0]);

              client.zadd("schedule", currentTime + parseInt(delay), item);              client.set(item, data);          }      }

  }}

注意:node_redis 插件是异步的,而需要同步操作检查缓存是否命中。可使用 bluebird module 将异步操作更改为同步。

5.常规服务器的设计方案

常规服务器的数据特点是查询频率高,且修改频率低。由于所有的用户都会查询该数据,则可以由用户触发。当第一个请求到达时,查询 db,并将查询结果缓存在 redis 中。之后的请求则直接从 redis 中拿到对应数据。

5.1  缓存设置

每次请求到达时,第一步查看缓存。如果缓存未命中,则从数据库中查询,并将查询结果加入到缓存中。

//redis 设置keyvar redisSet = async function(table,cond,res){  var key = table+":"+JSON.stringify(cond);  res = JSON.stringify(res);  await client.setAsync(key,res);}

5.2  缓存查询

每次请求到达时,先从缓存中查询是否已有对应的结果。

//redis 查看缓存是否命中var redisCheck = async function(table,cond){  var key = table+":"+JSON.stringify(cond);  var res =  await client.getAsync(key); // if(res === null) return false;  return res; }

5.3  缓存更新

当缓存的表发生了更新、删除、插入操作,那么 redis 中的数据为脏数据,此时需更新缓存。

//redis 更新缓存var redisUpdate = async function(table){  var keys = await client.keysAsync(table+"*");  for(var key of keys){      var value =  await getDbDataByName(key);      await client.setAsync(key,JSON.stringify(value));  }}

//根据查询条件返回db中数据var getDbDataByName = async function (key){  if(!key) return null;

  let {table,json} = parseKey(key);   json = JSON.parse(json);  let limit = json["limit"];  let pageNum = json["page"];  let orderCondition = json["orderBy"];  let orderType = json["orderType"];  let whereCond = json["where"];

  var model = new BaseModel(table);  var res = await model  .queryByCondition(limit,pageNum,orderCondition,orderType,whereCond);  return  res;}

6.总结

这两种方案各有各的数据特点,并且有各自的应用场景。在选择方案时,最好根据数据的特点和 Redis 的使用定位,选择最合适的方案。

在选择 redis 缓存方案时,由于是 Daemon 首次在实际工程中使用,毫不犹豫的选择了高并发情况下的方案。而在实际项目中,数据特点是查询频率高、更新频率低,因此使用常规服务器下的 Redis 缓存就可以满足条件。Daemon 从中学习了一个道理:技术并不是用的越牛13越好,而是越合适越好

redis 缓存数据_Redis 缓存数据方案对比:常规 VS 高并发服务器相关推荐

  1. redis一般缓存什么样数据_Redis缓存和MySQL数据一致性方案详解

    关注我,可以获取最新知识.经典面试题以及技术分享 一.需求起因 在高并发的业务场景下,数据库大多数情况都是用户并发访问最薄弱的环节.所以,就需要使用redis做一个缓冲操作,让请求先访问到redis, ...

  2. redis 什么是冷数据_redis 冷数据存储格式

    92题 一般来说,建立INDEX有以下益处:提高查询效率:建立唯一索引以保证数据的唯一性:设计INDEX避免排序. 缺点,INDEX的维护有以下开销:叶节点的'分裂'消耗:INSERT.DELETE和 ...

  3. redis mysql 雪崩_Redis缓存雪崩、缓存穿透、并发等5大难题,你有没有解决方案

    缓存雪崩 数据未加载到高速缓存中,或者高速缓存同时在较大区域中失效,这将导致所有请求都去查找数据库,从而导致数据库CPU和内存负载过高,甚至会出现宕机. 比如雪崩的一个简单过程: 1.redis集群大 ...

  4. redis mysql 雪崩_Redis缓存雪崩问题

    一.什么是缓存雪崩 缓存雪崩就是指缓存由于某些原因(比如 宕机.cache服务挂了或者不响应)整体crash掉了,导致大量请求到达后端数据库,从而导致数据库崩溃,整个系统崩溃,发生灾难. 下面的就是一 ...

  5. java redis缓存使用_redis缓存在项目中的使用

    关于redis为什么能作为缓存这个问题我们就不说了,直接来说一下redis缓存到底如何在项目中使用吧: 1.redis缓存如何在项目中配置? 1.1redis缓存单机版和集群版配置?(redis的客户 ...

  6. 数据库主流容灾方案对比分析

    说到容灾,先要清楚概念,因为现在很多人把备份和容灾经常放在一起称为灾备,但实际上是两个概念.备份是为了应对灾难来临时造成的数据丢失问题.容灾是为了在遭遇灾害时保证信息系统正常运行,帮助企业实现业务连续 ...

  7. java 缓存命中率_Redis缓存命中率

    本文地址:http://www.dutycode.com/post-172.html 除非注明,文章均为 www.dutycode.com 原创,欢迎转载!转载请注明本文地址,谢谢. Redis命中率 ...

  8. PHP收费事件导致用户流失,PHP秒杀系统方案(解决大流量,高并发)

    抢购.秒杀是如今很常见的一个应用场景,主要需要解决的问题有两个: 1 高并发对数据库产生的压力 2 竞争状态下如何解决库存的正确减少("超卖"问题) 对于第一个问题,已经很容易想到 ...

  9. redis存10万条数据_redis如何存储数据

    飞天技术汇 | 阿里云Redis产品升级大全 阿里云Redis重磅产品升级:全球多活版.混合存储版.多线程性能增强版. 这期飞天技术汇你将看到 ● 企业如何实现业务快速全球化布局 ● 冷热数据如何分离 ...

最新文章

  1. HDU 1061 Rightmost Digit
  2. php控制器无限极分类,thinkphp无限极分类实现方法
  3. 动效设计中的隐喻-1
  4. kubectl logs -f tail 显示100_系统管理员应该知道的9个kubectl命令
  5. [JavaScript] 使用ArrayBuffer和Blob编辑二进制流 下载文件
  6. return 输出为空php,thinkphp5 返回json数据的方法---以及返回json为空的原因
  7. 博客目录 Blog directory
  8. 型材机柜您了解多少?
  9. 京瓷1125打印机清零_怎么设置京瓷1125MFP打印机ip地址
  10. Python学习笔记 | 编码和文件读写
  11. java 基础知识(不定期更新)
  12. win10中用命令行打开画图
  13. [本校测试] 魔王的消失Day2——By Hineven T3葬诗 提交答案题(爬山算法)
  14. Celery 全面学习笔记
  15. C字符串操作strlen/strnlen_s详解
  16. python爬虫抓取头条街拍美女图片
  17. java bean 序列化_JAVA bean为何要实现序列化
  18. ERROR in multi ./runoob1.js bundle.js Module not found: Error: Can't resolve 'bundle.js' in 'E:\app'
  19. 【标准解读】-GB 39732-2020《汽车事件数据记录系统EDR》
  20. c#的传输组件dotnetty

热门文章

  1. poi excel 导入导出
  2. sqlserver 存储过程 分页搜索查询
  3. 山寨Facebook的Shimmer效果
  4. HttpHelper使用记录
  5. 【嵌入式】使用Cross Toolchain构建交叉工具链
  6. 帆软报表,报错:sql注入攻击问题
  7. V-SQL的简单使用
  8. 算法进阶之Leetcode刷题记录
  9. DP || HYSBZ 1207 打鼹鼠
  10. linux 查看进程启动路径