Redis GO!
- redis
- 选择Redis
- redis相比memcached优势
- redis数据类型和应用场景有哪些
- Redis 数据类型底层实现
- Redis使用场景
- Redis配置
- redis是单线程执行速度
- IO多路复用机制
- redis可能出现的问题
- redis的持久化方式
- redis数据过期回收策略与内存淘汰机制
- redis对事务支持:
- Redis扩容
- MySQL里有2000w数据,redis中只存20w的数据,如何保证redis中的数据都是热点数据
- Redis主要消耗什么物理资源
- Redis的内存用完了会发生什么
- Redis如何做内存优化
- Redis最适合的场景
- 保证缓存与数据库的双写一致性
- Redis热Key问题
- redis的主从复制机制:
- 哨兵Sentinel
redis
- redis(Remote Dictionary Server远程字典服务)是一款高性能的(key/value)分布式内存数据库,基于内存运行并支持持久化的NoSQL数据库
- 因为数据都在内存中,所以运行速度快
- redis支持丰富的数据类型并且支持事务,事务中的所有命令会被序列化、按顺序执行,在执行的过程中不会被其他客户端发送来的命令打断
选择Redis
- 高性能,用户第一次请求数据库中数据因为从硬盘上读取而缓慢,将访问数据存在数缓存中,下一次访问直接从缓存中获取,操作缓存就是直接操作内存,速度快,如果数据库中的对应数据改变只需同步缓存中相应的数据
- 高并发,直接操作缓存能够承受的请求是远大于直接访问数据库,把数据库中的部分数据转移到缓存中去则用户的一部分请求会直接到缓存这里而不用经过数据库,大大提高系统并发性能
- 高速,具有丰富的数据类型且支持事务,以及设置过期时间等丰富的特性
redis相比memcached优势
-
memcached所有的值均是简单的字符串,redis作为其替代者,支持更为丰富的数据类型
-
value大小不同,redis最大可以达到512MB,而memcache只有1MB
-
redis支持数据的备份,即master-slave模式的数据备份
-
redis的速度比memcached快很多,并且redis支持数据持久化
-
使用底层模型不同,它们之间底层实现方式以及与客户端之间通信的应用协议不一样。redis自己构建了VM 管理机制。
redis数据类型和应用场景有哪些
redis支持五种数据类型作为其Value,redis的Key都是字符串类型
string
- redis 中字符串 value 最大可为512M
- 可以用来做一些计数功能的缓存(也是实际工作中最常见的)。
list
- 简单的字符串列表,按照插入顺序排序,可以添加一个元素到列表的头部(左边)或者尾部(右边),其底层为链表
- 可以实现一个简单消息队列功能,做基于redis的分页功能等
hash
- 键值对集合,是一个字符串类型的 Key和 Value 的映射表,也就是说其存储的Value是一个键值对(Key- Value)
- 可以用来存放一些具有特定结构的信息
set
- 是一个字符串类型的无序集合,可以用来进行全局去重等
sorted set
是一个字符串类型的有序集合,给每一个元素一个固定的分数score来保持顺序。可以用来做排行榜应用或者进行范围查找等
Redis 数据类型底层实现
-
字典:是一个散列表结构,使用拉链法解决哈希冲突
-
跳表:基于多指针有序链表实现的,可以看成多个有序链表,是zset底层实现之一
- 插入速度非常快速,因为不需要进行旋转等操作来维护平衡性
- 更容易实现
- 支持无锁操作
Redis使用场景
-
计数器
可以对 String 进行自增自减运算,从而实现计数器功能,Redis 作为内存型数据库的读写性能非常高,很适合存储频繁读写的计数量 -
缓存
将热点数据放到内存中,设置内存的最大使用量以及淘汰策略来保证缓存的命中率 -
查找表
例如 DNS 记录就很适合使用 Redis 进行存储
查找表和缓存类似利用了Redis快速查找特性,但是查找表不能失效缓存可以失效,缓存不可以作为可靠的数据来源 -
会话缓存
可以使用 Redis 来统一存储多台应用服务器的会话信息,应用服务器不再存储用户会话信息(也就不再有状态),一个用户可以请求任意一个应用服务器实现高可用和可伸缩性 -
消息队列
List是一个双向链表,可以lpush和rpop写入和读取消息,但最好使用消息中间件 -
其他
Set可以实现交集并集操作,从而实现共同好友等功能;
Zset可以具有有序性操作从而实现排行榜等功能
Redis配置
下载并且解压redis之后,在解压目录下有个配置文件 redis.windows.conf,在配置文件中对redis进行了分模块配置
- NETWORK:该模块可以配置一些redis服务器地址,端口以及超时时间等
- GENERAL:该模块可以对日志文件的路径和日志级别等进行配置
- SNAPSHOTTING:redis持久化配置信息等
- REPLICATION:redis集群配置等信息
- MEMORY MANAGEMENT:内存管理,包括数据过期删除策略信息的设置
- APPEND ONLY MODE:日志持久化方式信息设置
redis是单线程执行速度
单线程
- redis是单线程的,redis的单线程是指网络请求模块使用了一个线程,所以不需考虑并发安全性
- 但是对于需要依赖多个操作的复合操作来说,还是需要锁,而且有可能是分布式锁。
那么单线程的redis为什么执行速度如此之快?
执行速度
- 基于内存实现,完全内存计算
- 单线程操作,避免了线程上下文切换操作
- 多路I/O复用的线程模型,实现了一个线程监控多个IO流,及时响应请求
- redis对外部的依赖比较少,属于轻量级内存数据库
IO多路复用机制
- 通过一种机制一个进程可以监视多个描述符,一旦某个描述符读就绪或者写就绪,其能够通知应用程序进行相应的读写操作
- 多路I/O复用机制与多进程和多线程技术相比系统开销小,系统不必创建进程/线程,也不必维护这些进程/线程,从而大大减小了系统的开销
支持I/O多路复用的系统调用有select,pselect,poll,epol
select
- 会修改传入的参数数组,这个对于一个需要调用很多次的函数,是非常不友好的。
- 有最大监听连接数1024个的限制
- 如果任何一个sock(I/O stream)出现了数据,select没有返回具体是哪个返回了数据,需要采用轮询的方式去遍历获取
- 线程不安全(当你在一个线程中已经监听该socket,另一个线程想要将该socket关闭,则结果会不可预知)
“If a file descriptor being monitored by select() is closed in another thread, the result is unspecified”
poll函数:
- 去掉了1024的限制(链表实现)
- 不再修改传入的参数数组
- 线程不安全
epoll函数:
- epoll 不仅返回socket组里面数据,还可以确定 具体哪个socket有数据(红黑树)
- 线程安全
redis可能出现的问题
缓存雪崩
-
缓存同一时间大面积的失效,请求直接打到数据库上,导致数据库连接异常。
-
解决办法
- 可以给缓存设置不同的缓存时间
- 更新数据使用互斥锁
- 通过双缓存避免缓存雪崩。
缓存击穿
- 举例:redis中热点数据的高并发访问如果redis中数据过期了,会造成缓存击穿的现象,请求都打到了数据库上。
解决办法:
- 使用互斥锁,只让一个请求去load DB,成功之后重新写缓存,其余请求没有获取到互斥锁,可以尝试重新获取缓存中的数据
- 设置热点数据永不过期
缓存穿透:
- 举例:故意的去请求缓存中不存在的数据,导致请求都打到了数据库上,导致数据库异常
解决办法
- 可以使用互斥锁
- 无论是否取到结果都将结果存入缓存,
- 还可以使用有效的机制比如布隆过滤器来拦截不合法的key值等
数据库和缓存的双写一致性问题
-
在高并发请求下很容易导致数据不一致的问题,如果你的业务需要保证数据的强一致性,那么建议不要使用缓存
-
在数据库中和缓存数据的删除或者写入过程中,如果有失败的情况,会导致数据的不一致
-
解决办法:
- 双删延时 可以先删除缓存数据,然后再更新数据库数据,最后再隔固定的时间再次删除缓存。更新数据库产生的binlog订阅(使用canal)。
- 将有变化的key记录下来,并且尝试去不断的去删除缓存(如果上次删除缓存失败)
redis的持久化方式
RDB(快照方式 snapshotting)(全量持久化)
将当前内存中的数据集快照写入磁盘,实现数据的持久化,恢复时可以将快照重新载入内存。
触发方式
- 自动触发:在配置文件中,可以配置执行了多少次save就自动触发自动持久化。
- 手动触发:通过bgsave命令
快照恢复
- == 将备份文件 (dump.rdb) 移动到 redis 安装目录并启动服务,redis会自动加载快照文件数据到内存==(因此可以创建具有相同数据的服务器夫v恶霸)
- redis 服务器在载入 RDB 文件期间,会一直处于阻塞状态,直到载入工作完成为止。
优缺点
- RDB持久化方式存在数据的丢失,因为其没有实现实时持久化(系统故障会丢失最后一次创建快照后的所有数据)
- 数据量很大时保存快照时间很长
- 在恢复大数据集时候,RDB方式相对于AOF要快
AOF(append-only-file)(增量持久化)
- 在 redis配置文件的 APPEND ONLY MODE 中,可以设置AOF持久化
- 通过记录redis服务器所执行的写命令来记录数据库状态
- 恢复时可以将AOF文件载入内存,并且可以通过redis-check-aof --fix 进行修复AOF文件
AOF日志重写
- AOF文件会随着服务器运行的时间越来越大,可以通过AOF重写去除冗余命令来控制AOF文件大小
- AOF重写会首先读取数据库中现有的键值对状态,然后根据类型使用一条命令来替代前面对键值对操作的多条命令
- 使用命令== bgrewriteaof== 来实现AOF重写
优缺点分析
- AOF文件可以做到秒级持久化,使用追加写的方式来写入,可读性强并且可以使用命令进行文件修复
- 相比于RDB文件,同样数据下AOF文件体积要大,在redis负载较高时,秒级更新AOF文件会影响性能
持久化策略选择:
- AOF更安全,可将数据及时同步到文件中,但需要较多的磁盘IO,AOF文件尺寸较大,文件内容恢复相对较慢也更加完整
- RDB持久化安全性较差,文件尺寸较小并且恢复速度较快,是正常时期数据备份及 master-slave数据同步的最佳手段
redis数据过期回收策略与内存淘汰机制
过期回收策略
redis中的数据过期回收策略使用了定期删除和惰性删除相结合的方式。
定期删除
redis会每隔一定的时间去抽查一定量的数据判断其是否过期,过期则进行删除。
惰性删除
在获取一个key的时候,redis会检查这个key是否已经过期,若过期,则会进行删除操作。
内存淘汰机制
-redis需要设置内存最大使用量,超出时会施行数据淘汰策略,实际上是抽取一小部分选择淘汰的key
-
在配置文件中,我们可以对内存淘汰机制进行配置。当内存使用达到最大值时,redis可以使用的清除策略如下:
- volatile-lru:利用LRU算法移除设置过过期时间的key
- allkeys-lru:利用LRU算法移除任何key
- volatile-random:移除设置过过期时间的随机key
- allkeys-random:移除随机key
- volatile-ttl:移除即将过期的key(minor TTL)
- noeviction :不移除任何key,只是返回一个写错误 ,默认选项
-
为了提高缓存命中率,需要保证缓存数据都是热点数据,可以将内存最大使用量设置为热点数据占用的内存量,然后启用allkeys-lru将最近最少使用的数据淘汰
Redis key的过期时间和永久有效设置
EXPIRE和PERSIST命令
redis对事务支持:
redis对事务的支持
- 原子性:事务中的命令要么全部被执行,要么全部都不执行
- 隔离性:redis 是单进程的程序,保证在执行事务时,所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断
redis操作事务的相关命令
Redis 最简单的事务实现方式是使用 MULTI 和 EXEC 命令将事务操作包围起来
- MULTI:标记一个事务块的开始。
- EXEC:执行所有事务块内的命令。
- DISCARD:取消事务,放弃执行事务块内的所有命令。
- UNWATCH:取消 WATCH 命令对所有 key 的监视。
- WATCH key [key …]:监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。
redis的事务不支持回滚操作
redis以 MULTI 开始一个事务,然后将多个命令入队到事务中,最后由 EXEC 命令触发事务, 一并执行事务中的所有命令
只有当被调用的redis命令有语法错误时,这条命令才会执行失败,或者对某个键执行不符合其数据类型的操作,但是应该在将命令入队列的时候就应该并且能够发现这些问题,所以redis的事务不支持进行回滚操作
Redis扩容
- 增加Redis服务器的数量,在客户端对存储的key进行hash运算,存入不同的Redis服务器中,读取时进行相同hash运算,找到对应的Redis服务器
- 搭建Redis集群
- 读写分离,主服务器写,从服务器读
MySQL里有2000w数据,redis中只存20w的数据,如何保证redis中的数据都是热点数据
Redis内存数据集大小上升到一定大小的时候,就会施行数据淘汰策略(六种)
Redis主要消耗什么物理资源
redis的数据都是存储在内存当中。内存数据库相比一般的关系型数据库,读取速度要更快,但是消耗的内存资源会更多
Redis的内存用完了会发生什么
- 达到设置的上限,Redis的写命令会返回错误信息(但是读命令还可以正常返回)
- 可以将Redis当缓存来使用配置淘汰机制,当Redis达到内存上限时会冲刷掉旧的内容
Redis如何做内存优化
尽可能使用散列表
Redis最适合的场景
- 会话缓存(Session Cache)
如购物车信息 - 全页缓存(FPC)
- 队列
- 排行榜/计数器
- 发布/订阅
保证缓存与数据库的双写一致性
使用缓存,就可能会涉及到缓存与数据库双存储双写,双写就一定会有数据一致性的问题
严格要求 “缓存+数据库” 必须保持一致
读请求和写请求串行化,串到一个内存队列(会导致系统的吞吐量大幅度降低,用比正常情况下多几倍的机器去支撑线上的一个请求)
Cache Aside Pattern
-
读的时候,先读缓存,缓存没有的话,就读数据库,然后取出数据后放入缓存,同时返回响应
-
更新的时候,先更新数据库,然后再删除缓存
-之所以选择删除缓存是因为1复杂缓存场景下缓存不单单是数据库中直接取出来的值,可能经过运算 2.更新缓存的代价比较高
使用集群同时扩展写负载和读负载等
Redis热Key问题
大量并发请求访问redis上的某个特定key。流量过于集中,达到物理网卡上限,从而导致这台redis的服务器宕机
热Key发现
-
凭借业务经验,预估热key
-
用redis自带命令
- monitor命令,实时抓取出redis服务器接收到的命令,编写代码统计出热key
- hotkeys参数
解决热Key
- 利用二级缓存
利用ehcache,或者一个HashMap都可以,发现热key以后,把热key加载到系统的JVM中,针对这种热key请求,会直接从jvm中取,而不会走到redis层
备份热key
把这个key,在多个redis上存储。接下来,有热key请求进来的时候,我们就在有备份的redis上随机选取一台,进行访问取值,返回数据
redis的主从复制机制:
-
当项目比较大的时候,我们可以使用主从架构Master/Slave机制,Master 以写为主,Slave 以读为主,Master 主节点更新后根据配置,自动同步到从机Slave 节点。
-
主从复制的原理包括旧版同步和命令传播,主从复制的代价就是系统复制较重的时候会导致主从延迟,并且根据CAP理论,无法同时保证服务可用性和数据一致性
-
通过使用 slaveof host port 命令来让一个服务器成为另一个服务器的从服务器。
哨兵Sentinel
Sentinel(哨兵)可以监听集群中的服务器,并在主服务器进入下线状态时,自动从从服务器中选举出新的主服务器