美好的一天应该从一杯茶和古典开始今天工作不太忙,读会书。
工作两年,用了几次redis,但是并没有做一次完整的学习。
简介:
作为一个Nosql数据库,它的使用场景经常被拿来和memcached作比较,Redis是一个单线程的数据库,他有6种常见的数据结构:
Strings,Lists,Hashes,Sets,SortedSets,GEO
并且都具备原子操作的能力
为什么redis是单线程的都那么快?
因为Redis主要消耗内存资源,数据落地消耗硬盘。而且采用了多路复用I/O技术 【多路I/O复用模型是利用 select、poll、epoll 可以同时监察多个流的 I/O 事件的能力,在空闲的时候,会把当前线程阻塞掉,当有一个或多个流有 I/O 事件时,就从阻塞态中唤醒,于是程序就会轮询一遍所有的流(epoll 是只轮询那些真正发出了事件的流),并且只依次顺序的处理就绪的流,这种做法就避免了大量的无用操作。】
“多路”指的是多个网络连接,“复用”指的是复用同一个线程。采用多路 I/O 复用技术可以让单个线程高效的处理多个连接请求(尽量减少网络 IO 的时间消耗)
那么问题又来了,为什么Redis要设计成单线程?
官方FAQ表示,因为Redis是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络带宽。既然单线程容易实现,而且CPU不会成为瓶颈,那就顺理成章地采用单线程的方案了。
Redis是单线程的。如何利用多个CPU /内核?
为了最大化CPU使用率,您可以在同一个框中启动多个Redis实例,并将它们视为不同的服务器。在某些时候,单个盒子可能还不够,所以如果你想使用多个CPU,你可以开始考虑早些时候进行分片。
但是使用Redis 4.0,我们开始使Redis更具线程性。目前,这仅限于在后台删除对象,以及阻止通过Redis模块实现的命令。对于下一个版本,计划是使Redis越来越多线程化。
据说19年下半年可能上多线程的版本。
既然涉及存储,免不了要了解Redis其value的容纳量,单个String类型的Value 512M 最大。
Redis最多可以处理2 个32键,并且在实践中经过测试,每个实例至少处理2.5亿个键。
每个散列,列表,集合和有序集合可以容纳2 32个元素。
Redis的内存很宝贵,有什么办法可以降低Redis的内存使用量吗?
FAQ:如果可以,请使用Redis 32位实例。还可以充分利用小哈希,列表,有序集和整数集,因为Redis能够以更紧凑的方式在少数元素的特殊情况下表示这些数据类型。
Redis的发布与订阅
发布订阅有丢失数据的风险,只有在能够承担丢失一小部分数据风险的情况下才可以使用。【比如:客户端在执行订阅操作过程中线程中断,那么客户端将丢失在断线期间发送的所有消息】
基本的Redis事务
有时候我们需要让Redis同时执行多个事务,虽然Redis可以在两个键之间复制或者移动元素但是却没有在不同类型之间移动元素的命令。为了支持对相同或者不同的类型的多个键操作,Redis有五个指令:
WATCH
MULTI
EXEC
UNWATCH
DISCARD
基本的redis事务需要MULTI 和 EXEC 它可以让客户端在不被其他客户端打断的情况下执行多个指令。【和关系型数据库的rollback不一样,被MULTI 指令 和 EXEC指令 包围的所有命令是一个接一接执行,一直到执行完毕为止,当这个块体内的指令完成后才会去操作其他家客户端的命令,有点像关系型数据库锁表的感觉】
同时使用上述流水线操作的另一个目的也是为了提高性能:
在执行一连串的指令时,减少Redis与客户端之间的通信往返次数可以大幅度的降低客户端等待回复所需要的时间。
但是由于这种简单的事务在EXEC命令被调用之前不会执行任何实际操作, 所以用户将没办法根据 读取到的数据来做决定。这个问题看上去似乎无足轻重, 但实际上无法以一致的形式读取数据将导致某一类型的问题变得难以解决, 除此之外, 因为 在多个事务同时处理同一个对象时通常需要用到二阶提交( two-phase commit ), 所以如果事务不能以 一致的形式读取数据, 那么二阶提交将无法实现, 从而导致一些原本可以成功执行的事务沦落至执行失败的地步。
为什么Redis没有实现典型的加锁功能?
因为加锁有可能会造成长时间的等待,Redis为了尽可能地减少客户端的等待时间, 并不会在执行WATCH命令时对数据进行加锁. 相反地,Redis只会在数据已经被其他客户端抢先修改了的情况下 , 通知执行了 WATCH命令的客户端, 这种做法被称为乐观锁( optimistic locking ), 而关系数据库实际执行的加锁操作则被称为悲观锁( pessimistic locking). 乐观锁在实 际使用中同样非常有效, 因为客户端永远不必花时间去等待第一个取得锁的客户端一一它们只 需要在自己的事务执行失败时进行重试就可以了.
Redis为键设置过期时间
redis可以原子的为键设置过期时间,但是对于set list 之类的容器来说,键过期的命令只能为整个键设置过期时间,而不能为键里面的某个元素单独设置过期时间。【可以使用存储了时间戳的有序集合来实现针对单个元素的过期操作】
eg:
1.使用SortedSet,程序定期清除过期项
2.将集合拆分成多个按时间排序、自动过期的小集合
Redis是如何将数据存储在硬盘里,使得数据在Redis重启之后仍然存在的。
Redis使用了两种不同的方法来将数据存储到硬盘里,一种方法叫快照。它可以将某一时刻的所有数据都写入硬盘里。第二种叫只追加文件(append-only file AOF ),它会在执行命令时将被执行的 写命令 复制到硬盘里。这两种持久化方法既可以同时使用, 又可以单独使用,在某些情况下甚至可以两种方法都不使用,具体选择哪种持久化方法需要根据用户的数据以及应用来决定。
在只使用快照持久化来保存数据时,如果系统真的发生崩溃, 用户将丢失最近一次生成快照之后更改的所有数据。
因此, 快照持久化只适用于那些即使丢失一部分数据也不会造成问题的应用程序, 而不能接受这种数据损失的应用程序则可以考虑使用
创建快照的几种方法
客户端可以通过向Redis发送BGSAVE命令来创建一个快照。对于支持BGSAVE命令的 平台来说 (基本上所有平台都支持, 除了Windows平台), Redis会调用fork,.D来创建一 个子进程, 然后子进程负责将快照写人硬盘, 而父进程则继续处理命令请求。
客户端还可以通过向Redis发送SAVE命令来创建一个快照, 接到SAVE命令的Redis服务器在快照创建完毕之前将不再响应任何其他命令。 SAVE命令并不常用, 我们通常只会在没有足够内存去执行BGSAVE命令的情况下, 又或者即使等待持久化操作执行完毕也无所谓的情况下, 才会使用这个命令。
如果用户设置了save配置选项, 比如save 60 10000, 那么从Redis最近一次创建 快照之后开始算起, 当 “60秒之内有10 000次写人” 这个条件被满足时, Redis就会自动触发BGSAVE命令。 如果用户设置了多个save配置选项,那么当任意一个save配置选项所设置的条件被满足时, Reclis就会触发一次BGSAVE命令。
当Redis通过SHUT DO刚命令接收到关闭服务器的请求时,或者接收到标准TERM信号 时, 会执行一个SAVE命令, 阻塞所有客户端, 不再执行客户端发送的任何命令, 并在SAVE命令执行完毕之后关闭服务器。
当一个Redis服务器连接另一个Redis服务器, 并向对方发送SYNC命令来开始一次复制操作的时候, 如果主服务器目前没有在执行BGSAVE操作, 或者主服务器并非刚刚执行完BGSAVE操作 , 那么主服务器就会执行BGSAVE命令。
对于较大的数据一般会手动bgsave或者使用save【bgsave创建子进程会耗费大量的时间,bgsave虽然会阻塞redis,但是因为它不用创建子线程 ,所以减少了创建子线程而使redis停顿的时间,并且因为没有子线程的争抢资源,所以save会比bgsave来的更快一些】
AOF
简单来说AOF持久化会将被执行的命令写到AOF文件的末尾,以此来记录数据发生的变化,因此Redis只要从头到尾执行一次AOF文件所有的命令,就可以恢复AOF文件所记录的数据。
复制
[场景:耗时较长的读请求,比如大量数据的统计,或者大的集合或者有序集合的操作,以及一些临时写入]
关系数据库通常会使用一个主服务器(master )向多个从服务器( slave )发送更新,并使用从服务器来处理所有读请求。 Redis也采用了同样的方法来实现
自己的复制特性, 并将其用作扩展性能的一种手段。在需要扩展读请求的时候,或者在需要写人临时数据的时候,用户可以通过设置额外的Redis从服务器来保存数据集的副本。 在接收到主服务器发送的数据初始副本之后, 客户端每次向主服务器进行写入时,从服务器都会实时地得到更新。在部署坷,主从服务器之后,客户端就可以向任意一个从服务器发送读请求了,而不必再像之前一样, 总是把每个读请求都发送给主服务器。
创建多个从服务器可能会造成网络不可用一一当复制需要通过五联网进行或 者需要在不同数据中心之间进行时,尤为如此。因为 Redis 的主服务器和从服务器并没有特别不 同的地方,所以从服务器也可以拥有自己的从服务器,并由此形成主从链( master/slave chaining )。
从服务器对从服务器进行复制在操作上和从服务器对主服务器进行复制 的唯一区别在于, 如果从服务器X拥有从服务器 Y,那么当从服务器X在执行快照文件发送完毕开始向从服务器发送存储在缓冲区里面的写命令时,它将断开与从 服务器 Y 的连接,导致从服务器y需要重新连接并重新同步( resync )。当读请求的重要性明显高于写请求的重要性,并且读请求的数量远远超出一 台 Redis服务器 可以处理 的范围时,用户就需要添加新的从服务器来处理读请求。随着负载不断上升,主服务器 可能会无法快速地更新所有从服务器,或者因为重新连接和重新同步从服务器而导致系统超载 。 为了缓解这个问题,用户可以创建一个由 Redis 主从节点( master/slave node )组成 的中间层来分担主服务器的复制工作
2019/3/27 log 持续更新中...