Redis

一、名词解释

1.Redis支持AOF和RDB两种持久化方式。

参考:
https://blog.csdn.net/qq_36795474/article/details/82938721?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.control&dist_request_id=1328627.8285.16153388408500279&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.control

https://blog.csdn.net/denghonghao/article/details/82108770

由于Redis的数据都存放在内存中,如果没有配置持久化,redis重启后数据就全丢失了,于是需要开启redis的持久化功能,将数据保存到磁盘上,当redis重启后,可以从磁盘中恢复数据。
  RDB(Redis DataBase) 持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘,实际操作过程是fork一个子进程,先将数据集写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储。
  (默认使用RDB)
  AOF(Append Only File)持久化以日志的形式记录服务器所处理的每一个写、删除操作,查询操作不会记录,以文本的方式记录,可以打开文件看到详细的操作记录。只许追加文件,不可以改写文件,redis重启后会读取appendonly.aof文件来实现重新恢复数据,完成恢复数据的工作。
  
  当两种方式同时开启时,数据恢复Redis会优先选择AOF恢复。
  
RDB优点

  1. 只有一个文件,dump.rdb 方便管理
  2. 是fork一个子进程单独进行持久化,不会使主进程进行io操作,效率高。
  3. 容灾性好,使文件可以安全保存至磁盘
  4. 相对大的数据集,比AOF效率高

RDB缺点

  1. 每次进行都会fork一个子进程,如果数据集过于庞大,那么fork的过程会过于耗时,会出现服务器暂停客户端请求,将内存中的数据复制一份给子进程,让子进程进行持久化操作。
  2. 不太适用于严格要求数据完整的情况,RDB是间隔一段时间进行持久化,容易出现还没有出发快照,就宕机的情况,那么一些数据库的写操作就还没来的急持久化保存。

AOF有着多种持久化策略:
 
  appendfsync always: 每修改同步,每一次发生数据变更都会持久化到磁盘上,性能较差,但数据完整性较好。
  appendfsync everysec: 每秒同步,每秒内记录操作,异步操作,如果一秒内宕机,有数据丢失。
  appendfsync no: 不同步。

AOF优点:

  1. 有always命令 ,能保证每次操作都能记录下来至aof文件,保障了数据安全。
  2. 通过append方式添加文件,即使宕机,也可以通过redis-check-aod解决数据一致性问题。
  3. 当日志过大时,可以采取rewrite机制。即Redis以append模式不断的将修改数据写入到老的磁盘文件中,同时Redis还会创建一个新的文件用于记录此期间有哪些修改命令被执行。因此在进行rewrite切换时可以更好的保证数据安全性。

AOF缺点:

  1. AOF文件比RDB文件大,且恢复速度慢
  2. 数据集大的时候,比rdb效率低

二者比较:

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

2. Redis的VM(虚拟内存)机制

参考:https://blog.csdn.net/Seky_fei/article/details/106843764
  Redis的VM(虚拟内存)机制就是暂时把不经常访问的数据(冷数据)从内存交换到磁盘中,从而腾出宝贵的内存空间用于其它需要访问的数据(热数据)。
  通过VM功能可以实现冷热数据分离,使热数据仍在内存中、冷数据保存到磁盘。这样就可以避免因为内存不足而造成访问速度下降的问题。
  Redis提高数据库容量的办法有两种:一种是可以将数据分割到多个Redis Server上;另一种是使用虚拟内存把那些不经常访问的数据交换到磁盘上.
  VM 机制 ,不会像一般的系统会调用系统函数处理,会浪费一定的时间去 移动 和 请求.

3.redis内存淘汰策略

参考:https://www.zhihu.com/question/396791243
https://blog.csdn.net/wsdc0521/article/details/106997623

1.参数设置

1.在redis.config文件下,设置数据库的最大缓存,
在这里插入图片描述
2.设置淘汰的策略,分为8种

LRU:最近最少使用
LFU:最不经常使用
(LRU和LFU算法都是在Redis内存占用满的情况下的淘汰策略)

  1. volatile-lru:设置了过期时间的key使用LRU算法淘汰;
  2. allkeys-lru:所有key使用LRU算法淘汰;
  3. volatile-lfu:设置了过期时间的key使用LFU算法淘汰;
  4. allkeys-lfu:所有key使用LFU算法淘汰;
  5. volatile-random:设置了过期时间的key使用随机淘汰;
  6. allkeys-random:所有key使用随机淘汰;
  7. volatile-ttl:设置了过期时间的key根据过期时间淘汰,越早过期越早淘汰;
  8. no-eviction:默认策略,当内存达到设置的最大值时,所有申请内存的操作都会报错(如set,lpush等),只读操作如get命令可以正常执行;

这八种大体上可以分为4中,lru、lfu、random、ttl。

3.内存没占满时在Redis中过期的key是如何从内存中删除以达到优化内存占用的呢?  
  在Redis中过期的key不会立刻从内存中删除,而是会同时以下面两种策略进行删除:

  • 惰性删除:当key被访问时检查该key的过期时间,若已过期则删除;已过期未被访问的数据仍保持在内存中,消耗内存资源;
  • 定期删除:每隔一段时间,随机检查设置了过期的key并删除已过期的key;维护定时器消耗CPU资源;

4.redis 如何做内存优化

1、缩减键值对象(缩减键(key)和值(value)的长度)
  key长度:如在设计键时,在完整描述业务情况下,键值越短越好
  value长度:值对象缩减比较复杂,常见需求是把业务对象序列化成二进制数组放入Redis。首先应该在业务上精简业务对象,去掉不必要的属性避免存储无效数据。其次在序列化工具选择上,应该选择更高效的序列化工具来降低字节数组大小。以JAVA为例,内置的序列化方式无论从速度还是压缩比都不尽如人意,这时可以选择更高效的序列化工具,如: protostuff,kryo等,下图是JAVA常见序列化工具空间压缩对比。

2、共享对象池
  对象共享池指Redis内部维护[0-9999]的整数对象池。创建大量的整数类型redisObject存在内存开销,每个redisObject内部结构至少占16字节,甚至超过了整数自身空间消耗。所以Redis内存维护一个[0-9999]的整数对象池,用于节约内存。 除了整数值对象,其他类型如list,hash,set,zset内部元素也可以使用整数对象池。因此开发中在满足需求的前提下,尽量使用整数对象以节省内存。
但是共享对象池与maxmemory+LRU策略冲突,使用时需要注意。
3、字符串优化
  字符串对象是Redis内部最常用的数据类型,在使用过程中应当尽量优先使用整数,比字符串类型更节省空间。并且要优化字符串使用,避免预分配造成的内存浪费。使用ziplist压缩编码优化hash、list等结构,注重效率和空间的平衡。
4、编码优化
  Redis对外提供了多种数据类型,但是Redis内部对于不同类型的数据使用的内部编码不一样。内部编码不同将直接影响数据的内存占用和读写效率。

5、控制key的数量
  当使用Redis存储大量数据时,通常会存在大量键,过多的键同样会消 耗大量内存。

5.redis事务

参考:https://www.cnblogs.com/fengguozhong/p/12161363.html

1.Redis事务相关命令:

  • MULTI :开启事务,redis会将后续的命令逐个放入队列中,然后使用EXEC命令来原子化执行这个命令系列。
  • EXEC:执行事务中的所有操作命令。
  • DISCARD:取消事务,放弃执行事务块中的所有命令。
  • WATCH:监视一个或多个key,如果事务在执行前,这个key(或多个key)被其他命令修改,则事务被中断,不会执行事务中的任何命令.
    (相当于是一个乐观锁,可以为 Redis 事务提供 check-and-set (CAS)行为。 可以监控一个或多个键,一旦其中有一个键被修改(或删除),之后的事务就不会执行,监控一直持续到EXEC命令。)
  • UNWATCH:取消WATCH对所有key的监视。

2.为什么Redis不支持事务回滚?
  多数事务失败是由语法错误或者数据结构类型错误导致的,语法错误说明在命令入队前就进行检测的,而类型错误是在执行时检测的,Redis为提升性能而采用这种简单的事务,这是不同于关系型数据库的,特别要注意区分。

3.Redis会将一个事务中的所有命令序列化,然后按顺序执行。

redis 不支持回滚,“Redis 在事务失败时不进行回滚,而是继续执行余下的命令”, 所以 Redis 的内部可以保持简单且快速。
如果在一个事务中的命令出现错误,那么所有的命令都不会执行;
如果在一个事务中出现运行错误,那么正确的命令会被执行。

(Redis的事务总是具有ACID中的一致性和隔离性,在aof吃鸡恶化always选项时,也具有持久性,但没有原子性)

6.redis集群

参考:https://www.cnblogs.com/vieta/p/11192137.html
1.集群:   
  集群,就是通过添加服务器的数量,提供相同的服务,从而让服务器达到一个稳定、高效的状态。
  
(1)单个redis存在不稳定性。当redis服务宕机了,就没有可用的服务了。

(2)单个redis的读写能力是有限的。

总结:redis集群是为了强化redis的读写能力。
2.redis集群三种模式:
主从模式、 Sentinel(哨兵)模式、Cluster模式

1.主从模式

主从模型内,有多个redis节点,只有一个master主节点,从节点slave可以有多个。
  只要网络正常,Master会一直将自己的数据更新同步给Slaves,保持主从同步。
  
  (1)主节点Master可读、可写.
  (2)从节点Slave只读。(read-only)
  
  主从模型可以提高读的能力,在一定程度上缓解了写的能力。因为能写仍然只有Master节点一个,可以将读的操作全部移交到从节点上,变相提高了写能力。
  缺点:
  主节点宕机,整个集群就没有可写的节点了。

2. Sentinel(哨兵)模式

(是在主从模式的基础上,如果只有一个Redis节点,sentinel就没有任何意义)
  Redis 的 Sentinel 系统用于管理多个 Redis 服务器(instance), 该系统执行以下三个任务:
  监控(Monitoring): Sentinel 会不断地检查你的主服务器和从服务器是否运作正常。
  提醒(Notification): 当被监控的某个 Redis 服务器出现问题时, Sentinel 可以通过 API 向管理员或者其他应用程序发送通知。
  自动故障迁移(Automatic failover): 当一个主服务器不能正常工作时, Sentinel 会开始一次自动故障迁移操作, 它会进行选举,将其中一个从服务器升级为新的主服务器, 并让失效主服务器的其他从服务器改为复制新的主服务器; 当客户端试图连接失效的主服务器时, 集群也会向客户端返回新主服务器的地址, 使得集群可以使用新主服务器代替失效服务器。

在这里插入图片描述

  • Sentinel可以监控任意多个Master和该Master下的Slaves。(即多个主从模式)
  • 同一个哨兵下的、不同主从模型,彼此之间相互独立。

在这里插入图片描述

Sentinel网络   
  监控同一个Master的Sentinel会自动连接,组成一个分布式的Sentinel网络,互相通信并交换彼此关于被监视服务器的信息。下图中,三个监控s1的Sentinel,自动组成Sentinel网络结构。
  当只有一个sentinel的时候,如果这个sentinel挂掉了,那么就无法实现自动故障切换了。
  在sentinel网络中,只要还有一个sentinel活着,就可以实现故障切换。
  
注意:

  • 哨兵至少需要 3 个实例,来保证自己的健壮性。
  • 哨兵 + redis 主从的部署架构,是不保证数据零丢失的,只能保证 redis集群的高可用性。
  • 对于哨兵 + redis 主从这种复杂的部署架构,尽量在测试环境和生产环境,都进行充足的测试和演练。

3.Cluster模式

sentinel模式基本可以满足一般生产的需求,具备高可用性。但是当数据量过大到一台服务器存放不下的情况时,主从模式或sentinel模式就不能满足需求了,这个时候需要对存储的数据进行分片,将数据存储到多个Redis实例中。
cluster模式的出现就是为了解决单机Redis容量有限的问题,将Redis的数据根据一定的规则分配到多台机器。
使用集群,只需要将redis配置文件中的cluster-enable配置打开即可。每个集群中至少需要三个主数据库才能正常运行,新增节点非常方便。

cluster集群特点:

  • 多个redis节点网络互联,数据共享

  • 所有的节点都是一主一从(也可以是一主多从),其中从不提供服务,仅作为备用

  • 不支持同时处理多个key(如MSET/MGET),因为redis需要把key均匀分布在各个节点上,
    并发量很高的情况下同时创建key-value会降低性能并导致不可预测的行为

  • 支持在线增加、删除节点

  • 客户端可以连接任何一个主节点进行读写

7.redis分区

分区是分割数据到多个Redis实例的处理过程,因此每个实例只保存key的一个子集。

1.分区好处:

  1. 提升性能,单机redis的i/o能力和计算资源是有限的,随着数据的增加,会受限于机器本身的存储容量,将请求分散。分区使Redis的计算能力通过简单地增加计算机得到成倍提升,Redis的网络带宽也会随着计算机和网卡的增加而成倍增长。
  2. 存储的横向扩展,即使Redis的服务能力能够满足应用需求,但是随着存储数据的增加,单台机器受限于机器本身的存储容量,将数据分散到多台机器上存储使得Redis服务可以横向扩展。

2.分区方式

1.范围分区:所谓范围分区,就是将一个范围内的key都映射到同一个Redis实例中,加入数据集还是上面提到的用户数据。

2.哈希分区:哈希分区跟范围分区相比一个明显的优点是哈希分区适合任何形式的key,而不像范围分区一样需要key的形式为object_name:,而且分区方法也很简单,一个公式就可以表达:id=hash(key)%N

3.分区的实现方式

  1. 客户端实现:在客户端就已经确定数据会被存储到哪个redis节点上。
  2. 代理实现:将客户端的请求发送给代理服务器,代理根据分区规则决定请求哪些Redis实例,然后根据Redis的响应结果返回给客户端。redis和memcached的一种代理实现就是Twemproxy
  3. 查询路由:将查询请求随机的发送到任意一个Redis实例,这个Redis实例负责将请求转发至正确的Redis实例中。但并不是直接将请求从一个redis节点转发到另一个redis节点,而是在客户端的帮助下直接redirected到正确的redis节点。Redis集群实现了一个通过和客户端协作的hybrid来做查询路由。

4.redis分区的缺点

1、多键操作是不被支持的,比如我们将要批量操作的键被映射到了不同的Redis实例中。

2、多键的Redis事务是不被支持的。

3、分区的最小粒度是键,能使用一个非常长的排序key存储一个数据集

4、当应用分区的时候,数据的处理是非常复杂的,比如我们需要处理多个rdb/aof文件,将分布在不同实例的文件聚集到一起备份。

5、添加和删除机器是很复杂的,例如Redis集群支持几乎运行时透明的因为增加或减少机器而需要做的rebalancing,然而像客户端和代理分区这种方式是不支持这种功能的。

8.Redis实现分布式锁

参考:https://www.jianshu.com/p/a1ebab8ce78a

**分布式锁:**为了防止分布式系统中的多个进程之间相互干扰,我们需要一种分布式协调技术来对这些进程进行调度。而这个分布式协调技术的核心就是来实现这个分布式锁。

Redis为单进程单线程模式,采用队列模式将并发访问变成串行访问,且多客户端对Redis的连接并不存在竞争关系Redis中可以使用SETNX命令实现分布式锁。

redis实现:setnx 加锁del解锁
exipre设置锁时间

锁超时:如果一个得到锁的线程在执行任务的过程中挂掉,来不及显式地释放锁,这块资源将会永远被锁住(死锁)

RedLock: :Redis 官方站提出了一种权威的基于 Redis 实现分布式锁的方式名叫 Redlock。
特性:

  • 安全特性:互斥访问,即永远只有一个 client 能拿到锁
  • 避免死锁:最终 client 都可能拿到锁,不会出现死锁的情况,即使原本锁住某资源的 client crash 了或者出现了网络分区
  • 容错性:只要大部分 Redis节点存活就可以正常提供服务

9.缓存异常

1. 缓存雪崩

缓存雪崩是指缓存同一时间大面积的失效,所以,后面的请求都会落到数据库上,造成数据库短时间内承受大量请求而崩掉。

解决方案:

  1. 缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。
  2. 一般并发量不是特别多的时候,使用最多的解决方案是加锁排队。
  3. 给每一个缓存数据增加相应的缓存标记,记录缓存的是否失效,如果缓存标记失效,则更新数据缓存。

2.缓存穿透

缓存穿透是指缓存和数据库中都没有的数据,导致所有的请求都落到数据库上,造成数据库短时间内承受大量请求而崩掉。

解决方案:

  • 接口层增加校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截
  • 从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击
  • 采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的 bitmap 中,一个一定不存在的数据会被这个 bitmap
    拦截掉,从而避免了对底层存储系统的查询压力

3.缓存击穿

缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力。和缓存雪崩不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。

解决方案

  • 设置热点数据永远不过期。
  • 加互斥锁,互斥锁

4.缓存预热

缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!

解决方案

  • 直接写个缓存刷新页面,上线时手工操作一下;

  • 数据量不大,可以在项目启动的时候自动进行加载;

  • 定时刷新缓存;

5.缓存降级

当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然需要保证服务还是可用的,即使是有损服务。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级。

缓存降级的最终目的是保证核心服务可用,即使是有损的。而且有些服务是无法降级的(如加入购物车、结算)。

在进行降级之前要对系统进行梳理,看看系统是不是可以丢卒保帅;从而梳理出哪些必须誓死保护,哪些可降级;比如可以参考日志级别设置预案:

  1. 一般:比如有些服务偶尔因为网络抖动或者服务正在上线而超时,可以自动降级;
  2. 警告:有些服务在一段时间内成功率有波动(如在95~100%之间),可以自动降级或人工降级,并发送告警;
  3. 错误:比如可用率低于90%,或者数据库连接池被打爆了,或者访问量突然猛增到系统能承受的最大阀值,此时可以根据情况自动降级或者人工降级;
  4. 严重错误:比如因为特殊原因数据错误了,此时需要紧急人工降级。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值