Listening to the Words

Redis技术的前世和今生

Redis技术的前世和今生


History 1 早期的文件存储

计算机早期数据文件的存储放在硬盘里:就像一个人的大脑中的想法,要记录在笔记本上一样。

这个中存储系统受到两个因素的限制:

  • 带宽(吞吐)
  • 寻址时间

有计算机基础的人都知道,硬盘的带宽吞吐速度最高也只是千兆级别;寻址时间一般都是ms级别,这对于人的感觉以及视觉来说足够快了,但是对于计算机的内存来说这个速度简直比蜗牛还慢。

因为内存的传输时间基本单位 是 ns(纳秒),足足是硬盘的10万倍,计算机显然不能等这么久。

其实人的大脑也一样,一些想法闪念间就过去了,还没来得及记录下来,然而有的时候一边想一边记录,又发现书写的速度根本跟不上大脑思考的速度。

还有一件趣事:据说有些人之所以是口吃,就是因为嘴巴发音的速度跟不上大脑速度从而导致的失序。

History 2 数据库(database)存储系统

Q1: 数据量大了,查询数据为什么慢了?

  • 全量扫描
  • 磁盘I/O

为了解决全量扫描的问题,database被开发出来,它的典型特性就是分块(datapage),每块由固定大小的数据组成。现实生活中也是如此,一个一个仓库都由不同大小不同的货物架子组成。

分块技术的思想来源于分治法(divide and conquer),几乎所有的分布式系统都来自于此。

但是仅仅如此仍然不能解决全量扫描的问题,让图书管理员取出一本《Redis技术解析》,她必须把整个图书馆翻过来才能找到,显然这是不可能的。

于是必须有索引,就像图书馆会给每个区域贴上标签分类,然而这样还是不够高效。图书管理员每次还要不断查找标签的位置,这又多了一步。

在数据库系统中,索引并不是完全存储在文件里,而是将一部分索引枝干存储在内存里,图书馆员已经把每个便签所在位置深深地记忆在脑海里,这是最好的利用方式。

mysql数据库使用B+树索引,这种索引避免了其他数据结构(二叉树红黑树)的缺点(层级结构多,I/O次数多),同时还能大量存储索引

但是数据库毕竟是存储硬盘的,总会有硬盘读取的极限,这时为了更好的存储数据该怎么办?

Q2:数据库能不能再快点?

世界上真实存在极端的数据库叫做Hana,它的数据都被放置在内存里,想想就觉得可敬,可怕。这要花费多少钱才能使用的起呢?

History 3 Redis 应运而生

为了解决数据读取速度的问题, Redis 应运而生,它的目标很简单:快,更快!同时为了保持数据的简单redis是k-v形式的nosql,很显然它不想做成Hana

redis的架构模式也很独特:

  • 单线程(worker)
  • epoll 非阻塞多路复用

Q3: Redis真的是单线程的吗?

keywords 单线程 I/O多线程

其实是也不是,在6.0以前Redis是单线程的,在这之后它引出了新的特性:I/O多线程,worker单线程,那么这样做的好处是什么呢?

worker单线程的好处特别明显:它实现了程序的串行化,那使用场景呢?可以说超级广泛,任何高并发技术下都有它的使用场景,我们知道多线程最让人头疼的就是锁的问题,而单线程是串行的、阻塞的,在秒杀、派对、抢红包、分布式事务锁等等诸多场景都需要用到单线程实现流量削峰

单线程还带来了原子化的操作,比如redis中基本的本地方法都是原子化的操作。这对于并发操作数据状态作用巨大。

看来,单线程未必是低效的。它反而在特定场景下是简单的高效的。

接下来,为了更好的利用redis的资源处理能力,必须想想其实那些地方是可以被高效利用的,就是出worker线程中计算的部分以外,都可以实现多线程。

举例: 餐厅有两个服务员,一个在上菜,一个在收拾桌子,而厨师在厨房做菜。这里面,厨师就是worker线程,服务员就是I/O多线程。

History 4 Redis Overview

  • 基于内存
  • K-V存储
  • worker单线程
  • I/O多线程(epoll)
  • 5个value类型,每个均有本地计算方法

Presence 5 五种数据类型和使用场景

参见以前的文章: Redis五种数据类型和使用场景

Presence 6 Redis的持久化

Redis持久化内存数据共有两种方式:

  • 快照 RDB
  • 日志 AOF

仔细想想: 快照的优点是什么? 恢复快,一瞬间。缺点呢? 丢的多。日志呢? 恢复慢,记录全,影响速度。

其实redis本来是没有持久化的,我只是一个缓存数据,丢了就去数据库拿呗!但是后来还是引入了持久化功能,只是默认开启的RDB快照模式,谁都不能阻止我快人一步!

在4.0之后,为了弥补这两种模式的不足,Redis提供了混合模式,一面使用快照,一面使用AOF日志模式。快照每天定时运行,到点获取当时内存数据,之后的数据才用AOF做增量记忆。在宕机恢复时,就先用快照数据,然后在追加日志数据。

Presence 7 Redis分布式集群

redis分布式集群是为了解决两大难题:

  • 单点故障
  • 压力过大

单点故障: 才用一变多主从双备(镜像)同步模式
压力过大:分片集群和代理集群 (不需同步,分区存储)

这看起来特别像是一个坐标系: X 轴和Y轴

事实上一旦涉及到了主从双备(复制模式)就是分布式系统了。这时就出现了分布式系统最根本的问题 【数据一致性】

  • 两台机器实时同步就会破坏系统的可用性,一旦一台出现问题整个系统就阻塞住,不能对外服务

  • 两台机器不事实同步就会出现脑裂现象,一旦一台机器死了,不知道,又活过来时数据出现了不一致。

  • 最终一致性:只要数据最终是一致的就行了。

最终一致性引出了分布式系统组重要的内容:【分布式协调】

  • zookeeper
  • 哨兵

它们都是对一篇叫做Paxos算法论文的实现基本的概念就是过半机制(即集群服务器至少有一半活着,否则关闭系统),每种技术都有它自己的侧重点。有的侧重于强一致性,有的侧重最终一致性。

例如:3台服务器至少保证两台是活着的,并且他们之间要两两通讯,一旦其中一台死掉,它应当关闭自己的服务。

很多分布式服务都借助于此:比如ES,Hadope,Kafka 等

Q4: Redis拆分和分片?

提及坐标系,就要讨论redis集群的划分原则:X Y Z

  • X轴:是由一变多,解决单机故障问题
  • Y轴:业务分治,分担数据库压力到不同集群
  • Z轴:业务分片对数据进行更细粒度细分

集群分片:集群分片可以客户端自己做,也可以使用代理(twitter),但是这样对客户端使用成本过高,redis集群自带了分片算法。

将所有key映射到16384个slot(槽)中,每个slot又会指定几个不同的集群机器,一旦有客户端连接请求,就会使用hash算法与16384取模,最后计算出slot位置。

如果结果不是当前服务所在的slot位置,则会通知客户端跳转到指定位置获取数据。

点赞