redis总结

redis为什么这么快

  1. 纯内存操作,避免大量访问数据库,减少直接读取磁盘数据,redis将数据储存在内存里面,读写数据的时候都不会受到硬盘 I/O 速度的限制,所以速度快;

  2. 单线程操作,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗;

  3. 基于非阻塞IO多路复用机制

    多路复用的原理:

    多路复用

    用户线程首先将需要进行IO操作的socket添加到select中,然后阻塞等待select系统调用返回。当数据到达时,socket被激活,select函数返回。用户线程正式发起read请求,读取数据并继续执行。这样用户可以注册多个socket,然后不断地调用select读取被激活的socketredis服务端将这些socke置于队列中,然后,文件事件分派器,依次去队列中取,转发到不同的事件处理器中,提高读取效率。

    采用多路 I/O复用技术可以让单个线程高效的处理多个连接请求(尽量减少网络 IO 的时间消耗),多路I/O复用模型是利用 select、poll、epoll 可以同时监察多个流的I/O 事件的能力,在空闲的时候,会把当前线程阻塞掉,当有一个或多个流有 I/O 事件时,就从阻塞态中唤醒,于是程序就会轮询一遍所有的流(epoll 是只轮询那些真正发出了事件的流),并且只依次顺序的处理就绪的流,这种做法就避免了大量的无用操作,从而提高效率。

  4. 灵活多样的数据结构。

    redis内部使用一个redisObject对象来表示所有的key和value。redisObject主要的信息包括 数据类型、编码方式、数据指针、虚拟内存等。它包含String,Hash,List,Set,Sorted Set五种数据类型,针对不同的场景使用对应的数据类型,减少内存使用的同时,节省网络流量传输。

  5. 持久化

    由于redis的数据都存放在内存中,如果没有配置持久化,redis重启后数据就全丢失了,于是需要开启redis的持久化功能,将数据保存到磁盘上,当redis重启后,可以从磁盘中恢复数据。redis提供两种方式进行持久化,一种是RDB持久化(原理是将redis在内存中的数据库记录定时 dump到磁盘上的RDB持久化),另外一种是AOF(append only file)持久化(原理是将redis的操作日志以追加的方式写入文件)。持久化似乎和redis的速度快并没有直接关系,但是这保证的redis数据的安全性和可靠性,也起到数据备份的作用。

redis持久化机制

RDB redis database

将某一时刻的内存快照,以二进制方式写入到磁盘中。

手动触发

  • save命令:使redis一直处于阻塞状态,直到RDB持久化完成,才会响应客户端发来的命令。生产环境慎用!
  • bgsave命令:fork一个子进程执行持久化,主进程在fork子进程的时候会有短暂的阻塞,子进程创建成功后,主进程就可以响应客户端的请求了。

自动触发

  • save m n:在m秒内有n个键发生改变,则触发持久化,通过bgsave执行。如果在配置文件设置多个,只要满足一个就触发。redis.conf中默认有三条配置。
  • flushall:用于清空redis所有的数据库,flushdb只会清空当前所处的数据库,会清空RDB文件,同时也会生成dump.rdb文件,内容为空。
  • 主从同步:全量同步的时候会自动触发bgsave命令,生成rdb文件发送个从节点。

优点

  • 整个redis只会有一份dump.rdb文件,方便持久化。
  • 容灾性好,方便备份。
  • 性能最大化,fork子进程来完成写操作,让主进程继续响应客户端请求,所以是IO最大化。单独使用子进程来进行持久化,主进程不会进行有任何的IO操作,保证了性能的最大化。
  • 相对于数据集比较大的时候,比AOF的启动效率更高。

缺点

  1. 数据安全性低,RDB是间隔一段时间进行持久化,如果在持久化期间redis发生了故障,那么保存在内存当中的数据就会丢失。
  2. 由于RDB是通过fork子进程来协助数据完成持久化工作的,如果数据集比较大的时候,会造成主进程阻塞很长时间不能工作,影响了效率。

AOF append only file

以日志的形式记录服务器所处理的每一个写、删除操作,查询记录不会记录,以文本形式记录,可以打开文件看到详细的操作记录,调操作系统命令进程刷盘。
1. 所有的写命令会追加到AOF缓冲区。
2. AOF缓冲区根据对应的策略向硬盘进行同步操作。
3. 随着AOF文件越来越大,需要定期对AOF文件进行重写,达到压缩的目的。
4. 当redis重启时,可以加载AOF文件进行数据恢复。

同步策略

每秒同步

异步完成,效率非常高,但是如果系统出现宕机现象,那么这一秒内修改的数据就会丢失。

每修改同步

同步持久化,每次发生数据变化都会立即被记录到磁盘中,最多丢失一条数据。

不同步

由操作系统控制,可能丢失较多数据。

优点

  • 数据安全
  • 通过append模式写文件,及时中途服务器宕机也不会破会已经存在的内容,可以通过redis-check-aof工具解决数据一致性问题。
  • AOF机制的rewrite模式。定期对AOF文件重写,已达到压缩的目的。

缺点

  • AOF文件比RDB文件大,切恢复速度比较慢。
  • 数据集大的时候,比RDB启动效率低。
  • 运行效率没有RDB高。

总结

  • AOF文件比RDB更新频率高,优先使用AOF还愿数据。
  • AOFRDB更安全,但是也更大。
  • RDB性能比AOF好。
  • 如果两个都配置了,会优先加载AOF

redis底层数据结构

string

字符串对象的底层实现有三种可能:intrawembstr.

int

如果一个字符串对象,保存的值是一个整数值,并且这个整数值在 long 的范围内,那么 redis 用整数值来保存这个信息,并且将字符串编码设置为 int.

127.0.0.1:6379> set test 1
OK
127.0.0.1:6379> object encoding test
"int"
127.0.0.1:6379>

raw

如果字符串对象保存的是一个字符串, 并且长度大于 32 个字节,它就会使用SDS(简单动态字符串)数据结构来保存这个字符串值,并且将字符串对象的编码设置为raw.

127.0.0.1:6379> set test faldfjasdflsjdflsjdflsajflajflsajflsadjflsdjdfasdfasdgsdgdsgasdgasdgasdfasdfsadfsad
OK
127.0.0.1:6379> object encoding test
"raw"

embstr

如果字符串对象保存的是一个字符串, 但是长度小于 32 个字节,它就会使用embstr来保存了,embstr编码不是一个数据结构,而是对 SDS 的一个小优化,当使用 SDS 的时候,程序需要调用两次内存分配,来给 字符串对象 和 SDS各自分配一块空间,而embstr只需要一次内存分配,因为他需要的空间很少,所以采用 连续的空间保存,即将 SDS 的值和 字符串对象的值放在一块连续的内存空间上。这样能在短字符串的时候提高一些效率。

127.0.0.1:6379> set test hello
OK
127.0.0.1:6379> object encoding test
"embstr"

浮点数如何保存

redis 的字符串数据类型是支持保存浮点数,并且支持对浮点数进行加减操作,但是 redis 在底层是把浮点数转换成字符串值,之后走上面三种编码的规则的。对浮点数进行操作时,也是从字符串转换成浮点数进行计算,然后再转换成字符串进行保存的。

编码转换条件

这块的知识其实是很符合我们的认知的,比如 int编码只可以保存整数,那么当我们对一个 int 编码的字符串对象,修改它的值,它自然就会使用 raw 编码了。但是有一个特性,Redis 没有为embstr编码提供任何的修改操作,这也就是为什么它只是个编码而不是一个数据结构的原因。所以在 Redis 中,embstr编码的值其实是 只读的,只要发生修改,立刻将编码转换成 raw.

总结:

编码 使用条件
int 可以用 long 保存的整数
embstr 字符串长度小于 32 字节(或者浮点数转换后满足)
raw 长度大于 32 的字符串

list

涉及到的数据结构,压缩列表, 双向链表, 快速列表。

Redis 3.2版本之前,列表对象底层由 压缩列表和双向链表配合实现,当元素数量较少的时候,使用压缩列表,当元素数量增多,就开始使用普通的双向链表保存数据。

但是这种实现方式不够好,双向链表中的每个节点,都需要保存前后指针,这个内存的使用量 对于Redis这个内存数据库来说极其不友好。

因此在 3.2 之后的版本,作者新实现了一个数据结构,叫做 quicklist. 所有列表的底层实现都是这个数据结构了。它的底层实现基本上就是将 双向链表和压缩列表进行了结合,用双向的指针将压缩列表进行连接,这样不仅避免了压缩列表存储大量元素的性能压力,同时避免了双向链表连接指针占用空间过多的问题。

127.0.0.1:6379> lpush test 123 323 43
(integer) 3
127.0.0.1:6379> object encoding test
"quicklist"
127.0.0.1:6379>

总结

编码 使用条件
quicklist 所有数据

set

inset

当集合中的所有元素都是整数,且元素的数量不大于 512 个的时候,使用 intset 编码。(这个数量是可以在配置文件设置)

127.0.0.1:6379> sadd test 3 1 23  43
(integer) 4
127.0.0.1:6379> object encoding test
"intset"
127.0.0.1:6379>

hashtable

当元素不符合全部为整数值且元素个数小于 512时,集合对象使用的编码方式为hashtable.

字典的每一个键都是一个字符串对象,其中保存了集合里的一个元素,字典的值全部被设置为 NULL.

127.0.0.1:6379> sadd test ab cd
(integer) 2
127.0.0.1:6379> object encoding test
"hashtable"

总结

编码 使用条件
intset 所有元素都是整数且元素个数小于 512(这个数量是可以在配置文件设置)
hashtable 其他数据

zset

ziplist

当元素个数少于128个,并且所有元素成员的长度小于64字节,使用该编码。(该参数可以在配置文件中配置)
每个集合的元素 (key-value), 使用两个紧挨着的压缩列表的节点来表示,第一个节点保存集合元素的成员 (member), 第二个节点保存集合元素的分支 (score).

在压缩列表的内部,集合元素按照分值从小到大进行排序。

127.0.0.1:6379> zadd test 10 a 29 b 43 c
(integer) 3
127.0.0.1:6379> object encoding test
"ziplist"

skiplist

当使用 skiplist 编码的时候,内部同时使用了跳跃表字典来保存数据。原因如下:
– 当我们只使用字典来实现,我们可以以 O(1)的时间复杂度获取成员的分值,但是由于字典是无序的,当我们需要进行范围性操作的时候,需要对字典中的所有元素进行排序,这个时间复杂度至少需要 O(nlogn).
– 我们只使用跳跃表来实现,我们可以在 O(logn) 的时间进行范围排序操作,但是如果要获取到某个元素的分值,时间复杂度也是 O(logn).

因此,将字典和跳跃表结合进行使用,可以在 O(1) 的时间复杂度下完成查询分值操作,而对一些范围操作,使用跳跃表可以达到 O(logn) 的是缠绵复杂度。

127.0.0.1:6379> zadd test 10 a 20 b 30 fjaljlfdjflajdflsjflsajfdlsjflsajdlfjsaldfjsdljflsjflsdjflsdjflsdjflsdjflsdjflsjdlfjaldfjajdflajflajfdlsadjflsdjlfjasdfjasdjfljflsdjflsajflsadjflsadjflasjdlf
(integer) 1
127.0.0.1:6379> object encoding test
"skiplist"

根据上面可以看见,当元素的长度大于64字节的时候,就变成了skiplist

总结

编码 使用条件
ziplist 元素数量少于 128 且 所有元素成员的长度小于 64 字节(可以在配置文件中配置)
skiplist 不满足上述条件的其他情况

hash

ziplist

ziplist 编码下的哈希对象,使用了压缩列表作为底层实现数据结构,用两个连续的压缩列表节点来表示哈希对象中的一个键值对。实现方式类似于上面的有序集合的场景。

127.0.0.1:6379> hset test a 2 b 3
(integer) 2
127.0.0.1:6379> object encoding test
"ziplist"

hashtable

哈希结构本身在结构上和字典 (hashtable) 就颇为相似,因此哈希对象中的每一个键值对都是字典中的一个键值对。
– 字典的每一个键都是一个字符串对象,对象中保存了键值对的键。
– 字典的每一个值都是一个字符串对象,对象中保存了键值对的值。

127.0.0.1:6379> hset test abc aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
(integer) 1
127.0.0.1:6379> object encoding test
"hashtable"

当向hash中放入一个很长的元素的时候,就会变成hashtable

总结

编码 使用条件
ziplist 键值对的键和值的长度都小于 64 字节,且 键值对个数小于 512(可以在配置文件中配置)
hastable 不满足上述条件的其他条件
暂无评论

发送评论 编辑评论


|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇