Redis是啥?用Redis官方的话来说就是:

Redis is an open source (BSD licensed), in-memory data structure store, used as a database, cache and message broker.

Redis是一个开源的、基于内存数据结构存储器,可以用作数据库缓存消息中间件

What??? 这玩意把数据放在内存,还想当数据库使?为什么是“data structure store”,而不是“data store”?还能用作消息中间件??你这么牛,你咋不上天?

是的,Redis就是这么牛 ( ̄▽ ̄)~*

我们只需从Redis最常用的功能——缓存,开始了解,上面那些问题也就迎刃而解了。

如果你是Redis新手,或者此前从未接触过Redis,那么这篇文章不仅能帮你快速了解Redis的实现原理,还能帮你了解一些架构设计的艺术;如果你是Redis老司机,那么,希望这篇文章能带给你一些新的东西。

1、你会怎样实现一个缓存?

假设让你设计一个缓存,你会怎么做?

相信大家都会想到用Map来实现,就像这样:

String value = map.get("someKey");if(null == value) { value = queryValueFromDB("someKey");}

那用什么Map呢?HashMap、TreeMap这些都线程不安全,那就用HashTable或者ConcurrentHashMap好了。

不管你用什么样的Map,它的背后都是key-value的Hash表结构,目的就是为了实现O(1)复杂度的查找算法,Redis也是这样实现的,另一个常用的缓存框架Memcached也是。

Hash表的数据结构是怎样的呢?相信很多人都知道,这里简单画个图:

简单说,Hash表就是一个数组,而这个数组的元素,是一个链表

为什么元素是链表?理论上,如果我们的数组可以做成无限大,那么每来一个key,我们都可以把它放到一个新的位置。但是这样很明显不可行,数组越大,占用的内存就越大

所以我们需要限制数组的大小,假设是16,那么计算出key的hash值后,对16取模,得出一个0~15的数,然后放到数组对应的位置上去。

好,现在key1放到index为2的位置,突然又来了一个key9,刚好他也要放到index为2的位置,那咋办,总不能把人家key1给踢掉吧?所以key1的信息必须存储在一个链表结构里面,这样key9来了之后,只需要把key1所在的链表节点的next,指向key9的链表节点即可。

这样就没问题了吗?想象一下,如果链表越来越长,会有什么问题?

很明显,链表越长,Hash表的查询、插入、删除等操作的性能都会下降,极端情况下,如果全部元素都放到了一个链表里头,复杂度就会降为O(n),也就和顺序查找算法无异了。(正因如此,Java8里头的HashMap在元素增长到一定程度时会从链表转成一颗红黑树,来减缓查找性能的下降)

怎么解决?rehash

关于rehash,这里就不细讲了,大家可以先了解一下Java HashMap的resize函数,然后再通过这篇文章:A little internal on redis key value storage implementation 去了解Redis的rehash算法,你会惊讶的发现Redis里头居然是两个HashTable。

好,上面带大家从一个及其微观的角度窥视了Redis,下面几个小节,再带大家用宏观的视角去观察Redis。

2、C/S架构

作为Redis用户,我们要怎样把数据放到上面提到的Hash表里呢?

我们可以通过Redis的命令行,当然也可以通过各种语言的Redis API,在代码里面对Hash表进行操作,这些都是Redis客户端(Client),而Hash表所在的是Redis服务端(Server),也就是说Redis其实是一个C/S架构。

显然,Client和Server可以是在一台机器上的,也可以不在:

如果你想玩一下Redis,又不想自己搭建环境,可以试一下这一个非常好玩的网页:Try Redis,你可以按照上面的提示,熟悉Redis的基础命令,感受一下Redis的C/S模式。

值得一提的是,Redis的Server是单线程服务器,基于Event-Loop模式来处理Client的请求,这一点和NodeJS很相似。使用单线程的好处包括:

  • 不必考虑线程安全问题。很多操作都不必加锁,既简化了开发,又提高了性能;
  • 减少线程切换损耗的时间。线程一多,CPU在线程之间切来切去是非常耗时的,单线程服务器则没有了这个烦恼;

当然,单线程服务器最大的问题自然是无法充分利用多处理器,不过没关系,别忘了现在的机器很便宜。请继续往下看。

3、集群

好,现在我们已经知道了Redis是一个C/S架构的框架,那就让我们开始用Redis来缓存信息,缓解数据库的压力吧!

我们搭起了这样一个框架,一台客户端,一台Redis缓存服务器:

一开始风和日丽,系统运行良好。

后来,我们系统中使用Redis的客户端越来越多,变成了这样:

这带来了两个问题:

  • Redis内存不足:随着使用Redis的客户端越来越多,Redis上的缓存数据也越来越大,而一台机器的内存毕竟是有限的,放不了那么多数据;
  • Redis吞吐量低:客户端变多了,可Redis还是只有一台,而且我们已经知道,Redis是单线程的!这就好比我开了一家饭店,一开始每天只有100位客人,我雇一位服务员就可以,后来生意好了,每天有1000位客人,可我还是只雇一位服务员。一台机器的带宽和处理器都是有限的,Redis自然会忙不过来,吞吐量已经不足以支撑我们越来越庞大的系统。

分析完问题,解决思路也就再清晰不过了——集群。一台Redis不够,那就再加多几台!

客户端的请求会通过负载均衡算法(通常是一致性Hash),分散到各个Redis服务器上。

通过集群,我们实现了两个特性:

  • 扩大缓存容量;
  • 提升吞吐量;

解决了上面提到的两个问题。

4、主从复制

好,现在我们已经把Redis升级到了集群,真可谓效果杠杠的,可运行了一段时间后,运维又过来反馈了两个问题:

  • 数据可用性差:如果其中一台Redis挂了,那么上面全部的缓存数据都会丢失,导致原来可以从缓存中获取的请求,都去访问数据库了,数据库压力陡增。
  • 数据查询缓慢:监测发现,每天有一段时间,Redis 1的访问量非常高,而且大多数请求都是去查一个相同的缓存数据,导致Redis 1非常忙碌,吞吐量不足以支撑这个高的查询负载。

问题分析完,要想解决可用性问题,我们第一个想到的,就是数据库里头经常用到的Master-Slave模式,于是,我们给每一台Redis都加上了一台Slave:

通过Master-Slave模式,我们又实现了两个特性:

  • 数据高可用:Master负责接收客户端的写入请求,将数据写到Master后,同步给Slave,实现数据备份。一旦Master挂了,可以将Slave提拔为Master;
  • 提高查询效率:一旦Master发现自己忙不过来了,可以把一些查询请求,转发给Slave去处理,也就是Master负责读写或者只负责写,Slave负责读;

为了让Master-Slave模式发挥更大的威力,我们当然可以放更多的Slave,就像这样:

可这样又引发了另一个问题,那就是Master进行数据备份的工作量变大了,Slava每增加一个,Master就要多备份一次,于是又有了Master/slave chains的架构:

没错,我们让Slave也有自己的Slave,有点像古代的分封制。

这样最顶层的Master的备份压力就没那么大了,它只需要备份两次,然后让那它底下的那两台Slave再去和他们的Slave备份。

关于Master/slave chains,大家可以参考这篇文章 RedisLab Master/slave chains

5、Redis没那么简单

这篇文章只是带大家逛一逛Redis的庄园,让大家从微观到宏观,对Redis有一个初步的了解。

事实上,Redis内部要处理的问题还有很多:

  • 数据结构。文章一开头提到了,Redis不仅仅是数据存储器,而是数据结构存储器。那是因为Redis支持客户端直接往里面塞各种类型的数据结构,比如String、List、Set、SortedSet、Map等等。你或许会问,这很了不起吗?我自己在Java里写一个HashTable不也可以放各种数据结构?呵呵,要知道你的HashTable只能放Java对象,人家那可是支持多语言的,不管你的客户端是Java还是Python还是别的,都可以往Redis塞数据结构。这一点也是Redis和Memcached相比,非常不同的一点。当然Redis要支持数据结构存储,是以牺牲更多内存为代价的,正所谓有利必有弊。关于Redis里头的数据结构,大家可以参考:Redis Data Types
  • 剔除策略。缓存数据总不能无限增长吧,总得剔除掉一些数据,好让新的缓存数据放进来吧?这就需要LRU算法了,大家可以参考:Redis Lru Cache
  • 负载均衡。用到了集群,就免不了需要用到负载均衡,用什么负载均衡算法?在哪里使用负载均衡?这点大家可以参考:Redis Partitioning
  • Presharding。如果一开始只有三台Redis服务器,后来发现需要加多一台才能满足业务需要,要怎么办?Redis提供了一种策略,叫:Presharding
  • 数据持久化。如果我的机器突然全部断电了,我的缓存数据还能恢复吗?Redis说,相信我,可以的,不然我怎么用作数据库?去看看这个:Redis Persistence
  • 数据同步。这篇文章里提到了主从复制,那么Redis是怎么进行主从复制的呢?根据CAP理论,既然我们已经选择了集群,也就是P,分区容忍性,那么剩下那两个,Consistency和Availability只能选择一个了,那么Redis到底是支持最终一致性还是强一致性呢?可以参考:Redis Replication
  • ……

5、参考文献&学习资源

一份Redis实战文档送给大家学习:

本书既覆盖了命令用法等入门主题,也包含了复制、集群、性能扩展等深入主题,所以无论是Redis新手还是对Redis有一定经验的使用者,应该都能从书中获益。

如果需要获取到这个【PDF】文档的话帮忙转发一下然后再关注我私信回复“架构资料”得到获取方式吧!(内容细节比较多,所以把知识点粗略的截图出来了)

目录

一、Redis入门介绍

二、Redis核心概念

如果需要获取到这个【PDF】文档的话帮忙转发一下然后再关注我私信回复“架构资料”得到获取方式吧!(内容细节比较多,所以把知识点粗略的截图出来了)

怎么查询redis缓存的数据_阿里开发十年写出这份「Redis简明教程」+「Redis实战」请你查收...相关推荐

  1. 前端如何调用后端接口_后端开发:如何写出可靠的接口

    毕业进入现在的公司已近一年,完整参与了部门新项目两期的开发上线过程,作为一名后端开发,觉得最痛苦的是上线前和上线后的改 bug 阶段,面对各种突如其来.莫名其妙的bug,头昏脑涨.手忙脚乱.越改越懵, ...

  2. 如何从数据库中筛选出达成指定里程碑节点的项目_【译】如何写出一份优秀的软件设计文档...

    作为一名软件工程师,我花了很多时间阅读和编写设计文档.在完成了数百篇这些文档之后,我亲眼目睹了优秀设计文档与项目最终成功之间的强烈关联. 本文试图描述什么使设计文档变得更好. 本文分为4个部分: 为什 ...

  3. redis一般缓存什么样数据_门户数据展示_Redis缓存数据

    学习主题:门户数据展示_Redis缓存数据 一.Redis_3主3从集群环境搭建 谈单你对读写分离和主从同步的理解 读写分离:Master负责写数据的操作,salve负责读数据的操作 主从同步:sal ...

  4. 一份完整的问卷模板_如何写出一份优秀的个人简历?

    想要写出一份优秀的个人简历,首要了解HR是怎么筛选简历的. 毕竟简历再牛逼,也需要HR认可才能进入面试关. 作为曾经的部门负责人,我有幸参与过多次招聘,基本了解在筛选简历时的一些方法: 首先,硬件条件 ...

  5. 如何写出一份高质量的数据分析师的简历?

    这是我的第25篇原创 看我文章的基本都是圈里的老朋友,上次发了一篇<价格是价值的外在表现形式-求职宝典>,被一位小朋友批评太短了,而且还偷懒引用别人的内容.好吧好吧,今天来手把手教你怎么写 ...

  6. redis 什么是冷数据_阿里Java三面凉凉:微服务,Redis,JVM一个都搞不懂

    前言: 金九银十刚刚过去了,不知道很多小伙伴都拿到自己心仪的offer没有,我这边也收到了一个粉丝投来的消息,说看到阿里的面试真题之后人都是懵的,发现自己一窍不通,下面给大家分享我这个粉丝的经历,以及 ...

  7. oracle中join另一个表后会查询不出一些数据_阿里规定超过3张表,禁止JOIN,为何?

    一. 问题提出 <阿里巴巴JAVA开发手册>里面写超过三张表禁止join,这是为什么? 二.问题分析 对这个结论,你是否有怀疑呢?也不知道是哪位先哲说的不要人云亦云,今天我设计sql,来验 ...

  8. java用redis缓存的步骤_详解在Java程序中运用Redis缓存对象的方法|chu

    这段时间一直有人问如何在Redis中缓存Java中的List 集合数据,其实很简单,常用的方式有两种: 1. 利用序列化,把对象序列化成二进制格式,Redis 提供了 相关API方法存储二进制,取数据 ...

  9. 【C 语言】文件操作 ( 学生管理系统 | 命令行接收数据填充结构体 | 结构体写出到文件中 | 查询文件中的结构体数据 )

    文章目录 一.学生管理系统 二.代码示例 一.学生管理系统 前两篇博客 [C 语言]文件操作 ( 将结构体写出到文件中并读取结构体数据 | 将结构体数组写出到文件中并读取结构体数组数据 ) [C 语言 ...

最新文章

  1. svn mysql认证_svnapachemysql 认证搭建
  2. 浅谈搜索引擎百度分词技术
  3. 企业USB权限控制心得
  4. The Illustrated Transformer 翻译
  5. UVa OJ 120
  6. C#异步批量下载文件
  7. redis zset转set 反序列化失败_Redis只往zset有序集合添加不存在的数据:关键字索引查询构建+源码分析...
  8. session.setAttribute和request.setAttribute的区别
  9. 领域应用 | 知识图谱的技术与应用
  10. 自定义分页 html,MVC 自定义HtmlHelper帮助类型之分页
  11. C++ typedef小结(转载)
  12. Python基础(while循环/赋值运算符)
  13. 查看你所使用计算机的网卡信息,查看网卡信息命令
  14. 路由器互通过程(简述)
  15. 华数java 知乎_纠结编程语言的选择?他们为什么都选择了Java?
  16. PHP字符串函数hex2bin( 转换十六进制字符串为二进制字符串)
  17. windows10,忘记密码,不用u盘就可以修改密码
  18. 计算机网络技术专业毕业论文参考选题,计算机网络技术专业毕业论文参考选题...
  19. 【洛谷3043】跳楼机
  20. Egret EUI Tab + ViewStack

热门文章

  1. css3 html5宽高不变,html – CSS圆圈,不使用固定的宽度和高度
  2. idea创建一个html5,idea创建一个SpringBoot项目
  3. linux python qt 安装目录,Linux 下QT调用Python库文件 以及Linux 安装Python3.8开发环境 问题...
  4. java中volatile_Java中Volatile关键字详解
  5. git如何忽略已经提交的文件 (.gitignore文件无效)
  6. android开发 获取相册名称_Android开发之获取相册照片和获取拍照照片
  7. python查看工作目录_闲话python-36:文件系统操作
  8. linux修改rc.local权限,Linux 7 的 rc.local 文件需要 添加 +x 权限才会自动执行
  9. 前端答题小游戏_这是什么神奇操作!两个前端一周上线一款联机小游戏
  10. 有100名考生参加C语言测验,全国计算机一级考试模拟试题及答案(1-100)