"已知的东西; 有些事情我们知道我们知道。 我们也知道有未知的事物。 就是说我们知道有些事情我们不知道。 但是,还有未知的未知数-我们不知道的我们不知道的……后一类往往是困难的。"

唐纳德·拉姆斯菲尔德

> Redis Wikipedia Page Logo

我与Redis在一起已经有好几年了,从来没有真正遇到过任何问题。 我已经使用Redis计划和部署了大规模的Web服务,并使用了数十亿条日常读写命令(使用可上下伸缩的主从复制),并且可以"正常工作"。 从开发人员的角度来看,这导致了我一个错误的假设,即我了解关于Redis所需的全部知识(而且,正如一位伟大的诗人所说,假设是所有问题的源头)。

当然,我不是DevOps工程师,也不知道Redis的所有细节(但我仍然不知道),但是我觉得使用Redis被"发现"了。 其他服务(例如MySQL)迫使我对它们的概念有扎实的了解,以便编写快速高效的代码(再次,无需像DBA一样掌握MySQL,但至少您需要了解索引或JOIN才能快速编写代码 和高效的查询)。 但是使用Redis,我可以将其"即插即用"到我的代码中。

几周前,我被分配来解决在我们的一项旧服务中从Redis提取数据时的性能问题。 该服务的业务逻辑并不那么重要,但是对该服务的工作方式以及Redis的使用方式的总体概述将有助于我们理解问题所在以及最终如何解决。

我们拥有一个大型系统,可以存储由不同实体产生的数十亿每日事件。 查询实体事件可能很慢,并且在许多情况下是多余的,因为我们不需要所有数据。 这就是我们提供服务的地方-它提供了一个API,可以快速查询最近30天的实体唯一事件。

例如,假设有一个系统监视着带有传感器的空调,该传感器在每次AC状态发生变化(打开/关闭会更改AC状态以及改变温度)时发送事件。 一个AC状态每天可能会更改很多次,并且我们的系统将为每次更改存储一个事件,但是我们的服务仅会为过去30天内所做的每个更改返回一个事件(无论AC被翻转了多少次) ,我们的服务将返回单个"打开"事件)。

Redis快速,支持数据过期(我们只需要最近30天的事件,还记得吗?),并且具有Set数据结构,是我们服务的理想存储:将同一项目多次添加到Set中将导致只有一个副本 此商品在套装中。 可以将存储在我们系统中的每个实体(AC)的数百万个不同事件转换为几十个唯一项(事件)的小集合。 我们的服务仅需要"侦听"传入的事件,并使用AC ID和日期作为键将它们存储到Redis中,并将到期时间设置为30天。 当查询AC事件时,我们的服务将计算所有此AC密钥并返回存储在这些密钥中的所有事件的并集。

Redis中存储的数据的一览视图如下所示:

: #{on, off, 24°, 28° ...}, expires 2019-10-30
: #{on, off, 25°, 27° ...}, expires 2019-10-29
: #{on, 26°, humidity 32° ...}, expires 2019-10-28
...
...
...
: #{off, humidity 35° ...}, expires 2019-10-01
: #{on, off, 23°, 27° ...}, expires 2019-10-30
: #{on, off, 25°, 28° ...}, expires 2019-10-29
...
...
...
: #{24°, 26°, humidity 42° ...}, expires 2019-10-01
: #{on, off, 23°, 27° ...}, expires 2019-10-30
...
...
...

密钥是具有事件收集日期的实体的ID。 ac1-2019-09-30代表id = ac1的实体,其值是该日期收集的一组事件。 该密钥将于2019–10–30到期。

在Redis内部,我们的服务使用SUNION命令来获取特定实体在不同日期存储的所有事件的联合集:

SUNION
...
...

这个简单的逻辑可以处理99.6%的查询。 但是,有时从Redis获取数据花费的时间很长(在某些情况下可能需要30秒),这在产品上并不被接受。

我已经开始解决因搜索通用规则而导致的响应缓慢的问题,这并不奇怪,这通常发生在SUNION处理大型Set时(也并非总是如此)。 Redis中存储的大多数Set(基本代表元素数)的基数小于100。 但是对于少量的Set,大小可能会很大。 如您在下表中所见,有85%的组合有0–50个项目,少于0.5%的组合有1,000个或更多项目)

我在阅读服务代码时注意到的另一件事是,为了避免产生较大的响应负载,无论对服务进行编码的是谁,都对API返回的事件数量进行了限制(我们将在后面看到为什么这与 解决方案)。

了解我对Redis的了解后,使用SUNION命令似乎是一个合理的解决方案。 如果我是首先编写此服务的人,那么我可能会做同样的事情。 但是,您不知道的内容有时会造成很多痛苦……谷歌搜索很快发现了两个我对Redis不了解或不完全了解的事实:

· SUNION是一个缓慢的命令,采用两个大集合的交集可能会花费大量时间。 实际上,我在Redis官方文档中阅读它感到很惊讶,因为我一直认为Redis会一直保持快速。

· 从命令执行的角度来看,Redis主要是单线程服务器,这意味着当请求缓慢时,所有其他客户端将等待该请求得到处理。

了解问题是找到正确解决方案的一半。 我意识到SUNION并不是我需要的命令,但是有哪些替代方案? 好吧,感谢Google和Redis.io文档,我遇到了SSCAN命令。 SSCAN命令(实际上,Redis中有针对不同数据类型的一堆不同的SCAN命令)允许在Set上进行增量迭代,每次调用仅返回少量元素。 可以使用它而不会受到诸如SMEMBERS或SUNION之类的命令的负面影响,这些命令在针对大型集合进行调用时可能会长时间阻塞服务器。

请注意,SSCAN在一个集合上完全迭代的时间复杂度为O(N),其中N是集合基数。 对于SMEMBERS来说,这也是命令的复杂性,但是如果您还记得,我还说过我们的服务对从其API返回的商品数量有一定的限制。 由于一旦达到此限制就可以中断SSCAN迭代,因此我们的时间复杂度实际上将是O(Min(N,Limit)),这是SUNION的要求,它将始终从Redis(和我们的服务逻辑)中提取数据 将对整个项目集合中返回的项目数设置限制)

到目前为止,一切都很好,但是在SUNION上使用SSCAN可能会有不利的一面吗? 好吧,可能存在—尽管像SUNION这样的阻塞命令可以保证在给定的时间内提供集合中的所有元素,但是SSCAN命令只能提供有限的保证,因为集合可以在迭代过程中进行更改。 在完整的SSCAN迭代过程中,集合中不存在的元素可能会返回,也可能不会返回:它是未定义的。 但是,考虑到我们不会从Redis中删除项目(只是添加新项目),因此这并不是我们服务中的真正问题。

确实运行了一些基准测试可以测试SSCAN(仅提取有限数量的项目)与SUNION提取所有数据,并且它们以编程方式仅从中提取第一个有限的项目,这显示出明显的改进。 对于小型Set,时间大致相同,但是对于具有1,000多个项目的大型Set,时间要好得多

繁荣! 问题解决了。 从这个过程中我得出什么结论? -可以说我们需要更好地了解我们的知识,但是在现实生活中,我们在日常工作中使用许多不同的服务和程序包(数据库,云服务,开源程序包等),而这几乎是不可能的 掌握所有这些知识(不仅仅是性能,请考虑其中一些软件包可能存在的安全性问题)。

但是,我们至少应该尝试掌握我们所依赖的服务,这些软件包会对我们的服务产生巨大影响。

(本文翻译自Tzafrir Ben Ami的文章《What I didn't know I didn't know about Redis》,参考:https://medium.com/swlh/what-i-didnt-know-i-didn-t-know-about-redis-6ad3729f29ed)

程序包android.support.annotation不存在_我不知道我不了解的Redis知识相关推荐

  1. 程序包android.support.annotation不存在_efcore技巧贴也许有你不知道的使用技巧

    前言 .net 环境近些年也算是稳步发展.在开发的过程中,与数据库打交道是必不可少的.早期的开发者都是DbHelper一撸到底,到现在的各种各样的ORM框架大行其道.孰优孰劣谁也说不清楚,文无第一武无 ...

  2. Android 错误: 程序包android.support.annotation不存在

    在使用GlideApp时[创建继承glideapp类],make项目时,报错: 解决方案: 在app.gradle下添加如下依赖: implementation 'com.github.bumptec ...

  3. android.support.annotation不存在

    迁移项目到AndroidX时,出现一个问题,如下: PublicMessageActivity_ViewBinding.java:4: 错误: 程序包android.support.annotatio ...

  4. Android Support Annotation的使用

    http://blog.magicer.xyz/2017/... 官方提供了很多特别好用的类或注解,这里说的support annotation就是特别好的工具,多使用其中的注解,可以规范我们的开发, ...

  5. android 项目编译报错 符号: 类 shape 位置: 程序包 android.widget

    错误 android 项目编译报错 符号: 类 shape 位置: 程序包 android.widget 详细错误 Compiling with JDK Java compiler API. D:\A ...

  6. Android Support Annotation Library使用详解

    概述 Support Annotation Library是在Android Support Library19.1版本开始引入的一个全新的函数包,它包含了诸多有用的元注解.用来帮助开发者在编译期间发 ...

  7. 错误 程序包android.app不存在,运行.app时Android Studio获取包名称错误

    运行.app时Android Studio获取包名称错误 我将程序包名称从com.mycompany.myapplication更改为com.mycompany.testapp. 一切都构建良好,但是 ...

  8. javadoc 程序包android.content不存在,Eclipse中的Javadoc无法识别包

    Eclipse中的Javadoc无法识别包 感谢这个线程 ,我能够获得Javadoc链接,以在Windows上的Eclipse中为我的Android项目工作. 具体来说,"{@link an ...

  9. Android Support Annotation介绍

    在浏览Butter Knife源码时,发现里面用了好几个support包里的注解,比如@UiThread:当时一脸懵逼,第一次见到.再学习过后,今天,介绍下support包的注解.注解可以简化代码并提 ...

最新文章

  1. maven入门(1-3)maven的生命周期
  2. ASP.NET页面间传值方式--zt
  3. java 本地发送邮件_java在本地发送邮件可以,在服务器发送报错
  4. 微信小游戏复活了传统PC游戏
  5. android textView 替文字添加下划线 删除线
  6. 双系统linux长时间黑屏,win10 ubuntu 双系统安装黑屏问题
  7. 美团点评Docker容器管理平台
  8. popen 如何获取指令执行情况_Linux下使用popen()执行shell命令
  9. 【模型压缩】通道剪枝《Pruning Filters For Efficient ConvNets》论文翻译
  10. 【报告分享】2020中国股民图鉴.pdf(附下载链接)
  11. android 加载系统私有库,Android 搭建私有maven仓库及上传项目
  12. verilog读入.txt的有符号十进制数,把有符号十进制数写入到.txt文件中
  13. 【Linux】文件描述符与重定向
  14. Fragment的使用
  15. 把一个web项目改名后复制,再把改名后的web项目发布到tomcat,访问出现404错误的解决办法
  16. Web前端的优点有哪些?为什么Web前端可以如此火爆?
  17. 魔方怎么更改计算机名,软媒魔方怎么通过设置向导进行设置
  18. excel文件修复工具_ArcGIS工具箱使用技巧汇总
  19. PDF删除页面免费的方法有什么?PDF怎么删除页面的技巧你不能错过
  20. cad安装日志文件发生错误_CAD安装不正确怎么办?CAD发生错误安装过早结束的解决方法...

热门文章

  1. leetcode_two sum()
  2. USTC English Club Note20211108
  3. 科大星云诗社动态20210911
  4. ModuleNotFoundError: No module named 'mpl_toolkits.basemap'
  5. MatConvnet工具箱文档翻译理解(1)
  6. 用内存流 文件流 资源生成客户端(Delphi开源)
  7. 有关 Form 的一些代码的标准写法
  8. Linux 进程管理命令
  9. 查看mysql表中的所有索引
  10. STM32开发 -- 系统架构