热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

microservice(4):漫谈使用Redis作为高速缓存

Redis适合哪些业务场景常规业务系统的数据库访问中,读写操作的比例一般在7/3到9/1,也就是说读操作远多于写操作,因此高并发系统设计里,通过No

Redis适合哪些业务场景
常规业务系统的数据库访问中,读写操作的比例一般在7/3到9/1,也就是说读操作远多于写操作,因此高并发系统设计里,通过NoSQL技术将热点数据(短期内变动概率小的数据)放入内存以达到减轻DB压力,提升数据访问速度的目的,Redis和MongoDB是当下应用最广泛的NoSQL产品,当然如果系统里的写操作居多,也没有必要使用缓存,因此Redis主要用于解决访问性能和并发能力的问题。除了纯数据缓存的作用之外,得益于其超高速的响应能力,Redis也常用于提供分布式锁的解决方案。

 

哪些设计思路保证了Redis高性能
单个Redis server对请求的处理是基于单线程工作模型的,但由于是纯内存操作,并且单线程的工作模式避免了线程上下文切换带来的额外开销,同时使用NIO多路复用机制(单线程维护多个I/O socket的状态,socket event handler统一进行event分发,并通知到各个event listener),所以即使是单台Redis server的性能也是非常的快,可支持11万次/秒的SET操作,8.1万次/秒的GET操作。

高并发系统里有时候单台Redis server不能满足性能需求,Redis 3.0之前的办法是Redis Sentinel,多个节点同时提供服务,并且每个节点都保存全量数据,Redis 3.0之后引入了Redis Cluster,通过数据分片(Data Sharding)在每个节点上只保留部分数据(总共有16384个slot)来实现高可用。

Redis Cluster采用无中心节点方式实现,客户端直接与redis集群的每个节点连接,客户请求到达节点之后,使用统一的哈希算法,CRC16(key)%16384,计算出key对应的slot,然后从Redis Cluster定位出具体的server,具体的data sharding,最终将数据返回给用户 。但由于Redis的事务仅能解决单台server上的ACID问题,对于多台server常见的问题是多个请求针对同一个key的操作一致性问题,需要结合Zookeeper使用分布式锁的机制解决一致性问题和顺序操作问题。

Redis支持动态添加和删除节点,动态迁移和再平衡slot,动态重新选举Leader并进行fail-over;每一个节点虽然只保存一部分的slot,但会保存一份相同的data sharding mapping table,这张表记录所有16384个slot的host分布位置,并且节点之间会定期同步更新这张表的信息,这样的设计可以保证Redis cluster内部节点之间只需要很少的信息就可以相互同步信息。客户端访问Redis Cluster的时候会指定集群内的一台host:port,如果操作的key不在当前的host上,则host会根据data sharding mapping table告诉客户端正确的host:port;如果访问的host:port下线了,则客户端的链接自动转移到对应的master或者slave节点。

micro-service(4):漫谈使用Redis作为高速缓存 - 文章图片

 

Redis中各种数据类型的使用场景
Redis的数据存储主要通过key/value实现,key都是string类型,value则分不同的应用场景有五种类型定义:
#1 string类型:可以包含任何数据(jps图片或者经过序列化的对象,单个key最大可以存储512M的数据),具有全局统计功能的数据,如全局ID生成器、集群配置信息等;
#2 hash类型:用于存储对象结构的数据,多个field绑定到一个key上(对比使用string类型存储对象的优势在于hash类型可以直接update具体field的值而不影响其他field),如实现SSO,COOKIE为key,用户信息为value,并有指定过期时间;
#3 list类型:用于存储需要基于队列或者栈操作的系列数据,如消息队列;
#4 set类型:用于存储需要维护一个全局不重复的集合,如服务注册发现,可以实现全局去重的功能,如访问网页的独立IP,共同好友等;
#5 zset类型:用于存储需要维护一个全局不重复但有权重排序的列表可以使用SORTED SET,如积分排行榜、带权重的消息队列。

对上述的字段类型都可以进行的类似的操作,
设置一个值:[set|hmset|lpush|sadd] key value
获取一个值:[get|hget] key
删除一个值:[del|hdel] key
设置一个具有过期时间的值:[setex] key time value
如果值不存在就设置这个值:[setnx] key value
查找redis中的keys或者pattern:[keys/scan] key
判断一个值是否存在:[exists|hexists] key
给指定值设置过期时间:[expire] key seconds
将指定key的value加1|减1:[incr|decr] key
将一个key\value迁移到指定server:[migrate] host port key dest-db timeout [copy] [replace]

HyperLogLog用于做基数统计(基于set类型的封装,仅根据输入的独立元素个数进行统计,而不存储元素本身),可以保证在输入元素数量或者体积非常大的时候可以保证统计所需的空间固定为12kb(最大2^64个元素) 。
在指定的key中添加基数:[pfadd] key value
统计指定key中不同基数的个数:[pfcount] key
将sourceKey的技术合并到destKey的基数统计中:[pfmerge] destKey sourceKey

Pub/Sub用于做消息的发布订阅(基于list类型的封装,将消息封装成list的节点)。
创建一个信息接收channel:[subscribe] channel
向指定的channel发送一个信息:[publish] channel message

单个redis命令的执行具有原子性,对于多个命令而言redis提供基础事务机制,但是不保证多个命令执行的原子性,一个典型的redis事务如下:
开启一个事务:[multi]
之后可以计划多条redis命令,但并不会执行
提交并执行之前的所有命令:[exec]
开始执行之前计划的redis命令,如果其中某条命令执行失败并不会影响其他命令的执行
取消执行事务块内所有计划的redis命令:[discard]
监视一个或者多个key:[watch] key
表示在执行exec之前如果key被事务之前的命令修改,则当前事务被discard。

 

Redis数据过期策略和内存回收策略
针对已经过期的数据Redis采用定期删除和延迟删除结合的策略,但是两者都有缺陷;由于定期检查所有的key是否过期会带来性能问题,因此定期删除策略使用的是随机抽查,另外在操作Key前会判断是否已经过期,如过期则立即删除;这样的策略会导致一些已经过期的key还堆积在内存里,使得redis server内存占用率居高不下,因此需要结合redis.conf中的maxmemory-policy配置使用,也就是当redis server的内存不足以写入新数据时的内存回收策略,
#1 noeviction:表示直接报错;
#2 allkeys-lur:表示在所有keys中根据LRU删除key;
#3 allkeys-random:表示在所有keys中随机删除key;
#4 volatile-lru/volatile-random/volatile-ttl用于当redis server既充当cache又当DB的时候,表示在设置了expire date的keys中进行删除,ttl表示删除拥有更早过期时间的key。

 

解决Redis缓存穿透和缓存雪崩问题
缓存穿透和雪崩可以看做一个问题,只是严重程度不同;当一个请求到达redis之后发现没有对应的缓存数据,然后向DB发送数据请求,如果能获取到数据那问题就停留在了缓存穿透上,DB获取到的数据会缓存到redis上;如果DB中也没有对应的数据,并且当这样的请求达到一定数量级并且耗用完所有的DB资源,最终导致DB连接异常就出现了缓存雪崩问题。
解决缓存穿透问题的思路有下述几种,不管是否从DB中查找到对应的值(没有值就为null),都在redis中记录一条缓存记录;在Dao层维护一张BitMap,用bit记录对应的key是否有对应值,从而避免冗余的DB操作;后台线程专门用于更新即将过期的Redis数据,从而避免缓存穿透。
解决缓存雪崩问题的思路有下述几种,在DB Connection上添加互斥锁,这样当大量缓存请求失效的时候需要排队去DB请求数据;对设置了相同过期时间的数据设置一个随机值,避免数据集体失效;使用双缓存或者多层缓存策略,需要配合缓存预热。

 


推荐阅读
  • 如何查询zone下的表的信息
    本文介绍了如何通过TcaplusDB知识库查询zone下的表的信息。包括请求地址、GET请求参数说明、返回参数说明等内容。通过curl方法发起请求,并提供了请求示例。 ... [详细]
  • Spring源码解密之默认标签的解析方式分析
    本文分析了Spring源码解密中默认标签的解析方式。通过对命名空间的判断,区分默认命名空间和自定义命名空间,并采用不同的解析方式。其中,bean标签的解析最为复杂和重要。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • 本文介绍了Windows操作系统的版本及其特点,包括Windows 7系统的6个版本:Starter、Home Basic、Home Premium、Professional、Enterprise、Ultimate。Windows操作系统是微软公司研发的一套操作系统,具有人机操作性优异、支持的应用软件较多、对硬件支持良好等优点。Windows 7 Starter是功能最少的版本,缺乏Aero特效功能,没有64位支持,最初设计不能同时运行三个以上应用程序。 ... [详细]
  • Android系统源码分析Zygote和SystemServer启动过程详解
    本文详细解析了Android系统源码中Zygote和SystemServer的启动过程。首先介绍了系统framework层启动的内容,帮助理解四大组件的启动和管理过程。接着介绍了AMS、PMS等系统服务的作用和调用方式。然后详细分析了Zygote的启动过程,解释了Zygote在Android启动过程中的决定作用。最后通过时序图展示了整个过程。 ... [详细]
  • 基于Socket的多个客户端之间的聊天功能实现方法
    本文介绍了基于Socket的多个客户端之间实现聊天功能的方法,包括服务器端的实现和客户端的实现。服务器端通过每个用户的输出流向特定用户发送消息,而客户端通过输入流接收消息。同时,还介绍了相关的实体类和Socket的基本概念。 ... [详细]
  • 微软头条实习生分享深度学习自学指南
    本文介绍了一位微软头条实习生自学深度学习的经验分享,包括学习资源推荐、重要基础知识的学习要点等。作者强调了学好Python和数学基础的重要性,并提供了一些建议。 ... [详细]
  • 基于PgpoolII的PostgreSQL集群安装与配置教程
    本文介绍了基于PgpoolII的PostgreSQL集群的安装与配置教程。Pgpool-II是一个位于PostgreSQL服务器和PostgreSQL数据库客户端之间的中间件,提供了连接池、复制、负载均衡、缓存、看门狗、限制链接等功能,可以用于搭建高可用的PostgreSQL集群。文章详细介绍了通过yum安装Pgpool-II的步骤,并提供了相关的官方参考地址。 ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • Linux重启网络命令实例及关机和重启示例教程
    本文介绍了Linux系统中重启网络命令的实例,以及使用不同方式关机和重启系统的示例教程。包括使用图形界面和控制台访问系统的方法,以及使用shutdown命令进行系统关机和重启的句法和用法。 ... [详细]
  • 本文讨论了在Windows 8上安装gvim中插件时出现的错误加载问题。作者将EasyMotion插件放在了正确的位置,但加载时却出现了错误。作者提供了下载链接和之前放置插件的位置,并列出了出现的错误信息。 ... [详细]
  • Android Studio Bumblebee | 2021.1.1(大黄蜂版本使用介绍)
    本文介绍了Android Studio Bumblebee | 2021.1.1(大黄蜂版本)的使用方法和相关知识,包括Gradle的介绍、设备管理器的配置、无线调试、新版本问题等内容。同时还提供了更新版本的下载地址和启动页面截图。 ... [详细]
  • JavaSE笔试题-接口、抽象类、多态等问题解答
    本文解答了JavaSE笔试题中关于接口、抽象类、多态等问题。包括Math类的取整数方法、接口是否可继承、抽象类是否可实现接口、抽象类是否可继承具体类、抽象类中是否可以有静态main方法等问题。同时介绍了面向对象的特征,以及Java中实现多态的机制。 ... [详细]
  • 本文介绍了Oracle数据库中tnsnames.ora文件的作用和配置方法。tnsnames.ora文件在数据库启动过程中会被读取,用于解析LOCAL_LISTENER,并且与侦听无关。文章还提供了配置LOCAL_LISTENER和1522端口的示例,并展示了listener.ora文件的内容。 ... [详细]
author-avatar
独孤依人x_762
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有