一文搞懂redis

一、redis简介

1、redis是什么

Redis = Remote Dictionary Server ,即远程字典服务。
是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。

2、redis特点

1、Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
2、Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储。
3、Redis支持数据的备份,即master-slave模式的数据备份。

3、redis优势

1、性能极高 : Redis能读的速度是110000次/s,写的速度是81000次/s 。
2、丰富的数据类型 : Redis支持 String, List, Hash, Set 及 Sorted Set 数据类型操作。
3、原子 : Redis的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,即原子性,通过MULTI和EXEC指令包起来。
4、丰富的特性 : Redis还支持发布/订阅, 通知, key 过期等特性。

二、redis的5大数据类型

1、String(字符串)

String类型是redis的最基础的数据结构,String类型的值最大能存储512M
使用场景:缓存、计数器

2、List(列表)

用来存储多个有序的元素,最多可以存储2^32-1个元素
使用场景:消息队列、分页展示功能

3、Hash(哈希)

Hash是一个健值对集合,是一个String类型的key与value的映射表,特别适合用于存储对象。
使用场景:常用于用户属性的存储、读取、修改

4、Set(集合)

集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)
使用场景:共同好友、标签、统计网站的独立IP

5、SortedSet(有序集合)

有序集合元素不能重复,给每个元素设置了一个分数
使用场景:排行榜、带权重的队列

三、redis持久化

1、什么是持久化

持久化(Persistence),即把数据(如内存中的对象)保存到可永久保存的存储设备中(如磁盘)。将Redis所有数据保存在内存中,对数据的更新将异步地保存到磁盘上。

2、RDB持久化

RDB = Redis DataBase,是Redis默认的持久化方式。按照一定的时间将内存的数据以快照的形式保存到硬盘中,对应产生的数据文件为dump.rdb。通过配置文件中的save参数来定义快照的周期。
优点
1、只有一个文件 dump.rdb,方便持久化。
2、容灾性好,一个文件可以保存到安全的磁盘。
3、性能最大化,fork 子进程来完成写操作,让主进程继续处理命令,所以是 IO 最大化。使用单独子进程来进行持久化,主进程不会进行任何 IO 操作,保证了 redis 的高性能
4、数据集大的时候,比 AOF 的启动效率高。
缺点
1、数据安全性低。RDB 是间隔一段时间进行持久化,如果持久化之间 redis 发生故障,会发生数据丢失。
三种触发持久化的方式:
a、save触发方式:该命令会阻塞当前Redis服务器,执行save命令期间,Redis不能处理其他命令,直到RDB过程完成为止。
b、bgsave触发方式:bgsave 命令执行一个异步操作,以RDB文件的方式保存所有数据的快照。Redis使用Linux系统的fock()生成一个子进程来将DB数据保存到磁盘,主进程继续提供服务以供客户端调用。
c、自动化触发:除了手动执行 save 和 bgsave 命令实现RDB持久化以外,Redis还提供了自动生成RDB的方式。可以通过配置文件对 Redis 进行设置, 让它在“ N 秒内数据集至少有 M 个改动”这一条件被满足时, 自动进行数据集保存操作。
bgsave命令执行流程:
在这里插入图片描述

3、AOF持久化

AOF持久化 = Append Only File持久化,则是将Redis执行的每次写命令记录到单独的日志文件中,当重启Redis会重新将持久化的日志中文件恢复数据。
当两种方式同时开启时,数据恢复Redis会优先选择AOF恢复。
优点:
1、数据安全,aof 持久化可以配置 appendfsync 属性,有 always,每进行一次 命令操作就记录到 aof 文件中一次。
2、通过 append 模式写文件,即使中途服务器宕机,可以通过 redis-check-aof 工具解决数据一致性问题。
3、AOF 机制的 rewrite 模式。AOF 文件没被 rewrite 之前(文件过大时会对命令 进行合并重写),可以删除其中的某些命令(比如误操作的 flushall))
缺点:
1、AOF 文件比 RDB 文件大,且恢复速度慢。
2、数据集大的时候,比 rdb 启动效率低。
持久化3种策略:
a、always:每次有新命令追加到 AOF 文件时就执行一次 fsync :非常慢,也非常安全。
b、everysec:每秒 fsync 一次:足够快(和使用 RDB 持久化差不多),并且在故障时只会丢失 1 秒钟的数据。推荐(并且也是默认)的措施为每秒 fsync 一次, 这种 fsync 策略可以兼顾速度和安全性。
c、no:从不 fsync :将数据交给操作系统来处理,由操作系统来决定什么时候同步数据。更快,也更不安全的选择。
AOF持久化流程:
在这里插入图片描述

4、RDB与AOF对比

对比项RDBAOF
体积
启动优先级
恢复速度
数据安全性丢数据策略决定

四、主从复制

1、主从复制作用

a、读写分离
b、数据容灾

2、复制原理

在这里插入图片描述

3、同步策略

主从刚刚连接的时候,进行全量同步;全量同步结束后,进行增量同步。当然,如果有需要,slave 在任何时候都可以发起全量同步。redis策略是,无论如何,首先会尝试进行增量同步,如不成功,要求从机进行全量同步

五、哨兵与集群

1、redis哨兵

sentinel,中文名是哨兵。哨兵是 redis 集群机构中非常重要的一个组件,主要有以下功能:

  • 集群监控:负责监控 redis master 和 slave 进程是否正常工作。
  • 消息通知:如果某个 redis实例有故障,那么哨兵负责发送消息作为报警通知给管理员。
  • 故障转移:如果 master node 挂掉了,会自动转移到 slave node上。
  • 配置中心:如果故障转移发生了,通知 client 客户端新的 master 地址。

在这里插入图片描述

1、Sentinel的作用

a、Master 状态监测
b、如果Master 异常,则会进行Master-slave 转换,将其中一个Slave作为Master,将之前的Master作为Slave
c、Master-Slave切换后,master_redis.conf、slave_redis.conf和sentinel.conf的内容都会发生改变,即master_redis.conf中会多一行slaveof的配置,sentinel.conf的监控目标会随之调换

2、Sentinel的工作方式

a、每个Sentinel以每秒钟一次的频率向它所知的Master,Slave以及其他 Sentinel 实例发送一个 PING 命令。
b、如果一个实例(instance)距离最后一次有效回复 PING 命令的时间超过 down-after-milliseconds 选项所指定的值, 则这个实例会被 Sentinel 标记为主观下线。
c、如果一个Master被标记为主观下线,则正在监视这个Master的所有 Sentinel 要以每秒一次的频率确认Master的确进入了主观下线状态。
d、当有足够数量的 Sentinel(大于等于配置文件指定的值)在指定的时间范围内确认Master的确进入了主观下线状态, 则Master会被标记为客观下线 。
e、在一般情况下, 每个 Sentinel 会以每 10 秒一次的频率向它已知的所有Master,Slave发送 INFO 命令 。
f、当Master被 Sentinel 标记为客观下线时,Sentinel 向下线的 Master 的所有 Slave 发送 INFO 命令的频率会从 10 秒一次改为每秒一次 。
g、若没有足够数量的 Sentinel 同意 Master 已经下线, Master 的客观下线状态就会被移除。
若 Master 重新向 Sentinel 的 PING 命令返回有效回复, Master 的主观下线状态就会被移除。

2、redis集群

Redis Cluster是一种服务端Sharding技术,3.0版本开始正式提供。Redis Cluster并没有使用一致性hash,而是采用slot(槽)的概念,一共分成16384个槽。将请求发送到任意节点,接收到请求的节点会将查询请求发送到正确的节点上执行
为什么是16384(2^14)个?
在redis节点发送心跳包时需要把所有的槽放到这个心跳包里,以便让节点知道当前集群信息,16384=16k,在发送心跳包时使用char进行bitmap压缩后是2k(2 * 8 (8 bit) * 1024(1k) = 16K),也就是说使用2k的空间创建了16k的槽数。
虽然使用CRC16算法最多可以分配65535(2^16-1)个槽位,65535=65k,压缩后就是8k(8 * 8 (8 bit) * 1024(1k) =65K),也就是说需要需要8k的心跳包,作者认为这样做不太值得;并且一般情况下一个redis集群不会有超过1000个master节点,所以16k的槽位是个比较合适的选择。
方案说明:
a、通过哈希的方式,将数据分片,每个节点均分存储一定哈希槽(哈希值)区间的数据,默认分配了16384 个槽位
b、每份数据分片会存储在多个互为主从的多节点上
c、数据写入先写主节点,再同步到从节点(支持配置为阻塞同步)
d、同一分片多个节点间的数据不保持一致性
e、读取数据时,当客户端操作的key没有分配在该节点上时,redis会返回转向指令,指向正确的节点
f、扩容时时需要需要把旧节点的数据迁移一部分到新节点
在这里插入图片描述

六、常见问题

1、缓存雪崩

问题描述: 缓存同一时间大面积的失效,导致所有的请求都会落到数据库上,造成数据库短时间内承受大量请求而崩掉。
解决方案:
a、缓存数据过期时间随机:过期时间设置随机,防止同一时间大量数据过期现象发生。
b、热点数据不设置过期时间,主动刷新缓存:缓存设置成永不过期,在更新或删除 DB 中的数据时,也主动地把缓存中的数据更新或删除掉。
c、检查更新:缓存依然保持设置过期时间,每次 get 缓存的时候,都和数据的过期时间和当前时间进行一下对比,当间隔时间小于一个阈值的时候,主动更新缓存。
d、使用锁:通过互斥锁或者队列,控制读数据库和写缓存的线程数量。

2、缓存击穿

问题描述: 缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,读缓存没读到数据,造成数据库短时间内承受大量请求而崩掉。和缓存雪崩不同的是,缓存击穿指并发查同一条数据,缓存雪崩是缓存同一时间大面积失效。
解决方案:
1、设置热点数据永远不过期。
2、加互斥锁

3、缓存穿透

问题描述: 缓存和数据库中都没有的数据,导致所有的请求都落到数据库上,造成数据库短时间内承受大量请求而崩掉。
解决方案:
a、接口层增加逻辑校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截;
b、从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击
c、采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的 bitmap 中,一个一定不存在的数据会被这个 bitmap 拦截掉,从而避免了对底层存储系统的查询压力
布隆过滤器

4、数据一致性

数据强一致性方案: 读请求和写请求串行化,串到一个内存队列里去,这样就可以保证一定不会出现不一致的情况,串行化之后,就会导致系统的吞吐量会大幅度的降低.
还有一种方式就是可能会暂时产生数据不一致的情况,但是发生的几率特别小,就是先更新数据库,然后再删除缓存。

5、热key问题

问题描述: 突然有几十万的请求去访问redis上的某个特定key。那么,这样会造成流量过于集中,达到物理网卡上限,从而导致这台redis的服务器宕机。那接下来这个key的请求,就会直接怼到你的数据库上,导致你的服务不可用。
解决方案:
a、利用二级缓存:在你发现热key以后,把热key加载到系统的JVM中。针对这种热key请求,会直接从jvm中取,而不会走到redis层。
b、备份热key:不要让key走到同一台redis上,我们把这个key在多个redis上都存一份,当有热key请求进来的时候,我们就在有备份的redis上随机选取一台,进行访问取值,返回数据。

6、大key问题

问题描述: 存储本身的key值空间太大,或者hash,list,set等存储中value值过多。
主要包括:
a、单个简单的key存储的value很大
b、hash, set,zset,list 中存储过多的元素
c、一个集群存储了上亿的key
解决方案:

  • 单个简单的key存储的value很大
    a、对象需要每次都整存整取:可以尝试将对象分拆成几个key-value, 使用multiGet获取值,这样分拆的意义在于分拆单次操作的压力,将操作压力平摊到多个redis实例中,降低对单个redis的IO影响;
    b、该对象每次只需要存取部分数据:可以像第一种做法一样,分拆成几个key-value, 也可以将这个存储在一个hash中,每个field代表一个具体的属性,使用hget,hmget来获取部分的value,使用hset,hmset来更新部分属性
  • hash, set,zset,list 中存储过多的元素
    可以对存储元素按一定规则进行分类,分散存储到多个redis实例中。
    对于一些榜单类的场景,用户一般只会访问前几百及后几百条数据,可以只缓存前几百条以及后几百条,即对用户经常访问的数据做缓存(正序倒序的前几页),而不是全部都做,对于获取中间的数据则可以直接从数据库获取
  • 一个集群存储了上亿的key
    如果key的个数过多会带来更多的内存空间占用,
    1、key本身的占用。
    2、集群模式中,服务端有时需要建立一些slot2key的映射关系,这其中的指针占用在key多的情况下也是浪费巨大空间。
    所以减少key的个数可以减少内存消耗,可以参考的方案是转Hash结构存储,即原先是直接使用Redis String 的结构存储,现在将多个key存储在一个Hash结构中
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值