分布式为什么使用Redis

一 为什么使用 Redis

在项目中使用 Redis,主要考虑两个角度:性能和并发。如果只是为了分布式锁这些其他功能,还有其他中间件 Zookpeer 等代替,并非一定要使用 Redis。

性能:

如下图所示,我们在碰到需要执行耗时特别久,且结果不频繁变动的 SQL,就特别适合将运行结果放入缓存。这样,后面的请求就去缓存中读取,使得请求能够迅速响应。

特别是在秒杀系统,在同一时间,几乎所有人都在点,都在下单。。。执行的是同一操作———向数据库查数据。

根据交互效果的不同,响应时间没有固定标准。在理想状态下,我们的页面跳转需要在瞬间解决,对于页内操作则需要在刹那间解决。

 并发:

如下图所示,在大并发的情况下,所有的请求直接访问数据库,数据库会出现连接异常。这个时候,就需要使用 Redis 做一个缓冲操作,让请求先访问到 Redis,而不是直接访问数据库。

使用 Redis 的常见问题

  • 缓存和数据库双写一致性问题
  • 缓存雪崩问题
  • 缓存击穿问题
  • 缓存的并发竞争问题

二 单线程的 Redis 为什么这么快

这个问题是对 Redis 内部机制的一个考察。很多人都不知道 Redis 是单线程工作模型。

原因主要是以下三点:

  • 纯内存操作
  • 单线程操作,避免了频繁的上下文切换
  • 采用了非阻塞 I/O 多路复用机制

仔细说一说 I/O 多路复用机制,打一个比方:小名在 A 城开了一家快餐店店,负责同城快餐服务。小明因为资金限制,雇佣了一批配送员,然后小曲发现资金不够了,只够买一辆车送快递。

经营方式一

客户每下一份订单,小明就让一个配送员盯着,然后让人开车去送。慢慢的小曲就发现了这种经营方式存在下述问题:

 

时间都花在了抢车上了,大部分配送员都处在闲置状态,抢到车才能去送。

  • 随着下单的增多,配送员也越来越多,小明发现快递店里越来越挤,没办法雇佣新的配送员了。
  • 配送员之间的协调很花时间。
  • 综合上述缺点,小明痛定思痛,提出了经营方式二。

经营方式二

小明只雇佣一个配送员。当客户下单,小明按送达地点标注好,依次放在一个地方。最后,让配送员依次开着车去送,送好了就回来拿下一个。上述两种经营方式对比,很明显第二种效率更高。

在上述比喻中:

  • 每个配送员→每个线程
  • 每个订单→每个 Socket(I/O 流)
  • 订单的送达地点→Socket 的不同状态
  • 客户送餐请求→来自客户端的请求
  • 明曲的经营方式→服务端运行的代码
  • 一辆车→CPU 的核数

于是有了如下结论:

  • 经营方式一就是传统的并发模型,每个 I/O 流(订单)都有一个新的线程(配送员)管理。
  • 经营方式二就是 I/O 多路复用。只有单个线程(一个配送员),通过跟踪每个 I/O 流的状态(每个配送员的送达地点),来管理多个 I/O 流。

下面类比到真实的 Redis 线程模型,如图所示:

Redis-client 在操作的时候,会产生具有不同事件类型的 Socket。在服务端,有一段 I/O 多路复用程序,将其置入队列之中。然后,文件事件分派器,依次去队列中取,转发到不同的事件处理器中。

三 Redis 的数据类型及使用场景

一个合格的程序员,这五种类型都会用到。

String

最常规的 set/get 操作,Value 可以是 String 也可以是数字。一般做一些复杂的计数功能的缓存。

Hash

这里 Value 存放的是结构化的对象,比较方便的就是操作其中的某个字段。我在做单点登录的时候,就是用这种数据结构存储用户信息,以 CookieId 作为 Key,设置 30 分钟为缓存过期时间,能很好的模拟出类似 Session 的效果。

List

使用 List 的数据结构,可以做简单的消息队列的功能。另外,可以利用 lrange 命令,做基于 Redis 的分页功能,性能极佳,用户体验好。

Set

因为 Set 堆放的是一堆不重复值的集合。所以可以做全局去重的功能。我们的系统一般都是集群部署,使用 JVM 自带的 Set 比较麻烦。另外,就是利用交集、并集、差集等操作,可以计算共同喜好,全部的喜好,自己独有的喜好等功能。

Sorted Set

Sorted Set 多了一个权重参数 Score,集合中的元素能够按 Score 进行排列。可以做排行榜应用,取 TOP N 操作。Sorted Set 可以用来做延时任务。

四 Redis 的过期策略和内存淘汰机制

Redis 是否用到家,从这就能看出来。比如你 Redis 只能存 5G 数据,可是你写了 10G,那会删 5G 的数据。怎么删的,这个问题思考过么?

正解:Redis 采用的是定期删除+惰性删除策略。

为什么不用定时删除策略

定时删除,用一个定时器来负责监视 Key,过期则自动删除。虽然内存及时释放,但是十分消耗 CPU 资源。在大并发请求下,CPU 要将时间应用在处理请求,而不是删除 Key,因此没有采用这一策略。

定期删除+惰性删除如何工作

定期删除,Redis 默认每个 100ms 检查,有过期 Key 则删除。需要说明的是,Redis 不是每个 100ms 将所有的 Key 检查一次,而是随机抽取进行检查。如果只采用定期删除策略,会导致很多 Key 到时间没有删除。于是,惰性删除派上用场。

采用定期删除+惰性删除就没其他问题了么

不是的,如果定期删除没删除掉 Key。并且你也没及时去请求 Key,也就是说惰性删除也没生效。这样,Redis 的内存会越来越高。那么就应该采用内存淘汰机制。

在 redis.conf 中有一行配置:

# maxmemory-policy volatile-lru

该配置就是配内存淘汰策略的:

  • noeviction:当内存不足以容纳新写入数据时,新写入操作会报错。
  • allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的 Key。(推荐使用,目前项目在用这种)(最近最久使用算法)
  • allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个 Key。(应该也没人用吧,你不删最少使用 Key,去随机删)
  • volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的 Key。这种情况一般是把 Redis 既当缓存,又做持久化存储的时候才用。(不推荐)
  • volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个 Key。(依然不推荐)
  • volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的 Key 优先移除。(不推荐)

五 Redis 和数据库双写一致性问题

一致性问题还可以再分为最终一致性和强一致性。数据库和缓存双写,就必然会存在不一致的问题。前提是如果对数据有强一致性要求,不能放缓存。我们所做的一切,只能保证最终一致性。

 

另外,我们所做的方案从根本上来说,只能降低不一致发生的概率。因此,有强一致性要求的数据,不能放缓存。首先,采取正确更新策略,先更新数据库,再删缓存。其次,因为可能存在删除缓存失败的问题,提供一个补偿措施即可,例如利用消息队列。

六 如何应对缓存穿透和缓存雪崩问题

这两个问题,一般中小型传统软件企业很难碰到。如果有大并发的项目,流量有几百万左右,这两个问题一定要深刻考虑。缓存穿透,即黑客故意去请求缓存中不存在的数据,导致所有的请求都怼到数据库上,从而数据库连接异常。

缓存穿透解决方案:

  • 利用互斥锁,缓存失效的时候,先去获得锁,得到锁了,再去请求数据库。没得到锁,则休眠一段时间重试。
  • 采用异步更新策略,无论 Key 是否取到值,都直接返回。Value 值中维护一个缓存失效时间,缓存如果过期,异步起一个线程去读数据库,更新缓存。需要做缓存预热(项目启动前,先加载缓存)操作。
  • 提供一个能迅速判断请求是否有效的拦截机制,比如,利用布隆过滤器,内部维护一系列合法有效的 Key。迅速判断出,请求所携带的 Key 是否合法有效。如果不合法,则直接返回。

 

缓存雪崩,即缓存同一时间大面积的失效,这个时候又来了一波请求,结果请求都怼到数据库上,从而导致数据库连接异常。

缓存雪崩解决方案:

  • 给缓存的失效时间,加上一个随机值,避免集体失效。
  • 使用互斥锁,但是该方案吞吐量明显下降了。
  • 双缓存。我们有两个缓存,缓存 A 和缓存 B。缓存 A 的失效时间为 20 分钟,缓存 B 不设失效时间。自己做缓存预热操作。
  • 然后细分以下几个小点:从缓存 A 读数据库,有则直接返回;A 没有数据,直接从 B 读数据,直接返回,并且异步启动一个更新线程,更新线程同时更新缓存 A 和缓存 B。

八 如何解决 Redis 的并发竞争 Key 问题

这个问题大致就是,同时有多个子系统去 Set 一个 Key。这个时候要注意什么呢?大家基本都是推荐用 Redis 事务机制。

 

但是并不推荐使用 Redis 的事务机制。因为我们的生产环境,基本都是 Redis 集群环境,做了数据分片操作。你一个事务中有涉及到多个 Key 操作的时候,这多个 Key 不一定都存储在同一个 redis-server 上。因此,Redis 的事务机制,十分鸡肋。

 

如果对这个 Key 操作,不要求顺序

这种情况下,准备一个分布式锁,大家去抢锁,抢到锁就做 set 操作即可,比较简单。

 

如果对这个 Key 操作,要求顺序

假设有一个 key1,系统 A 需要将 key1 设置为 valueA,系统 B 需要将 key1 设置为 valueB,系统 C 需要将 key1 设置为 valueC。

 

期望按照 key1 的 value 值按照 valueA > valueB > valueC 的顺序变化。这种时候我们在数据写入数据库的时候,需要保存一个时间戳。

 

假设时间戳如下

系统 A key 1 {valueA 3:00}

系统 B key 1 {valueB 3:05}

系统 C key 1 {valueC 3:10}

 

那么,假设系统 B 先抢到锁,将 key1 设置为{valueB 3:05}。接下来系统 A 抢到锁,发现自己的 valueA 的时间戳早于缓存中的时间戳,那就不做 set 操作了,以此类推。其他方法,比如利用队列,将 set 方法变成串行访问也可以。

转载于:https://www.cnblogs.com/liuqingzheng/p/10001279.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/464247.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

困扰多年的PCB散热问题终于可以解决了!

做电子的同学应该都会知道电路板,电路板分为基板导电部分组成。如下图,我们普通的单层电路板是一块有机材料加上导电铜层组成的。在大学那会我们会用面包板来做一些基础的实验,用面包板做实验可以减少打PCB板的时间,因为制作一个P…

进程之间的通信方式-共享内存

共享内存是被多个进程共享的一部分物理内存。共享内存是进程间共享数据的一种最快的方法,一个进程向共享内存区域写入了数据,共享这个内存区域的所有进程就可以立刻看到其中的内容。原理图如下: 共享内存的实现分为两个步骤: 一、…

和一个刚毕业不久的朋友聊天

我记得还在你上大学的时候,我们就开始联系了,想不到的是你已经毕业半年多。我相信每个人都会有经历从学生到工作的过程,当然肯定也会因为工作后面对很多不一样的人和事,我也希望能够通过我们的一次对话,让没有毕业的同…

ListView中convertView和ViewHolder的工作原理

w和Adapter参考:http://www.cnblogs.com/xiaowenji/archive/2010/12/08/1900579.html工作原理:1.ListView针对List中每个item,要求adapter给我一个视图(getView)2.一个新的视图被返回并显示如果我们有上亿个item要显示怎么办?为每…

RTOS 任务间互斥的难题

作者 | strongerHuang微信公众号 | 嵌入式专栏在基于RTOS开发项目时,通常都会遇到互斥的情况,比如:几个任务都要使用一个UART串口进行发送数据。如果不加互斥锁,优先级高的任务,会抢占串口并发送数据,则有可…

android中绘图Paint.setAntiAlias()和Paint.setDither()方法的作用

转载:http://blog.csdn.net/lovexieyuan520/article/details/50732023 在自定义View中,这两个Paint.setAntiAlias()和Paint.setDither()方法用的很多,都只有一个boolean值,作用大家未必清楚,今天抽了点时间研究下&…

简单分析STM32和51的区别

分享本文,简单分析STM32与51单片机的区别与取舍之处。单片微型计算机简称单片机,简单来说就是集CPU(运算、控制)、RAM(数据存储-内存)、ROM(程序存储)、输入输出设备(串口…

深圳,四月

我是一个工程师,每天有干不完的活,做不完的事情,还有加不完的班!认识的一个同学要去北京,我们昨天一块吃饭,他说了未来的计划,他说让我去北京了去找他,他还说让我把地址给他&#xf…

OOB与COM交互读写本地文件

相信读过前几篇Silverlight Out of Browser实例教程的朋友已经对Silverlight的OOB应用有了一定的认识和了解。今天,我们将讨论Silverlight Out of Browser的另外一个功能实例,Silverlight OOB与COM API的交互操作。在开始实例之前,我们先了解…

在Google工作的十年,也是我开心的十年

大家好,我是写代码的篮球球痴,昨晚看到bug君发的这篇文章,文章的主人公在Google工作了10年,我认真看了文章里的每一个字,我特别羡慕这样的工作和环境,当然,我相信我们有一天也会是这样的&#x…

纠结

几年前,我一同事回家创业,当时我们一起做软件开发,他觉得上班没意思,就决定回老家创业,当时他创业的项目是零售项目,离职回去筹备了几个月,跟我们看到的创业老板一样,当时他也去挖了…

RTSP的报文结构

RTSP的报文结构 RTSP是一种基于文本的协议,用CRLF作为一行的结束符。使用基于文本协议的好处在于我们可以随时在使用过程中的增加自定义的参数,也可以随便将协议包抓住很直观的进行分析。 RTSP有两类报文:请求报文和响应报文。请求报文是指从…

Qt性能优化,一位资深Qt工程师的经验

Qt是一个1991年由Qt Company开发的跨平台C图形用户界面应用程序开发框架。它既可以开发GUI程序,也可用于开发非GUI程序,比如控制台工具和服务器。Qt 支持的操作系统有很多,例如通用操作系统 Windows、Linux、Unix,智嵌入式系统 QN…

计算机指令称,通俗解释什么叫计算机指令?

实际上能接触到“计算机指令”的人,只是少数电脑工程师。现在一般的电脑程序员都已经无需使用计算机指令了,因为他们编程序已经使用高级的计算机语言了。下面我尽量用通俗的语言来试着解释。计算机由CPU、内存、硬盘、显示器和键盘等部件组成。计算机软件…

STM32和Arduino对比,谁更厉害?

前两天有一个同学问我嵌入式的学习路线他是一个大三的学生,我看了他的简历,里面几乎没什么项目经验,对于企业来说这样的学生就好比一张白纸一样,当然,这样的学生很难拿到一份好的offer。学习路线的文章我记得已经发了很…

人大计算机在职考研好考吗,人大在职研究生好考吗?通过率高吗?

答:最近咨询人大在职研究生进行学习的人非常多,其中咨询最多的问题是考试难度及通过率。下面就通过在职研究生招生信息网为大家介绍一下,人大在职研究生好考吗?通过率高吗?人大在职研究生招生是采用同等学力申硕的方式,该方式是…

可能你还不懂浮点数

在网上看到一个问题然后看到这篇关于浮点数的文章,希望大家看了之后有所启发想一下,为什么第一个打印的和预设值不同,但是第二个是相同的?如图:尾数部分是如何转变成二进制的?前言很多人在初学写程式时都会…

RTP协议的封装

最近一段时间学习了RTP协议相关的内容,一方面为了将自己学到的部分记录下来,便于后续查找,另一方面用于记录笔记 一个协议的封装是为了满足协议的功能需求的。从前面提出的功能需求,可以推测出RTP封装中应该有同步源和时戳等字段&…

Alpha冲刺(7/10)

团队信息 队名:爸爸饿了组长博客:here作业博客:here组员情况 组员1(组长):王彬 过去两天完成了哪些任务 学会了POSTMAN的使用,对后端已经完成的接口进行了收发消息正确性的验证推进项目进度&…

epoll模型之服务器设计

Linux2.6内核中提高网络I/O性能的新方法-epoll I/O多路复用技术在比较多的TCP网络服务器中有使用,即比较多的用到select函数。1、为什么select落后首先,在Linux内核中,select所用到的FD_SET是有限的,即内核中有个参数__FD_SETSIZE…