写点什么

Redis 学习 01

用户头像
Hex
关注
发布于: 2021 年 04 月 22 日

主要记录在学习 Redis 过程中的知识点,用于复习查看。


本篇学习笔记包括 Redis 数据结构概览(rehash)、单线程、IO 多路复用机制等知识点。


一、开篇

redis 是键值数据库。

既然是数据库,与关系型数据库有什么区别。

这二者的区别具体化可以到两个层面,应用场景和架构设计。

从应用场景来看,关系型数据库可以通过表以及表之间的管理处理相对较为复杂的业务场景,数据接口更丰富,如联表查询、聚合、统计、排序等功能;而 redis 应用场景更具象,最常用的场景是缓存。

从架构设计来看,redis 的数据存储在内存中;关系型数据库的数据存储在磁盘文件;底层数据结构设计等等,不展开。


对于一个数据库系统,判断其适不适合某种业务场景,可以从其数据接口和数据结构两个维度进行综合判断。数据接口(比如增删查改,操作函数等)可以从使用层面进行分析,包括已用性、可扩展性等;数据结构是存储数据的底层数据结构,不同的数据结构适合不同的场景,对应的是数据结构的效率(时间复杂度、空间复杂度),以及索引设计等。


redis 支持丰富的数据结构,string\list\hash\set\sorted set;提供了操作不同数据类型的接口,基本满足增删改查,对值的存储支持丰富的集合数据结构,使得 Redis 的数据结果能支持丰富的业务场景。


存储,redis 的数据是在内存中的,存在内存中的特点是快,缺点是一旦机器出故障会丢失数据。这里会涉及持久化。


IO 模型(阻塞 IO、非阻塞 IO、多路复用 IO、异步 IO),redis 使用多路复用 IO 模型,是单线程的 redis 可以承受高并发的主要原因之一。


二、数据结构

Redis 快的主要原因有,一是数据操作在内存中完成;二是高效的底层数据结构设计。

Redis 支持的数据类型有 String,List,Hash,Set,Sorted Set,它们底层的数据结构如下图:

图片摘抄自极客时间Redis专栏


2.1 全局哈希表

Redis 用一个全局哈希表来存储所有的键值对信息。

哈希表,就是一个数组,数组的元素为哈希桶,哈希桶保存键值对数据信息(键值对数据的地址信息)。

通过键计算出的哈希值能通过 O(1)的时间来找到哈希桶,然后通过哈希桶里的地址信息找到对应的键值数据。

使用哈希表不可避免的会遇到哈希冲突以及重新哈希(rehash)过程。

Redis 解决哈希冲突使用的是链式哈希,链式哈希解决哈希冲突存在问题是当哈希链表过长(数据量越大,冲突的概率越大)会影响效率。此时,Redis 会进行 rehash,为了让 rehash 过程更高效,采用以下流程进行,

Redis rehash 过程:

1.申请一个新的且大小为当前哈希表大小两倍(应该可以配置?)的哈希表;

2.把老的哈希表的数据映射迁移至新的哈希表中;

3.释放老的哈希表的内存空间;

在上述 rehash 的流程中的第二步中,涉及到大量的数据迁移,为了更高效且不影响 Redis 处理性能,Redis 采用渐进式哈希。

渐进式哈希是指在客户端访问时会将访问的哈希桶里的键值信息 rehash 至新的哈希表。

2.2 底层数据结构

压缩列表,跳表

压缩列表

有了数组、链表,为啥还要压缩列表(ZipList)?

主要是为了节省存储空间,因为 Redis 是使用内存进行存储,而内存资源是十分珍贵的,便有了压缩列表这种节省空间的数据结构。

如下图所示,数组每个元素分配的内存大小都是一样的;链表需要保存指针信息;压缩列表根据元素的大小为其分配空间,表头包含压缩列表包含的字节数、尾部元素的偏移量、长度,以及尾部元素后有个结束标志。



压缩列表的元素包含,前一个元素的大小、编码、值。缺点是不适合存储大型字符串以及数据元素多的情况。


三、单线程

Redis 使用单线程指的是 Redis 通过一个线程来处理网络 IO 和数据读写,这也是 Redis 提供键值对服务的主流程。而数据持久化、异步删除、集群数据同步等都是额外的线程来处理的。

Redis 的主流程为什么要采用单线程?

多线程的问题,对共享资源数据的并发控制的负责及成本,多线程切换的成本等。

通常来说多线程比单线程高效,那么为什么单线程的 Redis 的性能高(每秒数十万级别的处理能力)?

  1. 内存处理,高效的底层数据结构设计;

  2. IO 多路复用机制

四、IO 多路复用

Linux 的 select/poll 机制,在内核中,同时存在多个监听套接字和已连接套接字。为了在请求到达时能通知到 Redis 线程,select/epoll 提供了基于事件的回调机制,即针对不同事件(accept,read,write)的发生,调用相应的处理函数。

这些事件会进入事件队列,Redis 线程会从队列中取出事件进行处理。正因为此,Redis 可以同时和多个客户端连接并处理请求,从而提升并发性。

用户头像

Hex

关注

还未添加个人签名 2018.05.24 加入

还未添加个人简介

评论

发布
暂无评论
Redis学习01