Redis核心原理与设计思想

第一篇日志!!!

从学校出来到工作也将近一年的时间,从一个连spring都不会用的小白到熟练地写业务代码到涉及一些构架层次的底层框架。一路走来,我还是比较满意这一年的学习与进步。期间也看了不少书籍,看得快忘得也快,打算通过每天闲扯的方式梳理并加深印象。鉴于工作时间不长,还欠缺经验,若内容有纰漏及错误之处,还望指出。

前言

这篇文章希望通过一系列问题简洁的介绍Redis的核心原理,Redis的分块细节将在后续文章展开。

计划Redis分多篇学习笔记:

1)Redis核心原理

2)Redis数据结构

3)Redis多机及高可用实现

4)Redis优化与常见问题排查

概述

Redis is an open source (BSD licensed), in-memory data structure store, used as a database, cache and message broker. It supports data structures such as strings, hashes, lists, sets, sorted sets with range queries, bitmaps, hyperloglogs, geospatial indexes with radius queries and streams. Redis has built-in replication, Lua scripting, LRU eviction, transactions and different levels of on-disk persistence, and provides high availability via Redis Sentinel and automatic partitioning with Redis Cluster.

这是Redis官网对Redis的简短介绍,总的来说就是Redis是一款KV的纯内存数据库,可以支持多种数据结构,用Sentinal实现了高可用,用集群实现大规模部署。

大部分公司将redis用作缓存结合mysql等数据库使用,目的是提供更快的响应及更灵活的数据处理。

Redis基于C代码实现,核心思想是简单、高效

为什么Redis这么快?

《Redis核心原理与设计思想》 Redis Benchmark

根据官网的性能测试,当data size处于合适区间时,Redis的吞吐量可达10W/s

1)Redis基于纯内存操作,不存在Page Fault及Disk Seek,大大提升效率

2)Redis是单线程工作(详见为什么是单线程),单线程工作不存在操作系统的context switch及lock操作。单线程能最大化的利用缓存,提升cache hit。若希望使用多核cpu的优势,可以在一台机器上部署多个Redis实例或使用Shard。

3)Redis数据结构设计复杂度基本为O(1)及O(N),CPU可以快速计算

4)Redis大量使用哈希

5)网络应答基于Reactor模型,采用Multiplexing,可以同时处理大量客户端请求

Redis的Key是如何过期的?

从宏观上讲,Redis是由两个字典表组成的

1)Data Dictionary

2)Expire Dictionary

Data表存储的是真正的数据,Expire表存储的是每个Key的过期时间。当client键入一个 expire key 10时,在Expire表更新Value值为current_unixtime + 10 * 1000。每当使用TTL时,redis会使用 unixtime_from_expire_table – current_unixtime并返回给客户端。

Redis中集合类型,不能单独为某一个key设置过期时间,因为内部的KV对并不会在Expire内设置过期时间。

Redis对过期的Key是怎么处理的?

Redis对过期的Key采用惰性删除

1)当client请求查询key时,先在Expire表查询是否过期。如果过期,则在Data表删除数据并返回nil给客户端

2)如果长时间没有客户端请求,Redis内部采用定时任务,随机检查Expire表的过期Key并删除Data

因此我们可以知道,Redis的内存占用 > 我们真正使用的有效数据。另外不要将大量的key设置同一过期时间,当key过期时有可能造成短暂延时

Redis的底层数据结构是怎样的?

上面说明了Redis是由两张字典表Data和Expire组成,字典都是由KV组成的。在Redis里,每个K和V都是一个redisObject,redisObject包含了type、encoding、ptr等字段

1)type:对象的类型,如REDIS_STRING、REDIS_HASH、REDIS_ZSET等,也就是我们在使用redis时可以见到的类型。但Redis为了实现这些类型其实是使用了底层类型并封装得到的

2)encoding:encoding是每个redisObject的真正实现类型,也就是底层数据类型。如REDIS_ENCODING_INT、REDIS_ENCODING_LINKEDLIST、REDIS_ENCODING_HT等。每一个高层redis 类型可以由1-3种底层数据类型实现,目的是存储不同类型的数据都能有较少的内存占用及较快的存取速度

3)*ptr:是底层数据结构的指针

针对每一种高层type,redis都有一定的命令集合,若判断命令与数据类型不匹配则会报错

Redis的每种底层数据模型的复杂度都很低一般为O(1)及O(N),且采用了空间换时间的方式,集合类型的object都有一个字段size,读取集合长度时的复杂度为O(1),不需要遍历集合。一切向高性能靠近

Redis的慢日志

基于redis是单线程实现数据的存取,所以如果有一条请求耗时过长那么整个redis的性能都会受到根本性的影响。

redis根据链表存储慢查询日志,包括查询时间、查询时长、查询命令参数等。

一个常引起慢日志的命令是 keys *,keys *会遍历data表,复杂度O(N),当key数量较多时,则可能引起慢查询,因此在生产环境应尽少使用keys pattern命令。这时使用scan是一个备用选择。

Redis的持久化

RDB是Redis内部数据集的snapshot,也是Redis的默认持久化方式,通过SAVE和BGSAVE执行。SAVE会导致redis停止服务,而BGSAVE可以使得redis继续工作,转而fork新进程执行持久化,由于copy-on-write的技术,使得操作系统不需要大量复制内存值且新变化的值能单独存储,从而加速了持久化的速度。当Redis 达到N秒或M次修改时(依据dirty及lastsave值),redis自动执行BGSAVE,保存在dump.rdb。

AOF是记录数据库的所有操作至AOF缓冲区,再同步至磁盘,当需要恢复时重复这些操作就能恢复至原先状态。由缓冲区同步至磁盘的频率可选

RDB v.s. AOF

1)RDB数据更小,恢复速度更快,但fork指令在数据及请求量大时可能会对机器造成较大负载

2)AOF记录了数据库的操作记录,更容易追溯问题

3)无论是RDB还是AOF都无法保证数据的绝对完整性,因为都有一定频率执行,如果中间挂了那么数据无法恢复

Redis内存优化

1)当无必要时,集合中的数据不要存储过多。在某个阈值以下时,Redis用ziplist实现集合相关类型,ziplist使用更紧凑的编码方式,占用更少的内存。虽然ziplist的复杂度是O(N),但由于ziplist包含的元素很少,所以并不会影响CPU

2)当不需要大量的存储空间时,使用32位编译,指针占用空间更小

3)尽可能使用hash存储对象,因为当对象不多时可以使用ziplist,压缩存储空间,会比多个key存储多个对象要节省内存。此外ziplist会大大提升cache的命中率,使得查询速度更快。

下一篇

Redis数据结构及如何用好这些数据结构

    原文作者:Lee_d441
    原文地址: https://www.jianshu.com/p/24eb295b63df
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞