Redis
1.Redis是什么
1.1认识Redis
redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。
Redis 是一个高性能的key-value数据库。redis的出现,很大程度补偿了memcached这类key/value存储的不足,在部分场合可以对关系数据库起到很好的补充作用。Redis 与其他 key - value 缓存产品有以下三个特点:
Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储。
Redis支持数据的备份,即master-slave模式的数据备份。
Redis支持主从同步。数据可以从主服务器向任意数量的从服务器上同步,从服务器可以是关联其他从服务器的主服务器。这使得Redis可执行单层树复制。存盘可以有意无意的对数据进行写操作。由于完全实现了发布/订阅机制,使得从数据库在任何地方同步树时,可订阅一个频道并接收主服务器完整的消息发布记录。同步对读取操作的可扩展性和数据冗余很有帮助。
1.2Redis优势
- 性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。
- 丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。
- 原子性 – Redis的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,即原子性,通过MULTI和EXEC指令包起来。
- 丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性。
1.3Redis与其他key-value存储有什么不同?
-
Redis有着更为复杂的数据结构并且提供对他们的原子性操作,这是一个不同于其他数据库的进化路径。Redis的数据类型都是基于基本数据结构的同时对程序员透明,无需进行额外的抽象。
-
Redis运行在内存中但是可以持久化到磁盘,所以在对不同数据集进行高速读写时需要权衡内存,因为数据量不能大于硬件内存。在内存数据库方面的另一个优点是,相比在磁盘上相同的复杂的数据结构,在内存中操作起来非常简单,这样Redis可以做很多内部复杂性很强的事情。同时,在磁盘格式方面他们是紧凑的以追加的方式产生的,因为他们并不需要进行随机访问。
2.Redis使用场景
- 热点数据的缓存,由于redis访问速度块、支持的数据类型比较丰富,所以redis很适合用来存储热点数据,另外结合expire,我们可以设置过期时间然后再进行缓存更新操作,可以适用于几乎所有项目。
- 计数器,可以对 String 进行自增自减运算,从而实现计数器功能,Redis 这种内存型数据库的读写性能非常高,很适合存储频繁读写的计数量。
- 限时业务的使用,因为Redis可以为键设置过期时间,到时间后redis会删除它。利用这一特性可以运用在限时的优惠活动信息、手机验证码等业务场景。
- 分布式锁,主要利用redis的setnx命令进行,setnx:"set if not exists"就是如果不存在则成功设置缓存同时返回1,否则返回0 ,因为我们服务器是集群的,定时任务可能在两台机器上都会运行,所以在定时任务中首先通过setnx设置一个lock,如果成功设置则执行,如果没有成功设置,则表明该定时任务已执行。当然结合具体业务,我们可以给这个lock加一个过期时间,比如说30分钟执行一次的定时任务,那么这个过期时间设置为小于30分钟的一个时间就可以,这个与定时任务的周期以及定时任务执行消耗时间相关。
当然我们可以将这个特性运用于其他需要分布式锁的场景中,结合过期时间主要是防止死锁的出现。 - 消息队列,由于redis有list push和list pop这样的命令,所以能够很方便的执行队列操作,不过最好使用 Kafka、RabbitMQ 等消息中间件。
- 还有很多…
3.Redis的五种数据类型
Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。
String
string 是 redis 最基本的类型,一个 key 对应一个 value。
string 类型是二进制安全的。意思是 redis 的 string 可以包含任何数据。比如jpg图片或者序列化的对象。
string 类型是 Redis 最基本的数据类型,string 类型的值最大能存储 512MB。
List
Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。
Set
Redis 的 Set 是 string 类型的无序集合。
集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。
sadd 命令,添加一个 string 元素到 key 对应的 set 集合中,成功返回 1,如果元素已经在集合中返回 0。
ZSet(sorted set:有序集合)
Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。
不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。
zset的成员是唯一的,但分数(score)却可以重复。
zadd 命令,添加元素到集合,元素在集合中存在则更新对应score。
Hash
Redis hash 是一个键值(key=>value)对集合。
Redis hash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象。
4.Redis持久化策略
4.1什么是持久化
由于Redis运行环境在内存中,如果redis服务器关闭,则内存数据将会丢失。
那如何保存内存数据呢?
解决方案: 可以定期将内存数据持久化到磁盘中.
持久化策略规则:
当redis正常运行时,定期的将数据保存到磁盘中,当redis服务器重启时,则根据配置文件中指定的持久化的方式,实现数据的恢复.(读取数据,之后恢复数据.)
4.2RDB(快照)模式
4.2.1RDB模式特点说明
- RDB模式是Redis默认的策略,将某个时间点的所有数据都存放到硬盘上。
- RDB模式能够定期(时间间隔)持久化, 弊端:可能导致数据的丢失
- RDB模式记录的是内存数据的快照,持久化效率较高,快照只保留最新的记录
- RDB可以将快照复制到其它服务器从而创建具有相同数据的服务器副本。如果系统发生故障,将会丢失最后一次创建快照之后的数据。如果数据量很大,保存快照的时间会很长。
4.2.2RDB模式命令
1.save命令: 将内存数据持久化到磁盘中 主动的操作 会造成线程阻塞
2.bgsave命令: 将内存数据采用后台运行的方式,持久化到文件中. 不会造成阻塞.
3.默认的持久化的机制
save 900 1 如果在900秒内,执行了1次更新操作,则持久化一次
save 300 10 如果在300秒内,执行了10次更新操作,则持久化一次
save 60 10000 如果在60秒内,执行了10000次更新操作,则持久化一次
4.2.3创建快照的办法
- BGSAVE命令 :客户端向Redis发送 BGSAVE命令 来创建一个快照。对于支持BGSAVE命令的平台来说(基本上所有平台支持,除了Windows平台),Redis会调用fork来创建一个子进程,然后子进程负责将快照写入硬盘,而父进程则继续处理命令请求。
- SAVE命令 :客户端还可以向Redis发送 SAVE命令 来创建一个快照,接到SAVE命令的Redis服务器在快照创建完毕之前不会再响应任何其他命令。SAVE命令不常用,我们通常只会在没有足够内存去执行BGSAVE命令的情况下,又或者即使等待持久化操作执行完毕也无所谓的情况下,才会使用这个命令。
- save选项 :如果用户设置了save选项(一般会默认设置),比如 save 60 10000,那么从Redis最近一次创建快照之后开始算起,当“60秒之内有10000次写入”这个条件被满足时,Redis就会自动触发BGSAVE命令。
- SHUTDOWN命令 :当Redis通过SHUTDOWN命令接收到关闭服务器的请求时,或者接收到标准TERM信号时,会执行一个SAVE命令,阻塞所有客户端,不再执行客户端发送的任何命令,并在SAVE命令执行完毕之后关闭服务器。
- 一个Redis服务器连接到另一个Redis服务器:当一个Redis服务器连接到另一个Redis服务器,并向对方发送SYNC命令来开始一次复制操作的时候,如果主服务器目前没有执行BGSAVE操作,或者主服务器并非刚刚执行完BGSAVE操作,那么主服务器就会执行BGSAVE命令
4.3AOF模式
4.3.1AOF模式特点
1).AOF模式默认条件下是关闭的.需要手动开启
2).AOF模式记录的是用户的操作过程,所以可以实现实时持久化操作
3).AOF模式由于记录的是实时的操作过程,所以持久化文件较大.需要定期维护
4.3.2启动AOF模式
可以通过appendonly参数开启:
appendonly yes
选用AOF模式,可以防止由于flushALL命令导致的数据丢失的情况,此时需要找到aof文件之后,删除flushAll命令 之后重启redis,执行save命令即可恢复。
使用 AOF 持久化需要设置同步选项,从而确定写命令同步到磁盘文件上的时机。这是因为对文件进行写入并不会马上将内容同步到磁盘上,而是先存储到缓冲区,然后由操作系统决定什么时候同步到磁盘。在Redis的配置文件中存在三种同步方式
选项 | 同步频率 |
---|---|
always | 每个写命令都同步,这样会严重降低Redis的速度 |
everysec | 每秒同步一次 |
no | 让操作系统来决定何时同步 |
总结来说,always 虽然说可以实现将数据丢失减到最少,但是这种方式由于需要对硬盘进行大量的写入,十分影响Redis的速度。这会明显降低固态硬盘的使用寿命。everysec 为了兼顾数据和写入性能,可以考虑使用 ,让Redis每秒同步一次AOF文件,Redis性能几乎没受到任何影响。而且这样即使出现系统崩溃,用户最多只会丢失一秒之内产生的数据。no 模式由于不确定性,会使Redis丢失不定量的数据,不建议使用。
4.4两种持久化方法的选择
1.当内存数据允许少量丢失时,采用RDB模式 (快)
2.当内存数据不允许丢失时,采用AOF模式(定期维护持久化文件)
3.一般在工作中采用 RDB+AOF模式共同作用,保证数据的有效性.
5.Redis过期键删除策略
首先,Redis在set key的时候,可以给expire参数一个时间,用于指定键的生命时长(即这个key可以存话的时间),但是对于散列表这种容器,只能为整个键设置过期时间(整个散列表),而不能为键里面的单个元素设置过期时间。
由于Redis可以设置key的过期时间,那么对于一些过期了的数据该怎么处理呢?Redis自身有三种删除策略:
- 立即删除
在设置键的过期时间时,创建一个回调事件,当过期时间达到时,由时间处理器自动执行键的删除操作。 - 惰性删除
键过期了就过期了,暂时先不管,每次按key取值时,先检查此key是否已经过期,如果过期了就删除它,并返回nil,如果没过期,就返回键值。 - 定时删除
每隔一段时间,进行一次检查,删除里面的过期键。
总体来说,立即删除由于能够保证过期键值会在过期后马上被删除,其所占用的内存也会随之释放。但是立即删除对cpu是最不友好的。因为删除操作会占用cpu的时间,就会给cpu造成额外的压力。而惰性删除是指某个键值过期后,此键值不会马上被删除,而是等到下次被使用的时候,才会被检查到过期,此时才能得到删除。所以惰性删除很明显浪费内存,这对于性能非常依赖于内存大小的redis来说,是比较致命的。
而定时删除相当于是一个折中的策略,既能避免立即删除带来的CPU压力,也能减少惰性删除导致的内存消耗。
而在实际的Redis中,会同时使用惰性删除和定时删除,两者配合使用。
6.Redis数据淘汰(内存优化)策略
由于redis在内存中保存数据.如果一直存储,则内存数据必然溢出,所以需要定期维护内存数据的大小,需要删除旧的不用的数据,保留新的常用的数据。
在了解数据淘汰策略之前,先明白几个相关的算法:
6.1LRU算法
LRU是Least Recently Used的缩写,即最近最少使用,是一种常用的页面置换算法,选择最近最久未使用的页面予以淘汰。该算法赋予每个页面一个访问字段,用来记录一个页面自上次被访问以来所经历的时间 t,当须淘汰一个页面时,选择现有页面中其 t 值最大的,即最近最少使用的页面予以淘汰。
计算维度: 时间T
注意事项: LRU算法是迄今为止内存中最好用的数据置换算法。
6.2LFU算法
LFU(least frequently used (LFU) page-replacement algorithm)。即最不经常使用页置换算法,要求在页置换时置换引用计数最小的页,因为经常使用的页应该有一个较大的引用次数。但是有些页在开始时使用次数很多,但以后就不再使用,这类页将会长时间留在内存中,因此可以将引用计数寄存器定时右移一位,形成指数衰减的平均使用次数。
维度: 使用次数
6.3随机算法
随机算法,就是从过期键中随即删除。
6.4TTL算法
根据剩余的存活时间,将马上要超时的数据提前删除。
6.5内存优化策略
1.volatile-lru 在设定了超时时间的数据中,采用lru算法
2.allkeys-lru 在所有的数据中,采用lru算法
3.volatile-lfu 在设定了超时时间的数据中,采用lfu算法 #Redis4.0引入
4.allkeys-lfu 所有数据采用lfu算法 #Redis4.0引入
5.volatile-random 设置超时时间数据的随机算法
6.allkeys-random 所有数据的随机
7.volatile-ttl 将设定了超时时间的数据,提前删除.
8.noeviction 默认规则 如果设定noeviction 则不删除数据,直接报错返回.
7.Redis中缓存相关
7.1缓存雪崩
-
缓存雪崩是由于缓存中的数据大面积失效,导致大量的用户直接访问数据库,如果访问量巨大的话,这种情况可能导致数据库服务器宕机。
-
解决方案:
1.采用多级缓存
2.缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生
3.禁止执行 flushAll等敏感操作
4.给每一个缓存数据增加相应的缓存标记,记录缓存的是否失效,如果缓存标记失效,则更新数据缓存。
5.一般并发量不是特别多的时候,使用最多的解决方案是加锁排队。
7.2缓存穿透
- 用户频繁访问数据库中不存在的数据,可能出现缓存穿透的现象,如果该操作是高并发操作,则可能直接威胁数据库服务器。甚至有宕机的风险。
- 解决方法:
1.采用IP限流的方式 降低用户访问服务器次数. IP动态代理(1分钟变一次)
2.微服务的处理方式: 利用断路器返回执行的业务数据即可不执行数据库操作 从而保护了数据库.
3.微服务处理方式: API网关设计. 不允许做非法操作
4.也可以使用布隆过滤器优化缓存穿透带来的问题
7.3缓存击穿
- redis中某个热点数据由于超时/删除等操作造成数据失效.同时用户高并发访问该数据,则可能导致数据库宕机,该操作称之为缓存击穿
- 解决方法:
1.可以采用多级缓存的设计. 同时数据的超时时间采用随机数的方式
2.可以加互斥锁(不好)
7.4缓存预热
如果在系统上线初期,数据库中的数据还并没有更新到Redis缓存中,这会导致前期用户访问时效率变慢,缓存预热就是提前将相关的缓存数据(尤其是可能的热点数据)提前加载到Redis缓存中,省去了用户使用过程中,先判断缓存再访问数据库,然后更新缓存的过程,提高了效率,也提升了用户体验。
7.5缓存降级
当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然需要保证服务还是可用的,即使是有损服务。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级。
缓存降级的最终目的是保证核心服务可用,即使是有损的。而且有些服务是无法降级的(如加入购物车、结算)。
在进行降级之前要对系统进行梳理,看看系统是不是可以丢卒保帅;从而梳理出哪些必须誓死保护,哪些可降级;比如可以参考日志级别设置预案:
-
一般:比如有些服务偶尔因为网络抖动或者服务正在上线而超时,可以自动降级;
-
警告:有些服务在一段时间内成功率有波动(如在95~100%之间),可以自动降级或人工降级,并发送告警;
-
错误:比如可用率低于90%,或者数据库连接池被打爆了,或者访问量突然猛增到系统能承受的最大阀值,此时可以根据情况自动降级或者人工降级;
-
严重错误:比如因为特殊原因数据错误了,此时需要紧急人工降级。
服务降级的目的,是为了防止Redis服务故障,导致数据库跟着一起发生雪崩问题。因此,对于不重要的缓存数据,可以采取服务降级策略,例如一个比较常见的做法就是,Redis出现问题,不去数据库查询,而是直接返回默认值给用户。
8.Redis分片机制
说明
如果需要Redis存储海量的内存数据,使用单台redis不能满足用户的需求,所以可以采用Redis分片机制实现数据存储。
但是有一个注意事项:如果有多台redis,则其中的数据都是不一样的…
虽说是实现了Redis分片,但是还有一个问题,就是无法实现Redis的高可用,那么就可能会出先一些不好的问题,如果redis分片中有一个节点宕机,则直接影响所有节点的运行,这种情况肯定是不希望出现的,如果想要避免这个问题,可以使用哨兵机制来实现Redis分片的高可用。
9.Redis哨兵机制
采用Redis哨兵机制可以实现Redis分片高可用
9.1哨兵机制工作原理
1)当哨兵启动时,会链接redis主节点,同时获取所有节点的状态信息
2)当哨兵连续3次通过心跳检测机制(PING-PONG),如果发现主机宕机,则开始选举
3)哨兵内部通过随机算法筛选一台从机当选新的主机.其他的节点应该当新主机的从
9.2哨兵机制的几个配置了以了解一下
- 关闭保护模式
- 开启后台运行
- 配置投票机制
- 修改投票时间:主机宕机3秒之后开始选举
10.Redis集群搭建
虽然哨兵机制可以实现分片的高可用,但是会发现,那这样的话,哨兵死掉可咋办,那样redis分片不还是暴露了么,那redis分片的缺点又摆出来了,想要解决,就可能还得实现哨兵的高可用,这下麻烦了,这叫什么优化,步骤多了,维护复杂了,自然就不好了。
10.1分片/哨兵有哪些缺点
1.分片缺点: 分片的主要的功能是实现内存的扩容的. 但是没有高可用的效果.
2.哨兵缺点: 数据没有扩容,哨兵本身没有高可用机制
我们想要达到的一种状态其实是,既可以实现内存数据的扩容,同时实现高可用机制(不用第三方)。
10.2Redis集群搭建
1.首先关闭所有的Redis服务器
2.检查配置文件编辑是否正确.
3.删除多余的配置文件
4.重启redis服务器
5.搭建redis集群(可以参考这篇文章)
10.3知识点小结
1.redis集群中一共可以存储16384个KEY?
答: 16384只是槽位的数量,只负责规划这个数据归谁管理的问题,至于数据如何存储,是由redis内存决定的,就比如这种情况
hash(key1) = 3000,
hash(key2) = 3000;
2.Redis集群中最多可以有多少台主机? 16384台主机.
3.Redis中如果遇到多线程操作,是否有线程安全性问题 ? 没有
因为redis服务器是单进程单线程操作. 每次操作都是由一个线程执行,所以不会有线程安全性问题.
4.Redis如何实现内存数据的优化? LRU/LFU/随机算法/TTL