目录
1.关系型数据库和非关系型数据库的区别 ?
1. KV型NoSql(代表----Redis)
2. 搜索型NoSql(代表--ElasticSearch )
3. 文档型NoSql(代表----MongoDB)
4. Memcache 与 Redis 的区别都有哪些?
5. 数据库与NoSql及各种NoSql间的对比
2.Redis 是什么? 都有哪两种缓存? 为什么这么快? Redis 的优缺点?
1. Redis 是什么?
2. 本地/分布式 缓存:
3. Redis 为什么快:
4.Redis 和其key-value 相比, 有什么特殊的?
5. Resid 优缺点 :
3. Redis 中key-value中value 的5 种数据类型
1. 在项目中的应用场景
4. 数据的一致性问题、删除策略
1.如何让保证缓存数据的一致性问题?
2. Redis 中查询的数据怎么保证数据查询的一致性?
3. 为什么是删除缓存,而不是更新缓存?
4. 数据不一致 * 简单情况 : 为什么先删缓存, 再更新数据库?
5. 数据不一致 * 复杂情况: 数据变更--> 删了缓存-->修改数据库之前有了请求...
6. 单节点下: ✅先删缓存 后更新库 与 ❌先更新库 后删缓存 对比
7. key 过期的删除策略
7. Redis key的过期时间和永久有效分别怎么设置?
8. 有大量的 key 需要设置同一时间过期,需要注意 什么?
5. 内存相关
1. MySQL里有2000w数据,redis中只存20w的数据,如何保证redis中的数据都是热点数据
2. Redis的[ 内存淘汰策略/回收策略 ]有哪些? ---- 6 种..
3. Redis主要消耗什么物理资源?
3.Redis集群最大节点个数是多少?
3.一个字符串类型的值能存储最大容量是多少?
4.为什么 Redis 需要把所有数据放到内存中?
5. Redis的内存用完了会发生什么?
6. Redis如何做内存优化?
6. Redis 单线程快的原因?
1. 单线程机制
7. Redis持久
1. Redis 能保证数据不丢失吗?
2. Redis 怎么防止数据丢失?
3. RDB快照模式 : Redis DataBase.
4. AOF 日志模式 : Append Only File
5. 两种机制对比:
6. 如何选择合适的持久化方式
7. Redis持久化数据和缓存怎么做扩容?
8. 在你的项目中 Redis 中都存什么信息? 所有数据都可以存在Redis中吗?
6. Redis 事务
1. Redis 事务的本质 :
2. Redis事务的三个阶段
3. 事务管理(ACID)概述
7. Redis 集群
1. Redis 集群的原理
2. 哨兵模式
2.说说 Redis 哈希槽的概念?
3. Redis 集群的 主从复制--(异步复制)
Redis 回收进程如何工作的?
8. 缓存异常 :
1. 缓存击穿 :
2. 缓存穿透 :
3 缓存雪崩 :
4. 缓存预热
5. 缓存降级
6. 热点数据和冷数据
7. 缓存热点key
n. Redis 常见性能问题和解决方案:
分布式 、 锁部分、集群方案、主从复制
无脑打开, 准没错 : Redis 常见面试题(2020最新版) - Java知音号 - 博客园
1.关系型数据库和非关系型数据库的区别 ?
source : Sql Or NoSql,看完这一篇你就懂了 - 五月的仓颉 - 博客园
source2: 关系型数据库与非关系型数据库区别 - 灬菜鸟灬 - 博客园
关系型数据库: 适合处理一般量级的数据. (Mysql 和 Oracle)
- 表&表 ; 表&字段 ; 数据&数据 之间存在着关系.
- 优点: 数据之间有关系, 增删改查方便 ; 且关系型数据库是有事务操作的(ACID), 保证了数据的完整性和一致性.
- 缺点: 数据量大时操作效率低, 增删改查慢, 维护困难.
非关系型数据库: 处理海量数据, 出发点是为了代替关系型数据库 (Redis 和 MangoDB)
- 优点 : 易扩展, 数据间没有联系; 对于海量数据的处理比较有优势; 可用性高
- 缺点 : 数据间没有关系, 单独存在; 且没有事务关系, 无法保证数据的完整性和安全性. 且不具备分词功能.
Nosql : 非关系型数据库 Not only sql
Nosql 数据库四大分类 :
- 键值对存储 : (key-value)形式, Redis 键值存储, 优 : 适合结构查询; 缺 :存储缺少结构化
- 列存储 : (Hbase). 优 :快速查询,扩展性强 ; 缺 : 功能局限
- 文档数据存储 : MongoDB. 优 : 要求不严格, 缺 :查询效率不高
- 图形数据库存储 : 应用于社交网络, 优 : 有图型结构相关算法, 缺 : 需要整个图计算结果,不容易做分布式集群方案
1. KV型NoSql(代表----Redis)
以键值对形式存储的非关系型数据库。Redis、MemCache是其中的代表,Redis又是KV型NoSql中应用最广泛的NoSql,KV型数据库以Redis为例,最大的优点我总结下来就两点:
- 数据基于内存,读写效率高
- KV型数据,时间复杂度为O(1),查询速度快
因此,KV型NoSql最大的优点就是高性能,利用Redis自带的BenchMark做基准测试,TPS可达到10万的级别,性能非常强劲。同样的Redis也有所有KV型NoSql都有的比较明显的缺点:
- 只能根据K查V,无法根据V查K
- 查询方式单一,只有KV的方式,不支持条件查询,多条件查询唯一的做法就是数据冗余,但这会极大的浪费存储空间
- 内存是有限的,无法支持海量数据存储
- 同样的,由于KV型NoSql的存储是基于内存的,会有丢失数据的风险
综上所述,KV型NoSql最合适的场景就是缓存的场景:
- 读远多于写
- 读取能力强
- 没有持久化的需求,可以容忍数据丢失,反正丢了再查询一把写入就是了
例如根据用户id查询用户信息,每次根据用户id去缓存中查询一把,查到数据直接返回,查不到去关系型数据库里面根据id查询一把数据写到缓存中去。
2. 搜索型NoSql(代表--ElasticSearch )
它的优点为:
- 支持分词场景、全文搜索,这是区别于关系型数据库最大特点
- 支持条件查询,支持聚合操作,类似关系型数据库的Group By,但是功能更加强大,适合做数据分析
- 数据写文件无丢失风险,在集群环境下可以方便横向扩展,可承载PB级别的数据
- 高可用,自动发现新的或者失败的节点,重组和重新平衡数据,确保数据是安全和可访问的
同样,ElasticSearch也有比较明显的缺点:
性能全靠内存来顶,也是使用的时候最需要注意的点,非常吃硬件资源、吃内存,大数据量下64G + SSD基本是标配,算得上是数据库中的爱马仕了。至于ElasticSearch内存用在什么地方,大概有如下这些:
- Indexing Buffer----ElasticSearch基于Luence,Lucene的倒排索引是先在内存里生成,然后定期以Segment File的方式刷磁盘的,每个Segment File实际就是一个完整的倒排索引
- Segment Memory----倒排索引前面说过是基于关键字的,Lucene在4.0后会将所有关键字以FST这种数据结构的方式将所有关键字在启动的时候全量加载到内存,加快查询速度,官方建议至少留系统一半内存给Lucene
- 各类缓存----Filter Cache、Field Cache、Indexing Cache等,用于提升查询分析性能,例如Filter Cache用于缓存使用过的Filter的结果集
- Cluter State Buffer----ElasticSearch被设计为每个Node都可以响应用户请求,因此每个Node的内存中都包含有一份集群状态的拷贝,一个规模很大的集群这个状态信息可能会非常大
- 读写之间有延迟,写入的数据差不多1s样子会被读取到,这也正常,写入的时候自动加入这么多索引肯定影响性能
- 数据结构灵活性不高,ElasticSearch这个东西,字段一旦建立就没法修改类型了,假如建立的数据表某个字段没有加全文索引,想加上,那么只能把整个表删了再重建
因此,搜索型NoSql最适用的场景就是有条件搜索尤其是全文搜索的场景,作为关系型数据库的一种替代方案。
3. 文档型NoSql(代表----MongoDB)
因此文档型NoSql的出现是解决关系型数据库表结构扩展不方便的问题的。
因此,对于MongDB,我们只要理解成一个Free-Schema的关系型数据库就完事了,它的优缺点比较一目了然,优点:
- 没有预定义的字段,扩展字段容易
- 相较于关系型数据库,读写性能优越,命中二级索引的查询不会比关系型数据库慢,对于非索引字段的查询则是全面胜出
缺点在于:
- 不支持事务操作,虽然Mongodb4.0之后宣称支持事务,但是效果待观测
- 多表之间的关联查询不支持(虽然有嵌入文档的方式),join查询还是需要多次操作
- 空间占用较大,这个是MongDB的设计问题,空间预分配机制 + 删除数据后空间不释放,只有用db.repairDatabase()去修复才能释放
- 目前没发现MongoDB有关系型数据库例如MySql的Navicat这种成熟的运维工具
总而言之,MongoDB的使用场景很大程度上可以对标关系型数据库,但是比较适合处理那些没有join、没有强一致性要求且表Schema会常变化的数据。
4. Memcache 与 Redis 的区别都有哪些?
1)、存储方式
Memecache把数据全部存在内存之中,断电后会挂掉,数据不能超过内存大小。
Redis有部份存在硬盘上,这样能保证数据的持久性。
2)、value大小
redis最大可以达到1GB,而memcache只有1MB
3)、数据支持类型
Memcache对数据类型支持相对简单。1. 文本型 、二进制类型
Redis有复杂的数据类型。String List Hash Set ZSet.
4)、持久化支持
Redis有 RDB 和 AOF 持久化机制, Memecache 没有
5)、查询【操作】类型
Redis : 1. 批量操作 2. 事务支持 3. 每个类型不同的CRUD
Memecache : 1.常用的CRUD 2. 少量的其他命令
6)使用底层模型不同
它们之间 底层实现方式 、与客户端之间通信的应用协议 不一样。
Redis直接自己构建了VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求。
5. 数据库与NoSql及各种NoSql间的对比
最后一部分,做一个总结,本文归根到底是两个话题:
- 何时选用关系型数据库,何时选用非关系型数据库
- 选用非关系型数据库,使用哪种非关系型数据库
首先是第一个话题,关系型数据库与非关系型数据库的选择,在我理解里面无非就是两点考虑:
第一点,不多解释应该都理解,非关系型数据库都是通过牺牲了ACID特性来获取更高的性能的,假设两张表之间有比较强的一致性需求,那么这类数据是不适合放在非关系型数据库中的。
第二点,核心数据不走非关系型数据库,例如用户表、订单表,但是这有一个前提,就是这一类核心数据会有多种查询模式,例如用户表有ABCD四个字段,可能根据AB查,可能根据AC查,可能根据D查,假设核心数据,但是就是个KV形式,比如用户的聊天记录,那么HBase一存就完事了。
这几年的工作经验来看,非核心数据尤其是日志、流水一类中间数据千万不要写在关系型数据库中,这一类数据通常有两个特点:
- 写远高于读
- 写入量巨大
一旦使用关系型数据库作为存储引擎,将大大降低关系型数据库的能力,正常读写QPS不高的核心服务会受这一类数据读写的拖累。
接着是第二个问题,如果我们使用非关系型数据库作为存储引擎,那么如何选型?其实上面的文章基本都写了,这里只是做一个总结(所有的缺点都不会体现事务这个点,因为这是所有NoSql相比关系型数据库共有的一个问题):
2.Redis 是什么? 都有哪两种缓存? 为什么这么快? Redis 的优缺点?
1. Redis 是什么?
可以理解成是一个缓存, 缓存主要解决高性能和高并发的问题.
不能用作海量数据的高性能读写, 只能用在较小数据量的高性能操作和运算.
- 高性能: 每次从硬盘上读数据会比较慢,如果数据库中的数据改变, 同步改变缓存中相应的数据即可.
- 高并发: 请求缓存承受的压力比请求数据库的压力小. 所以请求缓存的话可以不经过数据库. 减压了
2. 本地/分布式 缓存:
- 本地缓存 : Java自带的map / guava 实现本地缓存,其生命周期随JVM 的销毁而结束, 在多例情况下,每一实例都要保存一份缓存, 会有缓存不一致的现象.
- 分布式缓存 : 相比于多实例情况, 各实例共享一份缓存数据, 数据具有一致性. 缺点是: 架构复杂.
3. Redis 为什么快:
- 完全基于内存, 绝大部分的请求是内存操作, (少部分需要去硬盘上读取), 像Hashmap 的查抄和操作的时间复杂度都是O(1).
- 数据结构简单, 对数据操作简单
- 采用单线程,避免了不必要的切换和竞争, 也不存在多线程的切换而消耗CPU, 就也不用考虑锁的添加、释放和死锁导致的性能消耗
- 使用了多路IO 复用模型, 非阻塞IO
- 使用的底层模型不同, Redis 直接自己构建了VM 机制, 避免了底层实现方式以及客户端之间通信应用协议不一样的问题.
4.Redis 和其key-value 相比, 有什么特殊的?
- Redis 提供了原子性操作,
- Redis 运行在内存中但是可以持久化到磁盘, 所以在对不同数据集进行高速读写时 需要权衡内存,因为数据量不能大于硬件内存.「在内存中的操作简单.」
- Redis 支持数据的备份,即 master-slave 模式的数据备份。
- Redis 不仅仅支持简单的 key-value 类型的数据String ,同时还提供 list,set,zset, hash 等数据结构的存储。
5. Resid 优缺点 :
优点 :
- 读写速度快. 读的速度是 11 0000次/s ; 写的速度是 81000次/s.
- 因为数据存在内存中,类似于 HashMap,HashMap 的优势就是查 找和操作的时间复杂度都是 O1)
- 支持事务.
- 支持数据持久化. 支持 AOF 和 RDB 两种持久化方式.
- 支持多种数据结构. 其中value类型包括: String; hash; set;zset; list;
- 支持主从复制, 住机会自动将数据同步到从机,可以进行读写分离.
- 可用于缓存,可以按 key 设置过期时间,过期后将会自动删除
缺点:
- 只能根据K查V,无法根据V查K
- 查询方式单一,只有KV的方式,不支持条件查询,多条件查询唯一的做法就是数据冗余,但这会极大的浪费存储空间
- 受限于数据库容量(受到物理内存的限制), 不能用作海量数据的高性能读写, 只能用在较小数据量的高性能操作和运算.
- Redis 不能自动容错,不具备自动恢复功能.主从机宕机的话都会导致前端部分读写请求失败, 解决办法: 只能等待及其重启或者手动切换前端ip.
- 宕机之后可能会发生数据不一致的问题. 造成可用性差. 如果在宕机前数据没能从主机传到从机,qiehuanIP后数据就不一致了
- Redis 不好在线扩容. 所以在使用时要留有足够大的空间, 会造成资源浪费.
3. Redis 中key-value中value 的5 种数据类型
数据类型 | 可存储的值 | 可进行的操作 | 应用场景 |
String | 字符串、整数、浮点数 | 对字符串字符串的一部分操作, 对整数和浮点数自增自减操作, | 简单的键值对缓存 |
List | 列表 | 从两端压入或弹出的元素,对单个或者多个元素进行修剪, 只保留一个范围内的元素 | 存储一些列表型数据结构,类似微博里的粉丝列表、文章评论列表的数据 |
Hash | 包含键值对的无序散列表 | 添加、获取、一处单个简直对;获取所有键值对, 检查某个键是否存在 | 结构化的数据,比如一个对象的所有信息 |
Set | 无序集合 | 添加、获取、移除单个元素, 检查一个元素是否存在于集合中.计算交集、并集、差集, 从集合里面随机获取元素 | 交集: 比如找共同好友 |
ZSet | 有序集合 | 添加、获取、删除元素,根据分支范围或者成员来获取元素, 计算一个键的排名 | 去重,可排序 |
1. 在项目中的应用场景
source :
1、缓存数据
最常用,对经常需要查询且变动不是很频繁的数据 常称作热点数据。
2、消息队列
相当于消息订阅系统,比如ActiveMQ、RocketMQ。如果对数据有较高一致性要求时,还是建议使用MQ)
3、计数器
比如统计点击率、点赞率,redis具有原子性,可以避免并发问题
4、电商网站信息
大型电商平台初始化页面数据的缓存。比如去哪儿网购买机票的时候首页的价格和你点进去的价格会有差异。
5、热点数据
比如新闻网站实时热点、微博热搜等,需要频繁更新。总数据量比较大的时候直接从数据库查询会影响性能
4. 数据的一致性问题、删除策略
1.如何让保证缓存数据的一致性问题?
source : 高并发问题 - 如何解决Redis缓存和MySQL数据一致性的问题 - MSJ521 - 博客园
缓存和数据库一致性解决方案:
1.第一种方案:采用延时双删策略
2.具体的步骤就是:先删除缓存、再写数据库、休眠500毫秒、再次删除缓存;
休眠500毫秒问题 : 要评估自己的项目的读数据业务逻辑的耗时。这么做的目的,就是确保读请求结束,写请求可以删除读请求造成的缓存脏数据。当然这种策略还要考虑redis和数据库主从同步的耗时。最后的的写数据的休眠时间:则在读数据业务逻辑的耗时基础上,加几百ms即可。
3.设置缓存过期时间
从理论上来说,给缓存设置过期时间,是保证最终一致性的解决方案。所有的写操作以数据库为准,只要到达缓存过期时间,则后面的读请求自然会从数据库中读取新值然后回填缓存。
4.该方案的弊端
结合双删策略+缓存超时设置,这样最差的情况就是在超时时间内数据存在不一致,而且又增加了写请求的耗时。
1.第二种方案:异步更新缓存(基于订阅binlog的同步机制)
1.技术整体思路:MySQL binlog增量订阅消费+消息队列+增量数据更新到redis
1)读Redis:热数据基本都在Redis
2)写MySQL: 增删改 都是操作MySQL
3)更新Redis数据:MySQ的数据操作binlog,来更新到Redis
2.Redis更新
1)数据操作主要分为两大块:
一个是全量(将全部数据一次写入到redis)
一个是增量(实时更新)
这里说的是增量,指的是mysql的update、insert、delate变更数据。
2)读取binlog后分析 ,利用消息队列,推送更新各台的redis缓存数据。
这样一旦MySQL中产生了新的写入、更新、删除等操作,就可以把binlog相关的消息推送至Redis,Redis再根据binlog中的记录,对Redis进行更新。
其实这种机制,很类似MySQL的主从备份机制,因为MySQL的主备也是通过binlog来实现的数据一致性。
这里可以结合使用canal(阿里的一款开源框架),通过该框架可以对MySQL的binlog进行订阅,而canal正是模仿了mysql的slave数据库的备份请求,使得Redis的数据更新达到了相同的效果。
当然,这里的消息推送工具也可以采用别的第三方:kafka、rabbitMQ等来实现推送更新Redis。
================================================================
2. Redis 中查询的数据怎么保证数据查询的一致性?
source : Redis中如何保证数据库和缓存双写时的数据的一致性? - King-D - 博客园
1.“缓存+数据库” 必须保持一致性,采用读请求和写请求串行化,串到一个内存队列的方案。
串行化可以保证一定不会出现不一致的情况,但是它也会导致系统的吞吐量大幅度降低,用比正常情况下多几倍的机器去支撑线上的一个请求。
2. 最经典的缓存+数据库读写的模式,就是 Cache Aside Pattern
读的时候,先读缓存,缓存没有的话,再读数据库,取出数据后放入缓存,同时返回响应。
更新时候,先更新数据库,然后再删除缓存。
================================================================
3. 为什么是删除缓存,而不是更新缓存?
很多时候,在复杂点的缓存场景,缓存不单单是数据库中直接取出来的值。而是需要进行运算后才能缓存出最新的值.
还有就是, 如果每次更新数据库都更新缓存的话, 都更新缓存, 这里涉及到一个问题就是 更新在缓存中的数据是不是热数据.
删除缓存,而非更新缓存,用的是懒计算思想.这样开销大幅降低, 当用到缓存才去算缓存。
================================================================
source :如何保证缓存(redis)与数据库(MySQL)的一致性-阿里云开发者社区
4. 数据不一致 * 简单情况 : 为什么先删缓存, 再更新数据库?
问题:先更新数据库,再删除缓存。如果删除缓存失败了,那么会导致数据库中是新数据,缓存中是旧数据,数据就出现了不一致。
解决思路:先删除缓存,再更新数据库。如果数据库更新失败了,那么数据库中是旧数据,缓存中是空的,那么数据不会不一致。因为读的时候缓存没有,所以去读了数据库中的旧数据,然后更新到缓存中。
采用 cache aside pattern 并发更新操作的时候可以先删除缓存,然后更新数据库。
此方案下的更新操作情况:
- 删除缓存失败,那么不会去执行update操作。
- 删除缓存成功,update失败,读请求还是会将旧值写回到redis中。
- 删除缓存成功,update成功,读请求会将新值写回到redis中。
================================================================
5. 数据不一致 * 复杂情况: 数据变更--> 删了缓存-->修改数据库之前有了请求...
问题描述 : 数据发生了变更,先删除了缓存,然后要去修改数据库,此时还没修改。一个请求过来,去读缓存,发现缓存空了,去查询数据库,查到了修改前的旧数据,放到了缓存中。随后数据变更的程序完成了数据库的修改。完了,数据库和缓存中的数据不一样了...
解决方案 : 这种情况应该采用异步读写请求队列去解决,简言之就是: update请求入队列,读请求入队列,update操作未执行完之前,读操作被阻塞,但是读操作需要while循环 一段时间,因为一旦当前操作的读请求之前还有一个读请求在队列中,很可能前一个读请求已经将update后的新值已经读取到redis当中了。
6. 单节点下: ✅先删缓存 后更新库 与 ❌先更新库 后删缓存 对比
单节点: 不涉及主从复制的这种情况
- 先删缓存,再更新数据库:
同步更新缓存的策略,可能会导致数据长时间不一致,如果用延迟双删来优化,还需要考虑究竟需要延时多长时间的问题——读的效率较高,但数据的一致性需要靠其它手段来保证
采用异步更新缓存的策略,不会导致数据不一致,但在数据库更新完成之前,都需要到数据库层面去读取数据,读的效率不太好——保证了数据的一致性,适用于对一致性要求高的业务
- 先更新数据库,再删缓存:
无论是同步/异步更新缓存,都不会导致数据的最终不一致,在更新数据库期间,缓存中的旧数据会被读取,可能会有一段时间的数据不一致,但读的效率很好——保证了数据读取的效率,如果业务对一致性要求不是很高,这种方案最合适
7. key 过期的删除策略
缓存中某些数据不再是热数据时, 可以通过删除 key-value 中的key 来减轻内存压力.
1. 惰性过期 : 只有在访问key 时才去判断 这个key 是否过期, 过期则删除.
会减轻CPU 的压力, 但是会增大内存压力. [如果好多key 不再被访问了,但也会一直占用内存]
2. 定时过期:为每个key 设置定时器,到期就立即删除.
对内存很友好; 但会占用大量的CPU资源去处理过期的数据,会影响到 : 缓存的响应时间 & 吞吐量。
3. 定期过期 : 每隔一定时间, 扫描一定数量的key ,如果过期,则删除. [是惰性过期和定时过期的折中]
可设置每次扫描的时间间隔 & 每次扫描的限定耗时.
所以, 定期过期是一个平衡、折中方案. Redis 同时使用了这两种策略.
定时清理 缺点: 维护大量缓存的key是比较麻烦的,
惰性清理 缺点: 每次用户请求过来都要判断缓存失效,逻辑相对比较复杂!
7. Redis key的过期时间和永久有效分别怎么设置?
Expire 和 Persist 命令。
8. 有大量的 key 需要设置同一时间过期,需要注意 什么?
如果大量的 key 过期时间设置的过于集中,到过期的那个时间点,redis 可能会出现短暂的卡顿现象。一般需要在时间上加一个随机值,使得过期时间分散一 些。
5. 内存相关
1. MySQL里有2000w数据,redis中只存20w的数据,如何保证redis中的数据都是热点数据
redis内存数据集大小上升到一定大小的时候,就会施行数据淘汰策略。
2. Redis的[ 内存淘汰策略/回收策略 ]有哪些? ---- 6 种..
source : Redis数据淘汰策略_ThinkWon的博客-CSDN博客
Redis的内存淘汰策略是指 : 在Redis用于缓存的内存不足时,怎么处理需要新写入且需要申请额外空间的数据。
全局的 键空间选择性移除
- no-eviction:当内存不足以容纳新写入数据时,新写入操作会报错。禁止清楚数据
- allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key。(这个是最常用的)
- allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个key。
设置过期时间的键空间选择性移除
- volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key。
- volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key。
- volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的key优先移除。
使用规则
1、如果数据呈现幂律分布,也就是一部分数据访问频率高,一部分数据访问频率 低,则使用 allkeys-lru
2、如果数据呈现平等分布,也就是所有的数据访问频率都相同,则使用 allkeys-random
总结
Redis的内存淘汰策略的选取并不会影响过期的key的处理。
内存淘汰策略用于处理内存不足时的需要申请额外空间的数据;
过期策略用于处理过期的缓存数据。
3. Redis主要消耗什么物理资源?
内存。
3.Redis集群最大节点个数是多少?
16384个
3.一个字符串类型的值能存储最大容量是多少?
512M
4.为什么 Redis 需要把所有数据放到内存中?
异步 的方式将数据写入磁盘。所以 redis 具有快速和数据持久化的特征。如果不将数据放在内存中, 磁盘 I/O 速度会严重影响 redis 的性能。
5. Redis的内存用完了会发生什么?
如果达到设置的上限,Redis的写命令会返回错误信息(但是读命令还可以正常返回。)
或者可以配置内存淘汰机制,当Redis达到内存上限时会冲刷掉旧的内容。
6. Redis如何做内存优化?
可以好好利用Hash,list,sorted set,set等集合类型数据,因为通常情况下很多小的Key-Value可以用更紧凑的方式存放到一起。
尽可能使用散列表(hashes),散列表(是说散列表里面存储的数少)使用的内存非常小,尽可能的将数据模型抽象到一个散列表中.
比如你的web系统中有一个用户对象,不要为这个用户的名称,姓氏,邮箱,密码设置单独的key,而是应该把这个用户的所有信息存储到一张散列表里面
6. Redis 单线程快的原因?
1. 单线程机制
Redis基于Re actor模式开发了文件事件处理器 file event handler。 文件事件处理器是单线程的,所以 Redis 才叫做单线程的模型,它采用IO多路复用机制来同时 监听多个Socket,根据Socket上的事件类型来选择对应的事件处理器来处理这个事件。
可以实现高性能 的网络通信模型,又可以跟内部其他单线程的模块进行对接,保证了 Redis 内部的线程模型的简单性。
文件事件处理器的结构包含4个部分:多个Socket、IO多路复用程序、文件事件分派器、事件处理器 (命令请求处理器、命令回复处理器、连接应答处理器等)。
多个 Socket 可能并发的产生不同的操作,每个操作对应不同的文件事件,但是IO多路复用程序会监听多个 Socket,会将 Socket 放入一个队列中排队,每次从队列中取出一个 Socket 给事件分派器,事件分派器把 Socket 给对应的事件处理器。
然后一个 Socket 的事件处理完之后,IO多路复用程序才会将队列中的下一个 Socket 给事件分派器。文 件事件分派器会根据每个 Socket 当前产生的事件,来选择对应的事件处理器来处理。
2. 单线程快的原因:
1)纯内存操作
2)核心是基于非阻塞的IO多路复用机制
3)单线程反而避免了多线程的频繁上下文切换带来的性能问题
7. Redis持久
持久化就是 : 把内存的数据写到磁盘中去,防止服务宕机了内存数据丢失。
1. Redis 能保证数据不丢失吗?
就先答可以吧, 把appendfsync值设置为always 可能也会丢失一个循环的数据
可以. 因为有持久化的AOF 和 RDB两种持久化模式.
2. Redis 怎么防止数据丢失?
Redis要想实现高可用,主要有以下方面来保证:
- 数据持久化
- 自动故障恢复
- 主从复制
- 集群化
3. RDB快照模式 : Redis DataBase.
在指定的时间间隔内降内存中的数据集快照存入磁盘. 实际的操作过程是fork 一个子进程, 先将数据集写入临时文件, 写入成功后, 再替换之前的文件, 用二进制压缩存储.
优点 :
- 子进程和主进程任务分离,实现了性能最大化, fork 子进程来完成写操作,完成持久化操作. 主进程继续执行命令,
- 整个Redis 数据库 只包含一个drum.rdb 文件, 通过配置文件中的save参数来定义快照的周期. 方便持久化,
- 容灾性好,文件可以保存到磁盘中,方便备份.
- 对于较大的数据集来说, RDB 的性能比AOF 的强.
缺点:
- 数据安全性低,因为RDB是间隔一段时间进行持久化的,如果在间隔期 Redis 发生了故障, 会导致数据丢失
- RDB 是通过fork 子进程来协助完成持久化工作的, 所以当数据量较大时, 可能会导致卡顿, 即 : 服务器停止服务几百毫秒,甚至1秒.
4. AOF 日志模式 : Append Only File
以文本日志的形式记录服务器每一次处理的命令, 主要是增删命令,查询的话,无记录.
优点:
1. 数据安全. AOF 持久化可以配置 appendfsync 属性. 有三种同步策略, 分别是每秒同步、每修改同步、不同步.
每秒同步 是 异步的, 执行效率高. 但是在这一秒内,如果系统出现宕机,则这一秒的数据会丢失.
每修改同步 是 同步的. 可将其理解成同步持久化, 每次操作的数据变化都会记到磁盘中
解决数据一致性的问题。
重写, 目的: 压缩.
[AOF 文件没被 rewrite 之前(文件过大时会对命令 进行合并重写),可以删除其中的某些命令(比如误操作的 flushall) ]
缺点:
- AOF 文件比 RDB 文件大,且恢复速度慢。
- 数据集大的时候,比 rdb 启动效率低。
5. 两种机制对比:
- AOF文件比RDB更新频率高,优先使用AOF还原数据, 也更安全.
- RDB性能比AOF好
- 如果两个都配了优先加载AOF
6. 如何选择合适的持久化方式
- 一般来说, 如果想提高数据安全性,应同时使用两种持久化功能。当 Redis 重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整。
- 如果非常关心你的数据, 但仍然可以承受数分钟以内的数据丢失,那么你可以只使用RDB持久化。
- 有很多用户都只使用AOF持久化,但并不推荐这种方式,因为定时生成RDB快照(snapshot)非常便于进行数据库备份, 并且 RDB 恢复数据集的速度也要比AOF恢复的速度要快,除此之外,使用RDB还可以避免AOF程序的bug
- 如果你只希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化方式。
7. Redis持久化数据和缓存怎么做扩容?
- 如果Redis被当做缓存使用,使用一致性哈希实现动态扩容 / 缩容。
- 如果Redis被当做持久化存储使用,必须使用固定的keys-to-nodes映射关系,节点的数量一旦确定不能变化。否则的话(即Redis节点需要动态变化的情况),必须使用可以在运行时进行数据再平衡的一套系统,而当前只有Redis集群可以做到这样。
8. 在你的项目中 Redis 中都存什么信息? 所有数据都可以存在Redis中吗?
不是所有的数据都存在Redis中.
主要用Redis存储一些 数据量不是很大的、 允许丢失、需要经常查询、变动不是很频繁的数据 .
目前我们主要用:
1.计数器,比如点击率,后台定期将这些数据写回数据库。
- 点餐系统里,顾客所选菜品的数量;
2.缓存列表,比如用户所发帖子的id列表,最热的帖子id列表。
- 主要存近期访问的电子银行账单、报税表.
6. Redis 事务
1. Redis 事务的本质 :
通过MULTI、、DISCARD、EXEC、WATCH等一组命令的集合。
一次性 : 事务支持一次执行多个命令,一个事务中所有命令都会被序列化。
顺序性 : 在事务执行过程,会按照顺序串行化执行队列中的命令,
排他性 : 其他客户端提交的命令请求不会插入到事务执行命令序列中。
总结说:redis事务就是一次性、顺序性、排他性的执行一个队列中的一系列命令。
2. Redis事务的三个阶段
1. 事务开始 MULTI
2. 命令入队
- 如果客户端发送的命令为 EXEC、DISCARD、WATCH、MULTI 四个命令的其中一个,那么服务器 立即执行 这个命令。
如果客户端发送的不是以上的四个命令,那么服务器并不立即执行这个命令。 - 首先检查此命令的格式是否正确: 如果不正确,服务器会在客户端状态(redisClient)的 flags 属性关闭REDIS_MULTI 标识,并且返回错误信息给客户端。
如果正确,将这个命令放入一个事务队列里面,然后向客户端返回 QUEUED 回复
3. 事务执行 EXEC
如果客户端状态的 flags 属性不包含 REDIS_MULTI 标识,或者包含 REDIS_DIRTY_CAS 或者 REDIS_DIRTY_EXEC 标识,那么就直接取消事务的执行。
否则客户端处于事务状态(flags 有 REDIS_MULTI 标识),服务器会遍历客户端的事务队列,然后执行事务队列中的所有命令,最后将返回结果全部返回给客户端;
- WATCH 命令是一个乐观锁,可以为 Redis 事务提供 check-and-set (CAS)行为。可以监控一个 或多个键,一旦其中有一个键被修改(或删除),之后的事务就不会执行,监控一直持续到EXEC 命令。
- MULTI命令用于开启一个事务,它总是返回OK。MULTI执行之后,客户端可以继续向服务器发送 任意多条命令,这些命令不会立即被执行,而是被放到一个队列中,当EXEC命令被调用时,所有 队列中的命令才会被执行。
- EXEC:执行所有事务块内的命令。返回事务块内所有命令的返回值,按命令执行的先后顺序排 列。当操作被打断时,返回空值 nil 。 通过调用DISCARD,客户端可以清空事务队列,并放弃执行事务, 并且客户端会从事务状态中退 出。
- UNWATCH命令可以取消watch对所有key的监控。
3. 事务管理(ACID)概述
1. 原子性(Atomicity): 事务是一个不可分割的工作单位,操作要么都发生,要么都不发生。
- 单条命令是原子性执行的,但事务不保证原子性,且没有回滚。
- 事务中任意命令执行失败,其余的命令仍会被执行。
2. 一致性(Consistency): 事务前后数据的完整性必须保持一致。所以Redis 事务有一致性.
3. 隔离性(Isolation): 多个事务并发执行时,一个事务的执行不应影响其他事务的执行
- Redis 是单进程程序,并且它保证在执行事务时,不会对事务进行中断,事务可以运行直到执行完所有事务队列中的命令为止。因此,Redis 的事务是总是带有隔离性的。
4. 持久性(Durability): 持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响
- 服务器在AOF持久化模式下,并且appendfsync选项的值为always时,事务具有持久性。
7. Redis 集群
1. Redis 集群的原理
1)、Redis Sentinal (哨兵) 着眼于高可用,在 master 宕机时会自动将 slave 提升为 master,继续提供服务。
2)、Redis Cluster (集群) 着眼于扩展性,在单个 redis 内存不足时,使用 Cluster 进行 分片存储。
2. 哨兵模式
哨兵的介绍 : sentinel,中文哨兵。哨兵是 redis 集群机构中非常重要的一个组件, 功能:
集群监控:负责监控 redis master 和 slave 进程是否正常工作。
消息通知:如果某个 redis 实例有故障,那么哨兵负责发送消息作为报警通知给管理员。
故障转移:如果 master node 挂掉了,会自动转移到 slave node 上。
配置中心:如果故障转移发生了,通知 client 客户端新的 master 地址。
哨兵用于实现 redis 集群的高可用,本身也是分布式的,作为一个哨兵集群去运行,互相协同工作。
故障转移时,判断一个 master node 是否宕机了,需要大部分的哨兵都同意才行,涉及到了分布式选举的问题。
即使部分哨兵节点挂掉了,哨兵集群还是能正常工作的,(因为如果一个作为高可用机制重要组成部分的故障转移系统本身是单点的,那就很坑爹了。)
哨兵的核心知识 :
哨兵至少需要 3 个实例,来保证自己的健壮性。
哨兵 + redis 主从的部署架构,是不保证数据零丢失的,只保证 redis 集群的高可用性。
对于哨兵 + redis 主从这种复杂的部署架构,尽量在测试环境和生产环境,都进行充足的测试和演练。
2.说说 Redis 哈希槽的概念?
哈希槽的概念,Redis 集群有 16384 个哈希槽(即 节点个数的最大值),每个 key 通过 CRC16 校验后对 16384 取模来决定放置哪个槽, 集群的每个节点负责一部分 hash 槽。
3. Redis 集群的 主从复制--(异步复制)
为使在部分节点失败或者大部分节点无法通信的情况下集群仍然可用,所 以集群使用了主从复制模型,每个节点都会有 N-1 个复制品.
Redis 回收进程如何工作的?
一个客户端运行了新的命令,添加了新的数据。Redi 检查内存使用情况,如 果大于 maxmemory 的限制, 则根据设定好的策略进行回收。一个新的命令被执行.
一个 Redis 实例最多能存放多少的 keys? -------- 理论上 Redis 可以处理多达 232 的 keys
Redis 集群方案什么情况下会导致整个集群不可用?
有 A,B,C 三个节点的集群,在没有复制模型的情况下, 若节点 B 失败了, 那整个集群就会因缺少 5501-11000 这个范围的槽而不可用
Redis 集群如何选择数据库? -------- Redis 集群目前无法做数据库选择,默认在 0 数据库。
怎么测试 Redis 的连通性? -------- 使用 ping 命令。
Redis 如何设置密码及验证密码?
设置密码:config set requirepass 123456
授权密码:auth 123456
13、Redis 的同步机制了解么?
8. 缓存异常 :
1. 缓存击穿 :
缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力。
2. 缓存穿透 :
缓存和数据库中都没有的数据,所以,导致所有的请求都落到数据库上,造成数据库短时间内承受大量请求而崩掉。
3 缓存雪崩 :
缓存同一时间大面积的失效,所以,后面的请求都会落到数据库上,造成数据库短时间内 承受大量请求而崩掉。
缓存击穿和缓存雪崩不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。
1.1 缓存击穿 解决方案:
- 设置热点数据永远不过期。
- 加互斥锁,互斥锁
1.2 缓存穿透 解决方案:
- 接口层增加校验,筛选出不符合的查询信息. 如用户鉴权校验,id做基础校验,id<=0的直接拦截;
- 从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value 写为 key-null,
- 缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击
- 采用布隆过滤器(Bloom Filter),将所有可能存在的数据哈希到一个足够大的 bitmap 中,一定不存在的数据会被这个 bitmap 拦截掉,从而避免了对底层存储系统的查询压力
Bloom-Filter算法的核心思想就是利用多个不同的Hash函数来解决“冲突”。----- 多重验证
Hash存在一个冲突(碰撞)的问题,用同一个Hash得到的两个URL的值有可能相同。为了减少冲突,我们可以多引入几个Hash,如果通过其中的一个Hash值我们得出某元素不在集合中,那么该元素肯定不在集合中。只有在所有的Hash函数告诉我们该元素在集合中时,才能确定该元素存在于集合中。这便是Bloom-Filter的基本思想。
Bloom-Filter 一般用于在大数据量的集合中判定某元素是否存在。
1.3 缓存雪崩 解决方案:
- 缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。
- 一般并发量不是特别多的时候,使用互斥锁排队 (使用最多的解决方案).
- 给每一个缓存数据增加相应的缓存标记,记录缓存的是否失效,如果缓存标记失效,则更新数据缓存。
4. 缓存预热
将相关的缓存数据直接加载到缓存系统。用户直接查询事先被预热的缓存数据!
这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!
解决方案
- 直接写个缓存刷新页面,上线时手工操作一下;
- 数据量不大,可以在项目启动的时候自动进行加载;
- 定时刷新缓存;
5. 缓存降级
当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然需要保证服务还是可用的,即使是有损服务。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级。
缓存降级的最终目的是保证核心服务可用,即使是有损的。而且有些服务是无法降级的(如加入购物车、结算)。
在进行降级之前要对系统进行梳理,看看系统是不是可以丢卒保帅;从而梳理出哪些必须誓死保护,哪些可降级;
服务降级的目的,是为了防止Redis服务故障,导致数据库跟着一起发生雪崩问题。因此,对于不重要的缓存数据,可采取服务降级策略,例如一个比较常见的做法就是,Redis出现问题,不去数据库查询,而是直接返回默认值给用户。
6. 热点数据和冷数据
热点数据,缓存才有价值; 冷数据,大部分数据可能还没有再次访问到就已经被挤出内存,不仅占用内存,而且价值不大。
频繁修改的数据,看情况考虑使用缓存 ---- 对于热点数据,数据更新前至少读取两次,缓存才有意义。
修改频率很高,但是又不得不考虑缓存的场景呢?
点赞数,收藏数,分享数等是非常典型的热点数据,但是又不断变化,此时就需要将数据同步保存到Redis缓存,减少数据库压力。
7. 缓存热点key
缓存中的一个Key(比如一个促销商品),在某个时间点过期的时候,恰好在这个时间点对这个Key有大量的并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。
解决方案
加锁,如果KEY不存在,就加锁,然后查DB入缓存,然后解锁;其他进程如果发现有锁就等待,然后等解锁后返回数据或者进入DB查询
n. Redis 常见性能问题和解决方案:
1、Master 最好不要写内存快照(RDB 持久化),如果 Master 写内存快照,save 命令调度 rdbSave 函数,会阻塞主线程的工作,当快照比较大时对性能影响是非常大的,会间断性暂停服务
2、如果数据比较重要,某个 Slave 开启 AOF 备份数据,策略设置为每秒同步一次
3、为了主从复制的速度和连接的稳定性,Master 和 Slave 最好在同一个局域网
4、尽量避免在压力很大的主库上增加从库
5、主从复制不要用图状结构,用单向链表结构更为稳定,即:Master <- Slave1 <- Slave2 <- Slave3...这样的结构方便解决单点故障问题,实现 Slave 对 Master 的替换。如果 Master 挂了,可以立刻启用 Slave1 做 Master,其他不变。