专栏电商日志财经减肥爱情
投稿投诉
爱情常识
搭配分娩
减肥两性
孕期塑形
财经教案
论文美文
日志体育
养生学堂
电商科学
头戴业界
专栏星座
用品音乐

Redis性能优化思路

  在一些网络服务的系统中,Redis 的性能,可能是比 MySQL 等硬盘数据库的性能更重要的课题。比如微博,把 热点微博 [1],最新的用户关系[2],都存储在 Redis 中,大量的查询击中 Redis,而不走 MySQL。
  那么,针对 Redis 服务,我们能做哪些性能优化呢?或者说,应该避免哪些性能浪费呢?
  Redis 性能的基本面
  在讨论优化之前,我们需要知道,Redis 服务本身就有一些特性,比如单线程运行。除非修改 Redis 的源代码,不然这些特性,就是我们思考性能优化的基本面。
  那么,有哪些 Redis 基本特性需要我们考虑呢?Redis 的项目介绍中概括了它特性:
  Redis is an in-memory database that persists on disk. The data model is key-value, but many different kind of values are supported.
  首先,Redis 使用操作系统提供的虚拟内存来存储数据。而且,这个操作系统一般就是指 Unix。Windows 上也能运行 Redis,但是需要特殊处理。如果你的操作系统使用交换空间,那么 Redis 的数据可能会被实际保存在硬盘上。
  其次,Redis 支持持久化,可以把数据保存在硬盘上。很多时候,我们也确实有必要进行持久化来实现备份,数据恢复等需求。但持久化不会凭空发生,它也会占用一部分资源。
  第三,Redis 是用 key-value 的方式来读写的,而 value 中又可以是很多不同种类的数据;更进一步,一个数据类型的底层还有被存储为不同的结构。不同的存储结构决定了数据增删改查的复杂度以及性能开销。
  最后,在上面的介绍中没有提到的是,Redis 大多数时候是 单线程运行 [2]的(single-threaded),即同一时间只占用一个 CPU,只能有一个指令在运行,并行读写是不存在的。很多操作带来的延迟问题,都可以在这里找到答案。
  关于最后这个特性,为什么 Redis 是单线程的,却能有很好的性能(根据 Amdahl’s Law,优化耗时占比大的过程,才更有意义),两句话概括是:Redis 利用了 多路 I/O 复用机制 [3],处理客户端请求时,不会阻塞主线程;Redis 单纯执行(大多数指令)一个指令不到  1 微秒 [4],如此,单核 CPU 一秒就能处理 1 百万个指令(大概对应着几十万个请求吧),用不着实现多线程( 网络才是瓶颈 [5])。
  优化网络延时
  Redis 的官方博客在几个地方都说,性能瓶颈更可能是 网络 [6],那么我们如何优化网络上的延时呢?
  首先,如果你们使用单机部署(应用服务和 Redis 在同一台机器上)的话,使用 Unix 进程间通讯来请求 Redis 服务,速度比 localhost 局域网(学名 loopback)更快。 官方文档 [7]是这么说的,想一想,理论上也应该是这样的。
  但很多公司的业务规模不是单机部署能支撑的,所以还是得用 TCP。
  Redis 客户端和服务器的通讯一般使用 TCP 长链接。如果客户端发送请求后需要等待 Redis 返回结果再发送下一个指令,客户端和 Redis 的多个请求就构成下面的关系:
  (备注:如果不是你要发送的 key 特别长,一个 TCP 包完全能放下 Redis 指令,所以只画了一个 push 包)这样这两次请求中,客户端都需要经历一段网络传输时间。
  但如果有可能,完全可以使用 multi-key 类的指令来合并请求,比如两个 GET key 可以用 MGET key1 key2 合并。这样在实际通讯中,请求数也减少了,延时自然得到好转。
  如果不能用 multi-key 指令来合并,比如一个 SET,一个 GET 无法合并。怎么办?
  Redis 中有至少这样两个方法能合并多个指令到一个 request 中,一个是 MULTI/EXEC,一个是 script。前者本来是构建 Redis 事务的方法,但确实可以合并多个指令为一个 request,它到通讯过程如下。至于 script,最好利用缓存脚本的 sha1 hash key 来调起脚本,这样通讯量更小。
  这样确实更能减少网络传输时间,不是么?但如此以来,就必须要求这个 transaction / script 中涉及的 key 在同一个 node 上,所以要酌情考虑。
  如果上面的方法我们都考虑过了,还是没有办法合并多个请求,我们还可以考虑合并多个 responses。比如把 2 个回复信息合并:
  这样,理论上可以省去 1 次回复所用的网络传输时间。这就是 pipeline 做的事情。举个 ruby 客户端使用 pipeline 的例子:
  require   "redis"
  @redis = Redis.new  ()
  @redis.pipelined do
  @redis.get "key1"
  @redis.set "key2"   "some value"
  end
  # => [1, 2]
  据说,有些语言的客户端,甚至默认就使用 pipeline 来优化延时问题,比如 node_redis。
  另外,不是任意多个回复信息都可以放进一个 TCP 包中,如果请求数太多,回复的数据很长(比如 get 一个长字符串),TCP 还是会分包传输,但使用 pipeline,依然可以减少传输次数。
  pipeline 和上面的其他方法都不一样的是,它不具有原子性。所以在 cluster 状态下的集群上,实现 pipeline 比那些原子性的方法更有可能。
  小结一下:  使用 unix 进程间通信,如果单机部署  使用 multi-key 指令合并多个指令,减少请求数,如果有可能的话  使用 transaction、script 合并 requests 以及 responses  使用 pipeline 合并 response
  警惕执行时间长的操作
  在大数据量的情况下,有些操作的执行时间会相对长,比如  KEYS *  ,LRANGE mylist 0 -1  ,以及其他算法复杂度为 O(n) 的指令。因为 Redis 只用一个线程来做数据查询,如果这些指令耗时很长,就会阻塞 Redis,造成大量延时。
  尽管官方文档中说  KEYS *  的查询挺快的,(在普通笔记本上)扫描 1 百万个 key,只需 40 毫秒(参见:https://redis.io/commands/keys),但几十 ms 对于一个性能要求很高的系统来说,已经不短了,更何况如果有几亿个 key(一台机器完全可能存几亿个 key,比如一个 key 100字节,1 亿个 key 只有 10GB),时间更长。
  所以,尽量不要在生产环境的代码使用这些执行很慢的指令,这一点 Redis 的作者在 博客 [8]中也提到了。另外,运维同学查询 Redis 的时候也尽量不要用。甚至,Redis Essential 这本书建议利用  rename-command KEYS ""   来禁止使用这个耗时的指令。
  除了这些耗时的指令,Redis 中 transaction,script,因为可以合并多个 commands 为一个具有原子性的执行过程,所以也可能占用 Redis 很长时间,需要注意。
  如果你想找出生产环境使用的「慢指令」,那么可以利用  SLOWLOG GET count   来查看最近的 count 个执行时间很长的指令。至于多长算长,可以通过在 redis.conf 中设置 slowlog-log-slower-than 来定义。
  除此之外,在很多地方都没有提到的一个可能的慢指令是  DEL  ,但 redis.conf 文件的注释[9]中倒是说了。长话短说就是 DEL 一个大的 object 时候,回收相应的内存可能会需要很长时间(甚至几秒),所以,建议用 DEL 的异步版本:UNLINK  。后者会启动一个新的 thread 来删除目标 key,而不阻塞原来的线程。
  更进一步,当一个 key 过期之后,Redis 一般也需要同步的把它删除。其中一种删除 keys 的方式是,每秒 10 次的检查一次有设置过期时间的 keys,这些 keys 存储在一个全局的 struct 中,可以用  server.db->expires   访问。
  检查的方式是:  从中随机取出 20 个 keys  把过期的删掉。  如果刚刚 20 个 keys 中,有 25% 以上(也就是 5 个以上)都是过期的,Redis 认为,过期的 keys 还挺多的,继续重复步骤 1,直到满足退出条件:某次取出的 keys 中没有那么多过去的 keys。
  这里对于性能的影响是,如果真的有很多的 keys 在同一时间过期,那么 Redis 真的会一直循环执行删除,占用主线程。
  对此,Redis 作者的 建议 [10]是警惕  EXPIREAT   这个指令,因为它更容易产生 keys 同时过期的现象。我还见到过一些建议是给 keys 的过期时间设置一个随机波动量。最后,redis.conf 中也给出了一个方法,把 keys 的过期删除操作变为异步的,即,在 redis.conf 中设置 lazyfree-lazy-expire yes  。
  优化数据结构、使用正确的算法
  一种数据类型(比如 string,list)进行增删改查的效率是由其底层的存储结构决定的。
  我们在使用一种数据类型时,可以适当关注一下它底层的存储结构及其算法,避免使用复杂度太高的方法。
  举两个例子:  ZADD  的时间复杂度是 O(log(N)),这比其他数据类型增加一个新元素的操作更复杂,所以要小心使用。 若 Hash 类型的值的 fields 数量有限,它很有可能采用 ziplist 这种结构做存储,而 ziplist 的查询效率可能没有同等字段数量的 hashtable 效率高,在必要时,可以调整 Redis 的存储结构。
  除了时间性能上的考虑,有时候我们还需要节省存储空间。比如上面提到的 ziplist 结构,就比 hashtable 结构节省存储空间(Redis Essentials 的作者分别在 hashtable 和 ziplist 结构的 Hash 中插入 500 个 fields,每个 field 和 value 都是一个 15 位左右的字符串,结果是 hashtable 结构使用的空间是 ziplist 的 4 倍。)。但节省空间的数据结构,其算法的复杂度可能很高。所以,这里就需要在具体问题面前做出权衡。
  如何做出更好的权衡?我觉得得深挖 Redis 的存储结构才能让自己安心。这方面的内容我们下次再说。
  以上这三点都是编程层面的考虑,写程序时应该注意啊。下面这几点,也会影响 Redis 的性能,但解决起来,就不只是靠代码层面的调整了,还需要架构和运维上的考虑。
  考虑操作系统和硬件是否影响性能
  Redis 运行的外部环境,也就是操作系统和硬件显然也会影响 Redis 的性能。在官方文档中,就给出了一些例子:  CPU:Intel 多种 CPU 都比 AMD 皓龙系列好  虚拟化:实体机比虚拟机好,主要是因为部分虚拟机上,硬盘不是本地硬盘,监控软件导致 fork 指令的速度慢(持久化时会用到 fork),尤其是用 Xen 来做虚拟化时。  内存管理:在 linux 操作系统中,为了让 translation lookaside buffer,即 TLB,能够管理更多内存空间(TLB 只能缓存有限个 page),操作系统把一些 memory page 变得更大,比如 2MB 或者 1GB,而不是通常的 4096 字节,这些大的内存页叫做 huge pages。同时,为了方便程序员使用这些大的内存 page,操作系统中实现了一个 transparent huge pages(THP)机制,使得大内存页对他们来说是透明的,可以像使用正常的内存 page 一样使用他们。但这种机制并不是数据库所需要的,可能是因为 THP 会把内存空间变得紧凑而连续吧,就像 mongodb 的文档 [11]中明确说的,数据库需要的是稀疏的内存空间,所以请禁掉 THP 功能。Redis 也不例外,但 Redis 官方博客上给出的理由是:使用大内存 page 会使 bgsave 时,fork 的速度变慢;如果 fork 之后,这些内存 page 在原进程中被修改了,他们就需要被复制(即 copy on write),这样的复制会消耗大量的内存(毕竟,人家是 huge pages,复制一份消耗成本很大)。所以,请禁止掉操作系统中的 transparent huge pages 功能。 交换空间:当一些内存 page 被存储在交换空间文件上,而 Redis 又要请求那些数据,那么操作系统会阻塞 Redis 进程,然后把想要的 page,从交换空间中拿出来,放进内存。这其中涉及整个进程的阻塞,所以可能会造成延时问题,一个解决方法是禁止使用交换空间(Redis Essentials 中如是建议,如果内存空间不足,请用别的方法处理)。
  考虑持久化带来的开销
  Redis 的一项重要功能就是持久化,也就是把数据复制到硬盘上。基于持久化,才有了 Redis 的数据恢复等功能。
  但维护这个持久化的功能,也是有性能开销的。
  首先说,RDB 全量持久化。
  这种持久化方式把 Redis 中的全量数据打包成 rdb 文件放在硬盘上。但是执行 RDB 持久化过程的是原进程 fork 出来一个子进程,而 fork 这个系统调用是需要时间的,根据Redis Lab 6 年前做的 实验 [12],在一台新型的 AWS EC2 m1.small^13 上,fork 一个内存占用 1GB 的 Redis 进程,需要 700+ 毫秒,而这段时间,redis 是无法处理请求的。
  虽然现在的机器应该都会比那个时候好,但是 fork 的开销也应该考虑吧。为此, 要使用合理的 RDB 持久化的时间间隔,不要太频繁 。
  接下来,我们看另外一种持久化方式:AOF 增量持久化。
  这种持久化方式会把你发到 redis server 的指令以文本的形式保存下来(格式遵循 redis protocol),这个过程中,会调用两个系统调用,一个是  write(2)  ,同步完成,一个是 fsync(2)  ,异步完成。
  这两部都可能是延时问题的原因:  write 可能会因为输出的 buffer 满了,或者 kernal 正在把 buffer 中的数据同步到硬盘,就被阻塞了。  fsync 的作用是确保 write 写入到 aof 文件的数据落到了硬盘上,在一个 7200 转/分的硬盘上可能要延时 20 毫秒左右,消耗还是挺大的。更重要的是,在 fsync 进行的时候,write 可能会被阻塞。
  其中,write 的阻塞貌似只能接受,因为没有更好的方法把数据写到一个文件中了。但对于 fsync,Redis 允许三种配置,选用哪种取决于你对备份及时性和性能的平衡:  always:当把  appendfsync   设置为 always,fsync 会和客户端的指令同步执行,因此最可能造成延时问题,但备份及时性最好。 everysec:每秒钟异步执行一次 fsync,此时 redis 的性能表现会更好,但是 fsync 依然可能阻塞 write,算是一个折中选择。  no:redis 不会主动出发 fsync (并不是永远不 fsync,那是不太可能的),而由 kernel 决定何时 fsync
  使用分布式架构 —— 读写分离、数据分片
  以上,我们都是基于单台,或者单个 Redis 服务进行优化。下面,我们考虑当网站的规模变大时,利用分布式架构来保障 Redis 性能的问题。
  首先说,哪些情况下不得不(或者最好)使用分布式架构:  数据量很大,单台服务器内存不可能装得下,比如 1 个 T 这种量级  需要服务高可用  单台的请求压力过大
  解决这些问题可以采用数据分片或者主从分离,或者两者都用(即,在分片用的 cluster 节点上,也设置主从结构)。
  这样的架构,可以为性能提升加入新的切入点:  把慢速的指令发到某些从库中执行  把持久化功能放在一个很少使用的从库上  把某些大 list 分片
  其中前两条都是根据 Redis 单线程的特性,用其他进程(甚至机器)做性能补充的方法。
  当然,使用分布式架构,也可能对性能有影响,比如请求需要被转发,数据需要被不断复制分发。(待查)
  后话
  其实还有很多东西也影响 Redis 的性能,比如 active rehashing(keys 主表的再哈希,每秒 10 次,关掉它可以提升一点点性能),但是这篇博客已经写的很长了。而且,更重要不是收集已经被别人提出的问题,然后记忆解决方案;而是掌握 Redis 的基本原理,以不变应万变的方式杜绝新出现的问题。
  参考资料
  [1] 热点微博:   https://www.infoq.cn/article/weibo-relation-service-with-redis
  [2] 单线程运行:   https://redis.io/topics/latency#single-threaded-nature-of-redis
  [3] 多路 I/O 复用机制:   https://redis.io/topics/clients#how-client-connections-are-accepted
  [4] 1 微秒:   https://redis.io/topics/latency#redis-latency-problems-troubleshooting
  [5] 网络才是瓶颈:   https://redis.io/topics/benchmarks#factors-impacting-redis-performance
  [6] 网络:   https://redis.io/topics/latency#latency-induced-by-network-and-communication
  [7] 官方文档:   https://redis.io/topics/benchmarks#factors-impacting-redis-performance
  [8] 博客:   https://redis.io/topics/latency#i39ve-little-time-give-me-the-checklist
  [9] 注释:   https://github.com/antirez/redis/blob/5.0/redis.conf#L669
  [10] 建议:   https://redis.io/topics/latency#latency-generated-by-expires
  [11] mongodb 的文档:   https://docs.mongodb.com/manual/tutorial/transparent-huge-pages/
  [12] 实验:   参见:https://redis.io/topics/latency#fork-time-in-different-systems  来源:www.jianshu.com/p/67093716547b

帝国时代2第十个民族日本步兵民族特点1。渔船两倍生命值,防御2,工作效率在黑暗时代快5,封建时代10,城堡时代15,帝王时代202。磨坊和伐木场矿场便宜503。从封建时代开始步兵攻击力快25团队特点战舰视侠盗猎车手4伦尼彼德洛维奇的描述出现任务琐事福斯坦谁也不是,滚开伦尼彼德洛维奇呼叫尼克贝里克。是侠盗猎车手中系列的一个角色,伦尼彼德洛维奇在在侠盗猎车手4中扮演次要角色。描述伦尼是自由城最有权势的,是俄罗斯黑帮分子肯尼彼德洛亚历山大赛维鲁的为政举措主要战争(1)为政举措宗教政策基督教在埃拉伽巴路斯统治时就已经不被受迫害,亚历山大和莫米娅容忍基督教。莫米娅与著名的基督徒俄利根有联系。4世纪和5世纪基督教文献称皇帝周围的部分人,或者甚至莫米娅马克西米安的人物生平历史评价人物生平早年经历马克西米安出身农民家庭,出生在伊利里亚行省的西米乌姆地区。他不识字,不懂得法律,举止笨拙。三世纪危机期间,他在奥勒良普罗布斯和卡鲁斯手下担任军职。他的军事才能多表现伽利埃努斯的军事改革和政治改革军事改革罗马军团长期以来都是以重装步兵为主力直到帝国时代中期,闻名天下的罗马军团都以职业重装步兵为铁打主力。大部分部队在和平时期驻扎在边境附近,以便在战争爆发的第一时间内,做到御敌李锡尼的成为东部皇帝争战人物形象成为东部皇帝瓦莱里乌斯李锡尼亚努斯李锡尼(ValeriusLicinianusLicinius),263年或265年出生于默西亚行省的一个达西亚族农民家庭。298年,李锡尼成为东部加卢斯和沃鲁西安努斯的人物生平人物生平公元251年,哥特人越过多瑙河侵入罗马帝国,皇帝德基乌斯出兵征讨。同时,他把自己的儿子赫伦尼乌斯立为共治的皇帝。但是公元251年的六月,发生了阿伯里图斯战役,哥特人在其国王埃拉伽巴路斯的简介家世夺权执政杀害评价简介漫画中的埃拉伽巴路斯埃拉伽巴路斯即位後,大力提倡他个人所信仰的太阳神崇拜,并将帝国东方华靡奢侈的宫廷风味带入罗马。年轻的埃拉伽巴路斯无心治国,又嫉妒他受欢迎表弟亚历山大,引发臣佩蒂纳克斯的出生和骑士生涯元老康茂德时期皇帝统治和死出生和骑士生涯佩蒂纳克斯是一名来自利古里亚的被释放的奴隶的儿子。他首先任教师,约160年进入罗马军队任军官,并且被提升为骑士。他首先在叙利亚任第七高卢辅助军大队的长官,并参加了对安瓦莱里安的内忧外患东征被俘战败影响内忧外患波斯的一再进犯让罗马东部边境苦不堪言根据拜占庭史学家佐西姆斯的记载,瓦勒利安大致在公元254年从罗马启程前往东方,同年冬抵达叙利亚首府安条克。彼时,沙普尔一世早已带着战利品伽利埃努斯的救火队长和评价救火队长伽里埃努斯继位前后东部战区已经被波斯人击穿在父亲瓦勒利安被俘后,罗马帝国已经陷入一片混乱。外有波斯人和哥特人的侵袭,内有诸多僭君自立。据不完全统计,仅公元260年7月至26
云顶之弈命运之轮结束,盘点S4赛季的那些高光特色文筱文聊电竞云顶之弈S4赛季已经进入了最后一个版本,云顶之弈S4赛季2020年9月17日上线,到2021年4月29日,整个赛季历时7个多月,赛季以命运之轮和S4。5的瑞兽闹新春作为云顶之弈厄加特盲仔和S5的佛耶戈,设计师为何钟情于移除文筱文聊电竞云顶之弈从S3赛季厄加特上线开始,拳头设计师开始钟情于移除或者斩杀机制了,如S3的厄加特,S4的盲仔以及即将更新的S5赛季的佛耶戈。这几个英雄的出场,基本上都是五费卡,云顶之弈流水的云顶铁打的亚索,S1到S5赛季的亚索哪个最强?文筱文聊电竞云顶之弈S5赛季马上就要更新了,索子哥赫然在英雄之列,不仅让人感慨到真的是流水的云顶,铁打的亚索。索子哥在每个赛季都没有缺席,虽然各个赛季身价不同,各个赛季技能不同,但云顶之弈一图带你看懂S5赛季英雄,一费卡英雄技能介绍(一)文筱文聊电竞云顶之弈S4赛季也进入了尾声,与之对应的就是S5赛季的到来从时间上来看S5赛季很有可能在4月底登录国服,而S5赛季的主题是光明与黑暗的对决,是完全对立面两个的阵容,并且云顶之弈带你看懂S5赛季英雄,二费和三费英雄技能介绍(二)文筱文聊电竞在上篇文章中,筱文介绍了云顶之弈13张一费卡英雄技能的介绍,里面其中就有着许多各位小伙们熟悉的面孔,那么这篇文章筱文继续为大家带来S5赛季英雄里面,二费卡和三费卡英雄的云顶之弈S3的厄加特和S4的盲僧,是设计师另外一种平衡文筱文聊电竞云顶之弈说起瞎子这个英雄,肯定很多玩家都想到赖皮两个字,自己辛辛苦苦样的大哥,遇见瞎子,站位不慎简直是难受。想必各位小伙伴也在各种游戏视频中,看到一星小瞎子,踢走对面的云顶之弈S4赛季最后的版本答案,九龙魂抬棺式上分文筱文聊电竞距离云顶之弈S4赛季结束还剩下不到一周时间,很多玩家在赛季末已经彻底放飞了自我现在的云顶变成了福星人的狂欢,一局游戏至少三家以上福星,但是玩福星嘛,非常容易反向上分,掉英雄联盟史上最成功的几大改动,符文天赋重做可排第一文筱文聊电竞英雄联盟作为一款全球火热的MOBA类游戏,已经走过来十一个年头,其中英雄版本变迁和英雄及机制改动数不胜数在长达十一年变迁中,自然有成功的,也有被玩家认为失败的,那么今天云顶之弈S5赛季更新,登神天使成为版本答案文筱文聊电竞云顶之弈S5赛季终于迎来了更新,在告别了S4赛点天选之人后,新版本让所有玩家都有点不适应在面对如此众多的英雄羁绊以及装备,大家都不懂游戏玩法的时候,就是最好上分的时候。英雄联盟英雄诸多外号的来由,带你揭开未解之谜文筱文聊电竞英雄联盟发展到今天,已经走过了11个年头,如今也有着众多的英雄,而每个英雄除了自己的本名外,大部分英雄都有着自己国服独特的外号,那么今天筱文就为大家带来诠释一下联盟中,英雄联盟改名来袭,法外狂徒成为枪火狂徒,鳄鱼也被迫改名文筱文聊电竞众所周知,英雄联盟中英雄的名字可由名字称号组成,称号往往代表着英雄的特点或者技能特色,如诡术妖姬乐芙兰,诡术妖姬本意就是欺骗者的意思除了上述的称呼外,往往玩家还会结合英
友情链接:快好知快生活快百科快传网中准网文好找聚热点快软件