【Redis】| 总结/Edison Zhou --- 一份Redis标准使用规范,规范Redis的使用,有助于提高Redis的效率。如果本系列文章选一个必看的,那本篇就是。 1键值对使用规范 <article data-content="[{" type":"block","id":"tjna-1649732218319","name":"paragraph","data":{},"nodes":[{"type":"text","id":"8ufx-1649732218317","leaves":[{"text":"在redis中存在一些“读取-修改-写回”的操作流程(","marks":[]},{"text":"read-modify-write,="" rmw","marks":[{"type":"bold"}]},{"text":"),并发的rmw操作可能会导致数据错误,因此redis提供了两种保证并发访问正确性的方法:","marks":[]}]}],"state":{}},{"type":"block","id":"hcso-1649732493872","name":"list-item","data":{"listid":"1lzp-1649732495442","listtype":"unordered","listlevel":1},"nodes":[{"type":"text","id":"dwnb-1649732493870","leaves":[{"text":"加锁","marks":[{"type":"bold"}]}]}],"state":{"index":1}},{"type":"block","id":"lvmg-1649732518991","name":"list-item","data":{"listid":"1lzp-1649732495442","listtype":"unordered","listlevel":2},"nodes":[{"type":"text","id":"eq0d-1649732518990","leaves":[{"text":"优点:简单易行,适合于多个客户端加锁的场景。","marks":[]}]}],"state":{"index":1}},{"type":"block","id":"lpvi-1649744608911","name":"list-item","data":{"listid":"1lzp-1649732495442","listtype":"unordered","listlevel":2},"nodes":[{"type":"text","id":"etud-1649744608910","leaves":[{"text":"缺点:一来加锁过多会降低系统的并发访问性能,二来redis客户端加锁时需要用到分布式锁,分布式锁实现复杂。","marks":[]}]}],"state":{"index":2}},{"type":"block","id":"mou6-1649732497834","name":"list-item","data":{"listid":"1lzp-1649732495442","listtype":"unordered","listlevel":1},"nodes":[{"type":"text","id":"4v7o-1649732497832","leaves":[{"text":"原子操作","marks":[{"type":"bold"}]}]}],"state":{"index":2}},{"type":"block","id":"zojr-1649732883779","name":"list-item","data":{"listid":"1lzp-1649732495442","listtype":"unordered","listlevel":2},"nodes":[{"type":"text","id":"y0c2-1649732883777","leaves":[{"text":"优点:","marks":[]},{"text":"执行过程保持原子性的操作,而且原子操作执行时并不需要再加锁,实现了","marks":[{"type":"fontsize","value":14}]},{"text":"无锁操作","marks":[{"type":"fontsize","value":14},{"type":"bold"}]},{"text":"。","marks":[{"type":"fontsize","value":14}]}]}],"state":{"index":1}},{"type":"block","id":"qpcd-1649745357333","name":"list-item","data":{"listid":"1lzp-1649732495442","listtype":"unordered","listlevel":2},"nodes":[{"type":"text","id":"7nqn-1649745357331","leaves":[{"text":"缺点:如果将过多的操作都放入原子操作中,也会降低redis的并发性能。","marks":[{"type":"fontsize","value":14}]}]}],"state":{}}]"=""><article data-content="[{" type":"block","id":"d1cu-1649906303140","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"z6vf-1649906303141","leaves":[{"text":"codis集群的整体架构和关键组件如下所示:中心思想是","marks":[]},{"text":"基于代理(proxy)","marks":[{"type":"bold"}]},{"text":"设计实现","marks":[]}]}],"state":{}},{"type":"block","id":"fuba-1649906318791","name":"image","data":{"version":1,"url":"https:="" note.youdao.com="" yws="" res="" 15762="" webresource71daf84e294d18b9bf1cfa41617e7fd3","width":513,"height":325,"style":{"textalign":"center"}},"nodes":[],"state":{"rendersource":"https:="" webresource71daf84e294d18b9bf1cfa41617e7fd3","initialsize":{"width":513,"height":323},"loading":false}},{"type":"block","id":"gurl-1649907095910","name":"paragraph","data":{},"nodes":[{"type":"text","id":"llig-1649907095911","leaves":[{"text":"codis集群保证高可靠性的架构:","marks":[]},{"text":"配置了="" server="" group="" 的="" codis="" 集群架构,在="" 集群中,通过部署="" 和哨兵集群,实现="" 的主从切换,提升集群可靠性。","marks":[{"type":"fontsize","value":14}]}]}],"state":{}},{"type":"block","id":"fbw7-1649907114754","name":"image","data":{"version":1,"url":"https:="" 15772="" webresourcee0ff841981af3e08688cfa67a21ce1fd","width":516,"height":343,"style":{"textalign":"center"}},"nodes":[],"state":{"rendersource":"https:="" webresourcee0ff841981af3e08688cfa67a21ce1fd","initialsize":{"width":516,"height":343},"loading":false}},{"type":"block","id":"khai-1649907218277","name":"quote","data":{},"nodes":[{"type":"block","id":"xz5h-1649907202936","name":"paragraph","data":{},"nodes":[{"type":"text","id":"ilrr-1649907202937","leaves":[{"text":"note:","marks":[{"type":"bold"}]},{"text":"对于="" dashboard="" 和="" fe="" 来说,它们主要提供配置管理和管理员手工操作,负载压力不大,所以,它们的可靠性可以不用额外进行保证了。","marks":[]}]}]}],"state":{}}]"=""><article data-content="[{" type":"block","id":"rlwi-1649906356457","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"xjjo-1649906356458","leaves":[{"text":"一个请求的整体处理如下图所示:","marks":[]}]}],"state":{}}]"=""><article data-content="[{" type":"block","id":"w148-1649907578004","name":"paragraph","data":{},"nodes":[{"type":"text","id":"mrr6-1649907578005","leaves":[{"text":"在数据分布的实现方法上,codis="" 和="" redis="" cluster="" 很相似,都采用了="" key="" 映射到="" slot、slot="" 再分配到实例上的机制。但是,codis是通过codis="" dashboard分配修改的,并保存在zookeeper集群中。而在redis="" cluster中,数据路由表是通过每个实例间的通信传递的,最后在每个实例上保存一份。","marks":[]}]}],"state":{}}]"=""><article data-content="[{" type":"block","id":"gm09-1650275031375","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"wlro-1650275031376","leaves":[{"text":"微博对redis的三大具体需求:","marks":[]}]}],"state":{}},{"type":"block","id":"sebf-1650263310682","name":"list-item","data":{"listid":"2yyt-1650263350366","listtype":"unordered","listlevel":1},"nodes":[{"type":"text","id":"gmon-1650263310683","leaves":[{"text":"能够提供","marks":[]},{"text":"高性能、高并发","marks":[{"type":"bold"}]},{"text":"的读写访问,保证读写低延迟;(高性能)","marks":[]}]}],"state":{"index":1}},{"type":"block","id":"cnep-1650263324504","name":"list-item","data":{"listid":"2yyt-1650263350366","listtype":"unordered","listlevel":1},"nodes":[{"type":"text","id":"jyci-1650263324502","leaves":[{"text":"能够支持","marks":[]},{"text":"大容量存储","marks":[{"type":"bold"}]},{"text":";(大容量)","marks":[]}]}],"state":{"index":2}},{"type":"block","id":"j6ra-1650263329720","name":"list-item","data":{"listid":"2yyt-1650263350366","listtype":"unordered","listlevel":1},"nodes":[{"type":"text","id":"cbug-1650263329719","leaves":[{"text":"可以灵活扩展,对于不同业务能够进行","marks":[]},{"text":"快速扩容","marks":[{"type":"bold"}]},{"text":";(易扩展)","marks":[]}]}],"state":{"index":3}},{"type":"block","id":"gsmy-1650275011410","name":"paragraph","data":{},"nodes":[{"type":"text","id":"yzik-1650275011408","leaves":[{"text":"微博2019年的效果:","marks":[]},{"text":"100t+="" 存储、1000+="" 台物理机、10000+="" redis实例、万亿级读写、响应时间="" 20="" 毫秒","marks":[{"type":"fontsize","value":14}]}]}],"state":{}}]"=""> Key的命名规范 <article data-content="[{" type":"block","id":"ozrw-1650016301568","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"h8dl-1650016301569","leaves":[{"text":"把业务名作为前缀,然后用冒号分隔,再加上具体的业务数据名。","marks":[]},{"text":"对于业务名或业务数据名,可以使用相应的英文单词的首字母表示,或者用缩写表示,减少key占用的内存空间。","marks":[{"type":"color","value":"#333333"},{"type":"backgroundcolor","value":"rgb(255,="" 255,="" 255)"},{"type":"fontfamily","value":"arial"},{"type":"fontsize","value":14}]}]}],"state":{}}]"=""> 把业务名作为前缀,然后用冒号分隔,再加上具体的业务数据名。对于业务名或业务数据名,可以使用相应的英文单词的首字母表示,或者用缩写表示,减少key占用的内存空间。 uv:page:1024
<article data-content="[{" type":"block","id":"0st8-1650016287228","name":"heading","data":{"level":"h2"},"nodes":[{"type":"text","id":"hjk3-1650016287227","leaves":[{"text":"避免使用bigkey","marks":[{"type":"bold"},{"type":"fontsize","value":22}]}]}]},{"type":"block","id":"yg8g-1650016302177","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"lzkd-1650016302178","leaves":[{"text":"bigkey的两种情况及应对方式:","marks":[]}]}],"state":{}},{"type":"block","id":"qosc-1650016821656","name":"list-item","data":{"listid":"4xmu-1650016826381","listtype":"unordered","listlevel":1},"nodes":[{"type":"text","id":"pprz-1650016821655","leaves":[{"text":"如果键值对的大小本身就很大,比如value为10kb以上的string类型数据。","marks":[]}]}],"state":{"index":1}},{"type":"block","id":"k6yx-1650016852939","name":"list-item","data":{"listid":"4xmu-1650016826381","listtype":"unordered","listlevel":2},"nodes":[{"type":"text","id":"wucn-1650016852938","leaves":[{"text":"在业务层,","marks":[]},{"text":"尽量将string类型的数据控制在10kb以下","marks":[{"type":"bold"}]},{"text":",比如通过映射为viewmodel存储、通过数据压缩来减少大小等;","marks":[]}]}],"state":{"index":1}},{"type":"block","id":"4mpn-1650016892798","name":"list-item","data":{"listid":"4xmu-1650016826381","listtype":"unordered","listlevel":1},"nodes":[{"type":"text","id":"1vd1-1650016892797","leaves":[{"text":"如果键值对是集合类型="" 且="" 集合元素个数非常多,比如包含了100万个元素的hash集合类型数据。","marks":[]}]}],"state":{"index":2}},{"type":"block","id":"p3rm-1650016958312","name":"list-item","data":{"listid":"4xmu-1650016826381","listtype":"unordered","listlevel":2},"nodes":[{"type":"text","id":"z0nv-1650016958311","leaves":[{"text":"尽量将集合类型的元素个数控制在1万个以下","marks":[{"type":"bold"}]},{"text":"。","marks":[]}]}],"state":{}}]"=""> 避免使用bigkey bigkey的两种情况及应对方式: 如果键值对的大小本身就很大,比如value为10KB以上的String类型数据。 <ul yne-block-type="list"> 在业务层,尽量将String类型的数据控制在10KB以下,比如通过映射为ViewModel存储、通过数据压缩来减少大小等; 如果键值对是集合类型 且 集合元素个数非常多,比如包含了100万个元素的Hash集合类型数据。 尽量将集合类型的元素个数控制在1万个以下。 </ul></article><article data-content="[{" type":"block","id":"nq13-1650017001843","name":"heading","data":{"version":1,"level":"h2"},"nodes":[{"type":"text","id":"eiw3-1650017001841","leaves":[{"text":"使用高效序列化方法和压缩方法","marks":[{"type":"bold"},{"type":"fontsize","value":22}]}]}]},{"type":"block","id":"wepq-1650017006305","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"ghtl-1650017006306","leaves":[{"text":"使用高效的序列化方法和压缩方法","marks":[{"type":"fontsize","value":14},{"type":"bold"}]},{"text":",可以减少="" value="" 的大小。比如,对于xml和json格式的数据,建议使用压缩工具(如gzip="" 或="" snappy)将数据压缩之后再写入redis,这样可以节省内存空间。","marks":[{"type":"fontsize","value":14}]}]}],"state":{}}]"=""> 使用高效序列化方法和压缩方法 使用高效的序列化方法和压缩方法,可以减少 value 的大小。比如,对于XML和JSON格式的数据,建议使用压缩工具(如gzip 或 snappy)将数据压缩之后再写入Redis,这样可以节省内存空间。 </article><article data-content="[{" type":"block","id":"gih9-1650017281889","name":"heading","data":{"version":1,"level":"h2"},"nodes":[{"type":"text","id":"dttj-1650017281880","leaves":[{"text":"使用整数对象共享池","marks":[{"type":"bold"},{"type":"fontsize","value":22}]}]}]},{"type":"block","id":"plms-1650017302362","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"jawz-1650017302363","leaves":[{"text":"整数是常用的数据类型,redis="" 内部维护了="" 0="" 到="" 9999="" 这="" 1="" 万个整数对象,并把这些整数作为一个共享池使用。因此,在满足业务数据需求的前提下,","marks":[{"type":"fontsize","value":14}]},{"text":"能用整数时就尽量用整数","marks":[{"type":"fontsize","value":14},{"type":"bold"}]},{"text":",这样可以节省实例内存。","marks":[{"type":"fontsize","value":14}]}]}],"state":{}},{"type":"block","id":"bd2b-1650017399777","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"nokn-1650017399776","leaves":[{"text":"注意事项:","marks":[{"type":"fontsize","value":14}]},{"text":"共享对象池与maxmemory="" +="" lru策略冲突","marks":[{"type":"fontsize","value":14},{"type":"bold"}]},{"text":"。","marks":[{"type":"fontsize","value":14}]}]}],"state":{}}]"=""> 使用整数对象共享池 整数是常用的数据类型,Redis 内部维护了 0 到 9999 这 1 万个整数对象,并把这些整数作为一个共享池使用。因此,在满足业务数据需求的前提下,能用整数时就尽量用整数,这样可以节省实例内存。 注意事项:共享对象池与maxmemory + LRU策略冲突。 </article></article>
</article></article></article></article></article><article data-content="[{" type":"block","id":"uwmw-1649672350607","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"ka8i-1649672350608","leaves":[{"text":"在应用服务端有时会存在一个事务性操作,需要几个操作都保证事务的原子性。当redis延迟增加,就会拖累应用服务端整个事务的执行。","marks":[]}]}],"state":{}}]"=""><article data-content="[{" type":"block","name":"image","data":{"version":1,"url":"https:="" note.youdao.com="" yws="" res="" 15322="" webresource3a974f12690d162f05a5e38fc33445a6","width":417,"height":197,"style":{"textalign":"center"}},"nodes":[],"state":{"rendersource":"https:="" webresource3a974f12690d162f05a5e38fc33445a6","initialsize":{"width":417,"height":197},"loading":false}}]"=""><article data-content="[{" type":"block","id":"9efd-1649732853416","name":"paragraph","data":{},"nodes":[{"type":"text","id":"yvop-1649732853414","leaves":[{"text":"注意事项:如果","marks":[]},{"text":"只是读操作,即使客户端有多个线程并发执行这两个操作,也不会改变任何值,所以并不需要保证原子性,就不建议把它们放到="" lua="" 脚本中了。如果将过多的操作都放入lua脚本中进行原子操作,也会降低redis的并发性能。","marks":[{"type":"fontsize","value":14}]}]}],"state":{}}]"="">
</article></article></article> 2数据保存规范 <article data-content="[{" type":"block","id":"u8kx-1649672351286","name":"heading","data":{"level":"h2"},"nodes":[{"type":"text","id":"cmdw-1649672351287","leaves":[{"text":"(1)查看redis的响应延迟","marks":[{"type":"bold"},{"type":"fontsize","value":22}]}]}]},{"type":"block","id":"s3zs-1649672874631","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"f7xy-1649672874632","leaves":[{"text":"在某时刻,如果redis实例的响应延迟很高,如果持续时间不长,那可能叫延迟“毛刺”。但如果持续时间较长,那就可以认定redis变慢了。","marks":[]}]}],"state":{}},{"type":"block","id":"0lgt-1649672965051","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"szgi-1649672965042","leaves":[{"text":"这种方法是看="" redis="" 延迟的绝对值,但需要根据不同软硬件环境来综合看待。","marks":[]}]}],"state":{}}]"=""><article data-content="[{" type":"block","id":"wi1m-1649905025863","name":"paragraph","data":{},"nodes":[{"type":"text","id":"ybsg-1649905025864","leaves":[{"text":"redis="" 3.0之后官方提供的切片集群方案,","marks":[]},{"text":"中心思想是","marks":[{"type":"fontsize","value":14}]},{"text":"去中心化","marks":[{"type":"fontsize","value":14},{"type":"bold"}]},{"text":"设计实现。","marks":[{"type":"fontsize","value":14}]}]}],"state":{}}]"=""><article data-content="[{" type":"block","id":"nu06-1649918586793","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"v0w5-1649918586794","leaves":[{"text":"redis="" 6.0之前,","marks":[]},{"text":"从网络io处理到实际的读写命令处理,都是单线程操作","marks":[{"type":"bold"}]},{"text":"。一些非核心的命令操作(如:数据删除、快照生成、aof重写等)使用的后台线程="" 或="" 子进程执行。","marks":[]}]}],"state":{}},{"type":"block","id":"80ei-1649918811954","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"oedv-1649918811945","leaves":[{"text":"redis="" 6.0开始,","marks":[]},{"text":"将网络io处理改为多线程,但是实际的读写命令处理仍然保持单线程","marks":[{"type":"bold"}]},{"text":"。这是因为,redis处理请求时,网络处理经常是瓶颈,通过多个io线程并行处理网络操作,可以提升实例的整体处理性能(主要是吞吐量)。","marks":[]}]}],"state":{}}]"=""><article data-content="[{" type":"block","id":"5orp-1650263189040","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"0tn1-1650263189041","leaves":[{"text":"微博对redis的基本性能改进可以分为两类:避免阻塞="" 和="" 节省内存。","marks":[]}]}],"state":{}},{"type":"block","id":"h43q-1650263503798","name":"heading","data":{"level":"h2"},"nodes":[{"type":"text","id":"sinw-1650263503790","leaves":[{"text":"(1)避免阻塞","marks":[{"type":"bold"},{"type":"fontsize","value":22}]}]}]},{"type":"block","id":"dk0d-1650263517186","name":"list-item","data":{"listid":"qhdp-1650263625041","listtype":"unordered","listlevel":1},"nodes":[{"type":"text","id":"quci-1650263517187","leaves":[{"text":"针对持久化需求,使用="" ","marks":[]},{"text":"全量rdb="" +="" 增量aof复制="" 结合的机制","marks":[{"type":"bold"}]},{"text":",避免数据可靠性或性能降低的问题。(这也是redis4.0之后提供的混合机制)","marks":[]}]}],"state":{"index":1}},{"type":"block","id":"h9my-1650263677231","name":"list-item","data":{"listid":"qhdp-1650263625041","listtype":"unordered","listlevel":1},"nodes":[{"type":"text","id":"epsd-1650263677222","leaves":[{"text":"在aof日志写入磁盘时,用","marks":[]},{"text":"额外的bio线程负责实际的刷盘工作","marks":[{"type":"bold"}]},{"text":",避免aof日志慢速刷盘阻塞主线程的问题。","marks":[]}]}],"state":{"index":2}},{"type":"block","id":"ibis-1650263731763","name":"list-item","data":{"listid":"qhdp-1650263625041","listtype":"unordered","listlevel":1},"nodes":[{"type":"text","id":"ltp3-1650263731761","leaves":[{"text":"增加了aofnumber配置项,用于设置aof文件的数量,","marks":[]},{"text":"控制aof写盘时的文件总量","marks":[{"type":"bold"}]},{"text":",避免写入过多的aof日志文件导致的磁盘写满问题。","marks":[]}]}],"state":{"index":3}},{"type":"block","id":"s0ev-1650263791213","name":"list-item","data":{"listid":"qhdp-1650263625041","listtype":"unordered","listlevel":1},"nodes":[{"type":"text","id":"stpj-1650263791212","leaves":[{"text":"在主从库复制机制上,使用","marks":[]},{"text":"独立的复制线程进行主从库同步","marks":[{"type":"bold"}]},{"text":",避免对主线程的阻塞影响。","marks":[]}]}],"state":{"index":4}},{"type":"block","id":"gzkz-1650263506502","name":"heading","data":{"level":"h2"},"nodes":[{"type":"text","id":"gs5l-1650263506501","leaves":[{"text":"(2)节省内存","marks":[{"type":"bold"},{"type":"fontsize","value":22}]}]}]},{"type":"block","id":"0fw1-1650263517994","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"e6a3-1650263517995","leaves":[{"text":"微博的一个典型优化:","marks":[]},{"text":"定制化数据结构","marks":[{"type":"bold"}]},{"text":"。","marks":[]}]}],"state":{}},{"type":"block","id":"unod-1650274933988","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"ahqw-1650274933987","leaves":[{"text":"优化内容:针对微博用户的关注列表场景,微博定制化设计了longset数据类型,它是一个存储long类型元素的集合。","marks":[]}]}],"state":{}},{"type":"block","id":"88m2-1650274771173","name":"paragraph","data":{},"nodes":[{"type":"text","id":"b0mv-1650274771171","leaves":[{"text":"优化剖析:首先,","marks":[]},{"text":"longset类型的","marks":[{"type":"fontsize","value":14}]},{"text":"底层数据结构是一个hash数组,","marks":[]},{"text":"hash="" 集合类型在保存大量数据时,内存空间消耗较大。其次,在并大请求压力很大的场景下,缓存实例从db读取用户关注列表后用hmset写入hash集合,这个过程会降低redis性能。","marks":[{"type":"fontsize","value":14}]}]}],"state":{}}]"="">
使用Redis保存热数据 <article data-content="[{" type":"block","id":"zrqo-1650017444018","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"le3b-1650017444019","leaves":[{"text":"在实际应用="" redis="" 时,我们会更多地把它作为缓存保存热数据,这样既可以充分利用="" 的高性能特性,还可以把宝贵的内存资源用在服务热数据上,就是俗话说的“好钢用在刀刃上”。","marks":[{"type":"color","value":"#333333"},{"type":"backgroundcolor","value":"rgb(255,="" 255,="" 255)"},{"type":"fontfamily","value":"arial"},{"type":"fontsize","value":14}]}]}],"state":{}}]"=""> 在实际应用 Redis 时,我们会更多地把它作为缓存保存热数据,这样既可以充分利用 Redis 的高性能特性,还可以把宝贵的内存资源用在服务热数据上,就是俗话说的“好钢用在刀刃上”。 </article> 不同的业务数据分实例存储 <article data-content="[{" type":"block","id":"g1au-1650017487470","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"swqd-1650017487471","leaves":[{"text":"假设a业务以写操作为主,b业务以读操作为主,同时使用一个redis实例,读写操作相互干扰,会导致业务响应变慢。因此,","marks":[]},{"text":"建议将不同的业务数据放到不同的redis实例,避免单实例的内存使用量过大","marks":[{"type":"bold"}]},{"text":"。","marks":[]}]}],"state":{}}]"=""> 假设A业务以写操作为主,B业务以读操作为主,同时使用一个Redis实例,读写操作相互干扰,会导致业务响应变慢。因此,建议将不同的业务数据放到不同的Redis实例,避免单实例的内存使用量过大。 </article> 在数据保存时,设置过期时间 <article data-content="[{" type":"block","id":"g1au-1650017487470","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"swqd-1650017487471","leaves":[{"text":"假设a业务以写操作为主,b业务以读操作为主,同时使用一个redis实例,读写操作相互干扰,会导致业务响应变慢。因此,","marks":[]},{"text":"建议将不同的业务数据放到不同的redis实例,避免单实例的内存使用量过大","marks":[{"type":"bold"}]},{"text":"。","marks":[]}]}],"state":{}}]"=""><article data-content="[{" type":"block","id":"3vdr-1650017488192","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"8tik-1650017488193","leaves":[{"text":"在数据保存时,","marks":[]},{"text":"根据业务使用数据的时长,设置数据的过期时间","marks":[{"type":"bold"}]},{"text":"。不然,写入的redis数据会一直占用内存,如果数据持续增多,会造成内存溢出,导致服务崩溃。","marks":[]}]}],"state":{}}]"=""> 在数据保存时,根据业务使用数据的时长,设置数据的过期时间。不然,写入的Redis数据会一直占用内存,如果数据持续增多,会造成内存溢出,导致服务崩溃。 </article> 控制Redis实例的容量 <article data-content="[{" type":"block","id":"g1au-1650017487470","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"swqd-1650017487471","leaves":[{"text":"假设a业务以写操作为主,b业务以读操作为主,同时使用一个redis实例,读写操作相互干扰,会导致业务响应变慢。因此,","marks":[]},{"text":"建议将不同的业务数据放到不同的redis实例,避免单实例的内存使用量过大","marks":[{"type":"bold"}]},{"text":"。","marks":[]}]}],"state":{}}]"=""><article data-content="[{" type":"block","id":"3vdr-1650017488192","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"8tik-1650017488193","leaves":[{"text":"在数据保存时,","marks":[]},{"text":"根据业务使用数据的时长,设置数据的过期时间","marks":[{"type":"bold"}]},{"text":"。不然,写入的redis数据会一直占用内存,如果数据持续增多,会造成内存溢出,导致服务崩溃。","marks":[]}]}],"state":{}}]"=""><article data-content="[{" type":"block","id":"nhaj-1650017791306","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"ff3z-1650017791305","leaves":[{"text":"建议经验,","marks":[]},{"text":"建议单实例内存控制在2~6g","marks":[{"type":"bold"}]},{"text":",这样无论是rdb快照="" 还是="" 集群数据同步,都能很快完成,不阻塞正常请求处理。","marks":[]}]}],"state":{}}]"=""> 建议经验,建议单实例内存控制在2~6G,这样无论是RDB快照 还是 集群数据同步,都能很快完成,不阻塞正常请求处理。 </article></article></article></article></article>
</article></article></article> 3命令使用规范 <article data-content="[{" type":"block","id":"ad84-1650263232352","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"trw1-1650263232353","leaves":[{"text":"微博业务层要保存的数据经常会达到="" tb="" 级别,因此对大容量存储的需求较强。","marks":[]}]}],"state":{}},{"type":"block","id":"jxci-1650275288095","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"ssug-1650275288094","leaves":[{"text":"优化内容:针对","marks":[]},{"text":"数据区分冷热度,","marks":[{"type":"fontsize","value":14}]},{"text":"把热数据保留在="" redis="" 中,而把冷数据通过="" rocksdb="" 写入底层的硬盘","marks":[{"type":"fontsize","value":14},{"type":"bold"}]},{"text":"。","marks":[{"type":"fontsize","value":14}]}]}],"state":{}},{"type":"block","id":"8u9i-1650275320452","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"pait-1650275320450","leaves":[{"text":"优化剖析:有些微博话题刚发生时,热度非常高,会有海量的用户访问这些话题,使用="" 服务用户请求就非常有必要。等到话题热度过了之后,访问人数就会急剧下降,这些数据就变为冷数据了。这个时候,冷数据就可以从="" 迁移到="" rocksdb,从而节省redis实例的内存来主要存储热数据。","marks":[{"type":"fontsize","value":14}]}]}],"state":{}},{"type":"block","id":"jubk-1650275547386","name":"image","data":{"version":1,"url":"https:="" note.youdao.com="" yws="" res="" 16485="" webresource3b3018a7fb4d0b8049443bfdfc8240a3","width":420,"height":297,"style":{"textalign":"center"}},"nodes":[],"state":{"rendersource":"https:="" webresource3b3018a7fb4d0b8049443bfdfc8240a3","initialsize":{"width":420,"height":296},"loading":false}},{"type":"block","id":"mx5j-1650275671183","name":"paragraph","data":{},"nodes":[{"type":"text","id":"iwzc-1650275671184","leaves":[{"text":"举一反三:","marks":[]},{"text":"如果想实现大容量的="" 实例,借助于="" ssd="" 和="" 来实现是一个不错的方案","marks":[{"type":"fontsize","value":14},{"type":"bold"}]},{"text":"。rocksdb="" 可以实现快速写入数据,同时使用内存缓存部分数据,也可以提供万级别的数据读取性能。","marks":[{"type":"fontsize","value":14}]},{"text":"当前="" 的性能提升很快,单块="" 的盘级="" iops="" 可以达到几十万级别。","marks":[{"type":"color","value":"#333333"},{"type":"backgroundcolor","value":"rgb(255,="" 255,="" 255)"},{"type":"fontfamily","value":"arial"},{"type":"fontsize","value":14}]}]}],"state":{}}]"=""> 线上禁用部分命令
<article data-content="[{" type":"block","id":"fgnf-1650018237667","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"z9of-1650018237668","leaves":[{"text":"以下命令可能会导致严重的主线程阻塞:","marks":[]}]}],"state":{}},{"type":"block","id":"euul-1650018292446","name":"list-item","data":{"listid":"tztz-1650018294300","listtype":"unordered","listlevel":1},"nodes":[{"type":"text","id":"z6ja-1650018292445","leaves":[{"text":"keys","marks":[{"type":"bold"}]},{"text":":","marks":[]},{"text":"按照键值对的="" key="" 内容进行匹配,返回符合匹配条件的键值对,该命令需要对="" redis="" 的全局哈希表进行全表扫描,严重阻塞="" 主线程;","marks":[{"type":"fontsize","value":14}]}]}],"state":{"index":1}},{"type":"block","id":"ovbt-1650018295950","name":"list-item","data":{"listid":"tztz-1650018294300","listtype":"unordered","listlevel":1},"nodes":[{"type":"text","id":"bzz0-1650018295948","leaves":[{"text":"flushall","marks":[{"type":"bold"}]},{"text":":","marks":[]},{"text":"删除="" 实例上的所有数据,如果数据量很大,会严重阻塞="" 主线程;","marks":[{"type":"color","value":"#333333"},{"type":"backgroundcolor","value":"rgb(255,="" 255,="" 255)"},{"type":"fontfamily","value":"arial"},{"type":"fontsize","value":14}]}]}],"state":{"index":2}},{"type":"block","id":"08lw-1650018299414","name":"list-item","data":{"listid":"tztz-1650018294300","listtype":"unordered","listlevel":1},"nodes":[{"type":"text","id":"fni7-1650018299413","leaves":[{"text":"flushdb","marks":[{"type":"bold"}]},{"text":":","marks":[]},{"text":"删除当前数据库中的数据,如果数据量很大,同样会阻塞="" 主线程。","marks":[{"type":"color","value":"#333333"},{"type":"backgroundcolor","value":"rgb(255,="" 255)"},{"type":"fontfamily","value":"arial"},{"type":"fontsize","value":14}]}]}],"state":{"index":3}},{"type":"block","id":"hndp-1650018339140","name":"paragraph","data":{},"nodes":[{"type":"text","id":"xtgu-1650018339139","leaves":[{"text":"解决办法:","marks":[{"type":"color","value":"#333333"},{"type":"backgroundcolor","value":"rgb(255,="" 255)"},{"type":"fontfamily","value":"arial"},{"type":"fontsize","value":14}]}]}],"state":{}},{"type":"block","id":"1luv-1650018343741","name":"list-item","data":{"listid":"ttlb-1650018363940","listtype":"unordered","listlevel":1},"nodes":[{"type":"text","id":"hkhi-1650018343740","leaves":[{"text":"管理员用="" ","marks":[]},{"text":"rename-command="" 命令在配置文件中对这些命令进行重命名","marks":[{"type":"bold"}]},{"text":",让客户端无法使用这些命令。","marks":[]}]}],"state":{"index":1}},{"type":"block","id":"clm5-1650018365332","name":"list-item","data":{"listid":"ttlb-1650018363940","listtype":"unordered","listlevel":1},"nodes":[{"type":"text","id":"6ckf-1650018365331","leaves":[{"text":"使用其他命令替代","marks":[]}]}],"state":{"index":2}},{"type":"block","id":"2lud-1650018370041","name":"list-item","data":{"listid":"ttlb-1650018363940","listtype":"unordered","listlevel":2},"nodes":[{"type":"text","id":"mv2c-1650018370039","leaves":[{"text":"keys="">SCAN,分批返回符合条件的键值对,避免主线程阻塞;","marks":[]}]}],"state":{"index":1}},{"type":"block","id":"WExc-1650018393678","name":"list-item","data":{"listId":"tTlB-1650018363940","listType":"unordered","listLevel":2},"nodes":[{"type":"text","id":"YbCi-1650018393677","leaves":[{"text":"FLUSHALL/FLUSHDB => 加上ASYNC选项,使用后台线程异步删除,避免主线程阻塞;","marks":[]}]}],"state":{}}]"> 以下命令可能会导致严重的主线程阻塞: KEYS:按照键值对的 key 内容进行匹配,返回符合匹配条件的键值对,该命令需要对 Redis 的全局哈希表进行全表扫描,严重阻塞 Redis 主线程; FLUSHALL:删除 Redis 实例上的所有数据,如果数据量很大,会严重阻塞 Redis 主线程; FLUSHDB:删除当前数据库中的数据,如果数据量很大,同样会阻塞 Redis 主线程。 解决办法: 管理员用 rename-command 命令在配置文件中对这些命令进行重命名,让客户端无法使用这些命令。 使用其他命令替代 <ul yne-block-type="list"> KEYS => SCAN,分批返回符合条件的键值对,避免主线程阻塞; FLUSHALL/FLUSHDB => 加上ASYNC选项,使用后台线程异步删除,避免主线程阻塞; </ul></article> 慎用MONITOR命令 <article data-content="[{" type":"block","id":"xd8a-1650018266208","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"ssfm-1650018266209","leaves":[{"text":"monitor="" 命令会把监控到的内容持续写入输出缓冲区。","marks":[]},{"text":"如果线上命令的操作很多,输出缓冲区很快就会溢出了","marks":[{"type":"bold"}]},{"text":",这就会对="" redis="" 性能造成影响,甚至引起服务崩溃。","marks":[]}]}],"state":{}},{"type":"block","id":"8x9b-1650018465881","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"2xly-1650018465879","leaves":[{"text":"除非十分需要监测某些命令的执行(例如,redis="" 性能突然变慢,我们想查看下客户端执行了哪些命令),可以","marks":[]},{"text":"偶尔在短时间内使用下="" monitor="" 命令","marks":[{"type":"bold"}]},{"text":",否则,建议不要使用="" 命令。","marks":[]}]}],"state":{}}]"=""> MONITOR 命令会把监控到的内容持续写入输出缓冲区。如果线上命令的操作很多,输出缓冲区很快就会溢出了,这就会对 Redis 性能造成影响,甚至引起服务崩溃。 除非十分需要监测某些命令的执行(例如,Redis 性能突然变慢,我们想查看下客户端执行了哪些命令),可以偶尔在短时间内使用下 MONITOR 命令,否则,建议不要使用 MONITOR 命令。 </article> 慎用全量操作命令 <article data-content="[{" type":"block","id":"mj3y-1650018266826","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"4enm-1650018266827","leaves":[{"text":"想要获得集合中的所有元素,","marks":[]},{"text":"一般不建议使用全量操作的命令","marks":[{"type":"bold"}]},{"text":"(例如="" hash="" 类型的="" hgetall、set="" smembers)。这些操作会对="" 和="" set="" 类型的底层数据结构进行全量扫描,如果集合类型数据较多的话,就会阻塞="" redis="" 主线程。","marks":[]}]}],"state":{}},{"type":"block","id":"uirp-1650018507209","name":"paragraph","data":{"version":1},"nodes":[{"type":"text","id":"imgw-1650018507207","leaves":[{"text":"解决办法:","marks":[]}]}],"state":{}},{"type":"block","id":"9k7i-1650018514442","name":"list-item","data":{"listid":"wh2z-1650018516033","listtype":"unordered","listlevel":1},"nodes":[{"type":"text","id":"ep3v-1650018514441","leaves":[{"text":"使用sscan、hscan命令分批返回集合中的数据","marks":[{"type":"bold"}]},{"text":",减少对主线程的阻塞;","marks":[]}]}],"state":{"index":1}},{"type":"block","id":"b8a0-1650018535985","name":"list-item","data":{"listid":"wh2z-1650018516033","listtype":"unordered","listlevel":1},"nodes":[{"type":"text","id":"phio-1650018535983","leaves":[{"text":"把一个大的hash集合拆分为多个小的hash集合","marks":[{"type":"bold"}]},{"text":";对应到业务层,就是对业务数据进行拆分,按照一定维度将一个大集合的业务数据拆分为多个小集合数据;","marks":[]}]}],"state":{"index":2}},{"type":"block","id":"ukfl-1650018578420","name":"list-item","data":{"listid":"wh2z-1650018516033","listtype":"unordered","listlevel":1},"nodes":[{"type":"text","id":"gxzo-1650018578418","leaves":[{"text":"如果集合类型保存的是业务数据的多个属性,而每次查询后也需要返回这些属性。这时,建议","marks":[]},{"text":"使用string类型将这些属性序列化后保存,每次直接返回string数据就行","marks":[{"type":"bold"}]},{"text":",避免做全量扫描;","marks":[]}]}],"state":{}}]"=""> 想要获得集合中的所有元素,一般不建议使用全量操作的命令(例如 Hash 类型的 HGETALL、Set 类型的 SMEMBERS)。这些操作会对 Hash 和 Set 类型的底层数据结构进行全量扫描,如果集合类型数据较多的话,就会阻塞 Redis 主线程。 解决办法: 使用SSCAN、HSCAN命令分批返回集合中的数据,减少对主线程的阻塞; 把一个大的Hash集合拆分为多个小的Hash集合;对应到业务层,就是对业务数据进行拆分,按照一定维度将一个大集合的业务数据拆分为多个小集合数据; 如果集合类型保存的是业务数据的多个属性,而每次查询后也需要返回这些属性。这时,建议使用String类型将这些属性序列化后保存,每次直接返回String数据就行,避免做全量扫描; </article></article> 一张图总结: 强制类别的规范:这表示,如果不按照规范内容来执行,就会给 Redis 的应用带来极大的负面影响,例如性能受损。 推荐类别的规范:这个规范的内容能有效提升性能、节省内存空间,或者是增加开发和运维的便捷性,你可以直接应用到实践中。 建议类别的规范:这类规范内容和实际业务应用相关,需要结合自己的业务场景参考使用。 参考资料 极客时间,蒋德钧《Redis核心技术与实战》 黄建宏,《Redis设计与实现》 拉钩教育,刘海丰《架构设计面试精讲》 免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |