Redis学习笔记1

Redis-分布式(mianshi)绕不开的话题

被打的体无完肤,才能越挫越勇

何为redis

简单讲,redis就和我们常见的mysql,sqlserver,oracle一样都是一种数据库,不同点在于redis面试常问(此处手动狗头)。正经:redis与传统关系型数据库不同,它是一种非关系型数据库即NoSql(not only sql),而且redis的数据是存储在内存中的(也可支持数据持久化,厉害),存取速度非常快,因此redis被广泛用于缓存方向,带来的问题是服务器内存有限且比硬盘珍贵,不过这都不是事。另外,在分布式系统中,由于业务当中经常需要进行一致性处理,此时redis也可做分布式锁。redis 提供了多种数据类型来支持不同的业务场景。除此之外,redis 支持事务 、持久化、LUA脚本、LRU驱动事件、多种集群方案。

为什么要使用redis

项目中使用Redis,主要考虑性能并发。当然redis的一些其他特性也是考虑的因素。
1.解决应用服务器的cpu和内存压力
2. 减少io的读操作,减轻io的压力
3.关系型数据库的扩展性不强,难以改变表结构
假如用户第一次访问数据库中的某些数据。这个过程会比较慢,因为是从硬盘上读取的。将该用户访问的数据存在数缓存中,这样下一次再访问这些数据的时候就可以直接从缓存中获取了。操作缓存就是直接操作内存,所以速度相当快。
直接操作缓存能够承受的请求是远远大于直接访问数据库的,所以我们可以考虑把数据库中的部分数据转移到缓存中去,这样用户的一部分请求会直接到缓存这里而不用经过数据库,那么也就是可以同时支持更高的并发请求量而不至于系统崩溃。

redis优点

  • 响应速度快,因为是内存数据库,每秒大约可以执行110000个写入操作/81000个读取操作,可以有效提高系统性能。
  • 支持的数据类型丰富,有string,list,set,sorted set,hash,此外还有支持存储地理位置的Redis GEO以及用于消息队列的Redis Stream。
  • 支持原子操作,redis的所有操作的都是原子性的,即要么全部执行成功要么全部执行失败,单个操作也是原子性的,在多个操作下可以使用事务,通过MULTI和EXEC指令包起来。
  • 丰富的特性,redis支持发布/订阅模式,通知,key过期等特性。
  • 支持主从复制,主机会自动将数据同步到从机,可以进行读写分离。

redis缺点/问题

  • 内存数据库受物理内存限制,不能用于海量数据的高性能读写。
  • Redis不具备自动容错和恢复功能,主机从机的宕机都会导致前端部分读写请求失败,需要等待机器重启或者手动切换前端的IP才能恢复。
  • 主机宕机,宕机前有部分数据未能及时同步到从机,切换IP后还会引入数据不一致的问题,降低了系统的可用性。
  • Redis较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。
  • 缓存与数据库双写一致性问题。
  • 缓存雪崩问题。
  • 缓存击穿问题。
  • 缓存穿透问题。
  • 缓存的并发竞争问题。

redis相比于memcached有什么优势

  1. memcached的所有值均为简单的字符串,数据类型单一,redis作为替代者,支持更加丰富的数据类型。
  2. 速度比memcached快很多。
  3. redis可以持久化数据,memcached不能。

redis数据类型

  • string:简单的字符串,key-value形式,比如需要存储一些文本或者json字符串可以使用该数据类型,其value最大可以存储512M。
  • list:简单的字符串列表,key-value1,value2,value3…形式,按照插入的顺序排序,可以添加一个元素到头部(最左边)或者尾部(最右边)。redis的一个列表最多可以包含 2 32 − 1 2^{32}-1 2321(4294967295,超过40亿)个元素。
  • set:简单的字符串无序集合,key-value1,value2,value3…形式,成员是唯一的,也就是说集合中不能出现重复的数据。其实现方式是通过hash表实现的,因此添加,删除,查找的复杂度都是O(1)。最多可以包含 2 32 − 1 2^{32}-1 2321(4294967295,超过40亿)个元素。
  • hash:哈希表,简单的字符串类型的key-value,key-[key1,value1],[key2-value2]…形式,特别适合于存储对象。最多可以包含 2 32 − 1 2^{32}-1 2321(4294967295,超过40亿)个元素。
  • sorted set:是字符串类型的有序集合,且不允许重复的成员,与无序集合区别在于每个元素都会关联一个double类型的分数值,通过分数值为成员进行从小到大的排序。成员唯一但是分数值可以重复。其实现方式是通过hash表实现的,因此添加,删除,查找的复杂度都是O(1)。最多可以包含 2 32 − 1 2^{32}-1 2321(4294967295,超过40亿)个元素。

redis为什么是单线程的,怎么充分利用CPU资源

redis采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗cpu,不需要去考虑各种并发问题,比如并发条件下加解锁的问题,没有因为可能出现死锁而导致的性能消耗。redis使用【多路I/O复用模型】,非阻塞IO。
官方解释:因为Redis是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络带宽。既然单线程容易实现,而且CPU不会成为瓶颈,那就顺理成章地采用单线程的方案了。

题外话
多路 I/O 复用模型
多路I/O复用模型是利用 select、poll、epoll 可以同时监察多个流的 I/O 事件的能力,在空闲的时候,会把当前线程阻塞掉,当有一个或多个流有 I/O 事件时,就从阻塞态中唤醒,于是程序就会轮询一遍所有的流(epoll 是只轮询那些真正发出了事件的流),并且只依次顺序的处理就绪的流,这种做法就避免了大量的无用操作。
**这里“多路”指的是多个网络连接,“复用”指的是复用同一个线程。**采用多路 I/O 复用技术可以让单个线程高效的处理多个连接请求(尽量减少网络 IO 的时间消耗),且 Redis 在内存中操作数据的速度非常快,也就是说内存内的操作不会成为影响Redis性能的瓶颈,主要由以上几点造就了 Redis 具有很高的吞吐量。

如果想充分利用CPU资源,那就可以在多核服务器上运行多个redis实例。由于是单线程模型,Redis 更喜欢大缓存快速 CPU, 而不是多核。

redis应用场景有哪些

缓存,Redis主力使用场景,在提升服务器性能方面非常有效。

1.排行榜,如果使用传统的关系型数据库来做,非常麻烦,而利用Redis的SortSet数据结构能够非常方便搞定;

2.计算器/限速器,利用Redis中原子性的自增操作,我们可以统计类似用户点赞数、用户访问数等,这类操作如果用MySQL,频繁的读写会带来相当大的压力;限速器比较典型的使用场景是限制某个用户访问某个API的频率,常用的有抢购时,防止用户疯狂点击带来不必要的压力;

3.好友关系,利用集合的一些命令,比如求交集、并集、差集等,可以方便搞定一些共同好友、共同爱好之类的功能;

4.简单消息队列,除了Redis自身的发布/订阅模式,我们也可以利用List来实现一个队列机制,比如到货通知、邮件发送之类的需求,不需要高可靠,但是会带来非常大的DB压力,完全可以用List来完成异步解耦;

5.Session共享,以PHP为例,默认Session是保存在服务器的文件中,如果是集群服务,同一个用户过来可能落在不同机器上,这就会导致用户频繁登陆;采用Redis保存Session后,无论用户落在那台机器上都能够获取到对应的Session信息。

6.分布式锁,使用setnx,px方法可以模拟分布式锁;使用Redisson开原框架实现分布式锁,使用官方的redlock实现分布式锁。
7.分布式数据共享,因为redis十分不是的独立服务,可以在多个系统之间很方便的实现数据共享。
8.全局唯一ID,利用redis的incrby的原子性操作,可以实现分布式的全局唯一ID。
9.位统计,利用redis的bitmap进行bit位计算,可以极大地节省空间,比兔统计在线用户,活跃用户等。
10.购物车系列,商品ID为key,商品数量为value,使用原子性自增操作,方便与库存进行比较。
11.用户关注,粉丝等。
12.商品标签,商品筛选等。

redis部署有哪些方式

1.单机模式

优点:
部署简单,0成本。
成本低,没有备用节点,不需要其他的开支。
高性能,单机不需要同步数据,数据天然一致性。
缺点:
可靠性保证不是很好,单节点有宕机的风险。
单机高性能受限于CPU的处理能力,redis是单线程的。

2.主从复制模式

是指将一台Redis服务器的数据,复制到其他的Redis服务器,复制数据的服务器为从(slave)服务器,被复制数据的服务器称为主(master)服务器。
一般来说,主服务器负责写数据,从服务器负责读数据,这样可以水洗建读写分离,提高吞吐率,降低主服务器的压力。
主从模式,可以是树状结构,从服务属于多台主服务,且从服务也可以有从服务。
主从复制模式的优点:
1.同一个master可以同步多个slaves。
2.slave同样可以接受其他slaves的连接和同步请求,这样可以有效的分在master的同步压力。因为和我们可以视主从复制模式为结构。
3.master服务器是以非阻塞方式完成数据同步的,所以在同步器件,客户端仍然可以提交查询或者修改请求,在同步期间,如果有客户端提交查询请求,redis则返回同步之前的数据,直至同步完成,新的数据才会提供服务。
4.为了分在master服务器的读操作压力,slave服务器可以为客户端提供只读操作,而master只提供写服务操作,以此来实现系统读写分离,提高系统伸缩性。
5.master可以将数据的持家化操作交由slave服务器执行,从而避免了在master中开辟独立的进程来完成持久化操作而影响性能。
6.支持主从复制,主机会自动将数据同步到从机。
主从复制模式的缺点:
1.redis不具备自动容错和恢复功能,主机从机的宕机都会导致客户端的部分读写请求失败,需要等待机服务重启或者手动求换服务的ip。
2.主机宕机,宕机前有部分数据没能及时同步到从机,切换主机后会造成数据不一致的问题,降低了系统的可用性。
3.redis主从复制采用全量复制的情况下,复制过程中主机会fork一个子进程对内存做一个快照,并将快照保存为文件发送给从机,这一过程需要确保主机有足够多的空余内存。若快照文件过大,网络条件不好的情况下也会对集群的服务能力产生影响。
4.redis较难支持在线扩容,在集群容量达到上限的时候在线扩容会变得很复杂。

3.哨兵模式(Sentinel)

哨兵模式是一种特殊的模式,首先redis本身提供了哨兵的命令,哨兵是一个独立的进程,独立运行,其原理是哨兵通过发送命令,等待redis服务器的响应,从而监控运行中的多个服务器。
哨兵进程的作用

  1. 监控:哨兵会不断的检查redis的master和slave服务器运行是否正常。
  2. 提醒:当被监控的某个redis节点出现问题时,哨兵可以通过api向管理员或者其他的应用程序发送通知。
  3. 自动故障转移:当一个master不能正常工作时,哨兵会开始一次自动故障转移操作
    1)哨兵会将失效master所拥有的slave里面选取一个节点作为新的master,并通知其他的slave,同步修改所依赖的master服务器。
    2)当客户端试图连接失效的master时,集群也会向客户端返回新的那是特然地址。
    3)master和slave服务器切换后,master的redis.conf,slave的redis.conf和sentinel.conf的配置文件的内容都会发生相应的变化,即master的redis.conf配置文件会多一行slaveof的配置,sentinel.conf的监控目标也会随之调整。

哨兵进程的工作方式

  1. 每个sentinel进程以每秒一次的频率向整个集群的master主服务器,slave从服务器以及其他sentinel进程发送一个ping命令。
  2. 如果一个实例距离最后一次有效回复PING命令的时间超过down-after-milliseconds选项所指定的值,则这个实例会被sentinel进程标记为主观下线(Subjective down)
  3. 如果一个master服务器被标记为主观下线(SDOWN),则正在监视这个master服务器的所有sentinel进程将会以每秒一次的频率向master服务器发送消息,以此来确认master服务器的确进入了主观下线状态
  4. 当有足够数量的sentinel进程(大于等于配置文件中指定的数量)在指定的时间范围内确认master服务器进入主观下线状态(SDOWN),则master服务器会被标记为客观下线(Objective Down)
  5. 在一般情况下,每个sentinel进程会以每10秒一次的频率向及群众的所有master服务器,slave服务器发送INFO命令。
  6. 当master服务器被sentinel进程标记为客观下线(ODOWN)时,sentinel进程向下线的master服务器下所有的slave服务器发送INFO命令的频率从每10秒一次改为每秒一次
  7. 若没有足够数量的sentinel进程同意master服务器下线,master服务器的客观下线状态将会被移除。若master服务器重新向sentinel进程发送PING命令并返回有效回复,则master主观下线状态将会被移除。

sentinel(哨兵)在内部有三个定时任务

  1. 每10s每个sentinel会对master和slave执行INFO命令,1:发现slave节点,2:确认主从关系。
  2. 每2s每个sentinel通过master节点的channel交换信息(pub/sub)。master节点上有一个发布订阅通道,sentinel节点通过该通道进行信息交换并达成共识。
  3. 每1s每个sentinel对其他的sentinel和redis的节点(master以及slave)执行PING命令来进行相互监控,这是一个心跳检测,作为失败判定的依据。

4.集群模式-多主多从(cluster)

配置命令:./redis-cli --cluster create 192.168.1.250:7000 192.168.1.250:7001 192.168.1.243:7002 192.168.1.243:7003 192.168.1.224:7004 192.168.1.224:7005 --cluster-replicas 1
–cluster-replicas 1:表示希望为集群中的每个主节点创建一个从节点(一主一从)。
Redis Cluster是一种服务器Sharding技术,3.0版本开始正式提供。 一般由多个节点组成,节点数量至少为 6 个才能保证组成完整高可用的集群,其中三个为主节点,三个为从节点。三个主节点会分配槽,处理客户端的命令请求,而从节点可用在主节点故障后,顶替主节点。
redis的服务器节点之间任何两个节点都是相互连通的。客户端可以与任何一个节点相连接,然后就可以访问集群中的任何一个节点。对其进行存取和其他操作。
redis集群数据分片:
redis集群采用虚拟哈希槽分区,所有的键根据哈希函数映射到0~16383个整数槽内,哈希函数采用的公式为CRC16(循环冗余校验)算法,没一个节点都负责维护一部分槽以及槽所映射的键值数据。
一般来说,如果不手动指定,槽分配的原则是根据指定的master服务器的数量进行平均分配。
redis虚拟槽分区的特点:

  • 解耦数据和节点之间的关系,简化了节点扩容和收缩的难度。
  • 节点自身维护槽的映射关系,不需要客户端或者代理服务器维护槽分区元数据。
  • 支持节点、槽和键之间的映射查询,用于数据路由,在线集群伸缩等场景。

redis集群提供了灵活的节点扩容和节点缩容方案,在不影响集群对外服务的情况下, 可以为集群添加节点进行扩容,也可以下线一部分节点进行缩容,槽是Redis集群管理数据的基本单位,集群的伸缩就是槽和数据在节点之间的移动。

redis持久化机制

redis持久化有RDB(内存快照)方式和AOF(文件追加)方式。

RDB(Redis Database)方式

RDB其实就是把内存数据以快照的方式保存到磁盘上。RDB持久化是指在指定的时间间隔内将内存中的数据集快照以二进制形式写入到磁盘中,默认的文件名为dump.rdb,该方式是redis默认的持久化方式。

RDB手动触发方式:save和bgsave命令
save命令:该命令会阻塞当前redis服务器,直到RDB过程完成为止,对于内存比较大的实例会造成长时间阻塞,线上环境慎用。
bgsave命令:该命令是redis进程执行fork操作城建一个子进程,RDB持久化过程由子进程负责,完成后自动结束。阻塞只发生在fork阶段,一般来说时间很短,不会影响redis对外提供服务的能力。

RDB自动触发方式:
在redis.conf配置文件中有一个配置参数,“save m n”,意思是在m秒内数据集存在n次修改时,自动触发bgsave命令。该参数可以存在多个,并集执行。如果不需要自动触发持久化,可以注释掉所有的save行。

RDB持久化的优势

  1. RDB是一个紧凑压缩的二进制文件,代表redis在某个时间点上的数据快照。非常适用于普通备份,容灾备份,全量复制等场景。
  2. redis在回复大数据集时加载RDB恢复数据的速度远远大于AOF的方式。
  3. 生成RDB文件的时候,redis会foek一个子进程来处理所有包村工作,主进程不需要进行任何磁盘IO操作,对性能影响不大。

RDB持久化的劣势
RDB快照是一次全量备份,存储的是内存数据的二进制序列化形式,数据非常紧凑。且在进行快照期间主进程所产生的修改操作不会映射到子进程,即在此期间的数据是没法快照保存的,可能会丢失数据。

AOF(Append Only File)方式

以独立日志的形式记录每次的修改命令,重启时再重新执行AOF文件中的命令达到恢复数据的目的。其主要作用是解决了数据持久化的实时性,当前已成为redis的主流持久化方式。
AOF工作过程:

  1. 所有的写命令都会追加到aof_buf中。
  2. AOF缓冲区根据对应的策略想硬盘做同步操作。
  3. 随着AOF文件越来越大,redis会定期对AOF文件进行重写,达到压缩的目的。

AOF文件重写
redis通过执行bgrewriteaof命令来对aof文件重写,重写过程中,redis将会生成一个新的aof文件,此过程不会打断redis对外提供服务的能力。重写后的文件包含重建当前数据集所需要的最少命令集合。
原理
AOF重写利用写时复制机制:redis执行fork()函数创建一个子进程。
子进程开始将新AOF文件的内容写到临时文件。
对于所有新执行的写入命令,父进程一边将他们累积到一个内存缓冲区中,一边将这些改动追加到现有的AOF文件的末尾,这样即使在重写的中途发生停机,现有的AOF文件也还是安全的,可以保证数据正确性。
AOF在重写的过程中不是分析之前执行写入的命令,而是根据数据库中数据集的状态生成还原键的当前值的命令,这样做可以减少很多无用的命令,从而压缩体积。

AOF保存模式

  • AOF_FSYNC_NO:不保存,写入和保存都由主进程执行,两个操作都会阻塞主进程。
    宕机丢失的数据量:操作系统最后一次对 AOF 文件触发 SAVE 操作之后的数据。
    阻塞情况:WRITE阻塞,SAVE阻塞。
    该模式下,每次调用flushAppendOnlyFile函数,WRITE命令都会被执行,但是SAVE命令不会执行。SAVE只会在Redis被关闭或者AOF功能被关闭或者系统的写缓存被刷新这三种情况下执行。
  • AOF_FSYNC_EVERYSEC:每秒钟保存一次,写入操作由主进程执行,阻塞主进程。保存操作由子线程执行,不直接阻塞主进程,但保存操作完成的快慢会影响写入操作的阻塞时长。
    宕机丢失的数据量:一般情况下不超过 2 秒钟的数据。
    阻塞情况:WRITE阻塞,SAVE不阻塞。
    原则上该模式每秒钟执行一次,但是在实际运行中,该模式下执行fsync和fdatasync函数并不是每秒钟一次,它和调用flushAppendOnlyFile函数式redis的状态有关,可能会出现一下四种情况:
    1.子线程正在执行save且该save的执行时间未超过2s,那么程序会直接返回,并不会执行write或者新的save。
    2.子线程正在执行save且该save执行时间已经超过2s,那么程序会执行write但不执行新的save,注意:因为这时的write写入必须等待子线程完成(save执行完成),因为这里的write会比平时阻塞更长的时间。
    3.子线程没有在执行save且上次成功执行save距今不超过1s,那么程序会执行write但不执行save。
    4.子线程没有在执行save且上次成功执行save距今已超过1s,那么程序会执行write和save。
  • AOF_FSYNC_ALWAYS:每执行一个命令保存一次,写入和保存都由主进程执行,两个操作都会阻塞主进程。
    宕机丢失的数据量:最多只丢失一个命令的数据。
    阻塞情况:WRITE阻塞,SAVE阻塞。

AOF自动重写触发条件(全部满足)
1.没有BGSAVE命令正在进行。
2.没有BGREWRITEAOF命令正在进行。
3.当前AOF文件的大小大于server.aof_rewrite_min_size(默认1MB)。
4.当前AOF文件大小和最后一次AOF重写后的大小之间的比率大于等于指定的增长百分比。
提示:服务器在AOF开启的情况下,会维持以下三个变量:

  • 记录当前AOF文件大小的变量aof_current_size
  • 记录最后一次AOF重写后,AOF文件大小的变量aof_rewrite_base_size
  • 增长百分比变量aof_rewrite_perc,默认该比率为100%
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值