1、redis使用场景
1.1、缓存穿透
缓存击穿指的是,查询一个不存在的数据,数据库里面和缓存都没有数据,如果有高并发访问db,这样会造成数据库压力很大
为了避免这个问题产生,有2个方案:
1、空数据,存取空数据到缓存,可以减少对db的访问,实现简单,但是内存压力大
2、布隆过滤器,实现复杂,但是可以有效降低内存消耗 布隆过滤器的实现原理是一个位图map,在存储到数据库的同时,在布隆过滤器里面保存一个是否存在的标识
1.2、缓存击穿
缓存击穿是指热点数据在瞬间过期,导致大量用户访问数据库,造成db压力
解决方案:
1、互斥锁,当发现热点数据失效,加锁,查询,恢复缓存数据,返回数据 ,当其他线程进来的时候,互斥,等待恢复缓存之后,再查询
这样的弊端是性能不好,查询效率不高
2、方案二:逻辑锁 在数据里面建立一个字段,过期时间,当发现数据过期的时候,加互斥锁、直接返回过期的数据,然后开启新的一个线程,去查询、维护更新缓存、跟新完成之后释放锁 其他线程进来发现获取锁失败,也是返回旧的数据就行
1.3、缓存雪崩
缓存雪崩主要是因为,redis中大量的key在同一时间过期,或者redi宕机导致数据库压力大
解决方案:
1、设置随机的ttl(1-5min)
2、利用高可用集群来防止redis宕机
3、接口限流策略(aop思想来实现缓存)
1.4、双写一致性
1、数据库和redis缓存之间的一致性存在的问题
在业务场景中,有可能会存在数据不一致的问题
先操作数据库、删除、更新缓存 期间有可能有其他线程进来,修改数据库
先操作缓存,把缓存删除了,然后修改数据库,期间可能有线程进来更新缓存
解决方案:
1、强一致性: 读写锁(redission 读共享、写排她) 或者延时双删,但是这样会造成数据不一致的风险,延时的时间不好控制 也会导致数据有不一致的情况
ps;延时双删就是先删除缓存,然后再修改数据库,然后再延时删除缓存,这样可以尽可能避免数据不一致,因为延时的时间不好来确定,所以还是有数据不一致的风险,所以强一致性还是推荐读写锁来控制
2、对于对一致性要求不高的场景,可以通过异步来实现,修改数据库之后,发送消息,通过mq来实现(通过mq来发送消息,实现运动数据本月卡路里消耗总量的修改)
1.5、redis持久化
###### 1.5.1、rdb和aof两种持久化方式的实现方式是怎样的?
###### 1.5.2、rdb和aof两种持久化方式的区别是怎样的?优缺点对比?
###### 1.5.3、根据不同的业务场景怎么选择持久化策略?
Redis 提供了两种主要的持久化机制,用于将内存中的数据保存到磁盘上,以便在 Redis 重启后能够恢复数据。这两种持久化机制分别是 RDB(Redis Database Backup) 和 AOF(Append-Only File)。
1. RDB(Redis Database Backup)
RDB 是 Redis 的默认持久化方式。它通过生成数据集的快照(snapshot)来保存数据。
特点:
二进制格式:RDB 文件是二进制格式的,文件紧凑,适合备份和恢复。
快照机制:RDB 会在指定的时间间隔内,将内存中的数据保存到磁盘。
性能高:RDB 持久化是通过fork子进程来完成的,主进程不需要进行磁盘 I/O 操作,因此对性能影响较小。
恢复速度快:RDB 文件加载速度快,适合大规模数据恢复。
配置:
在 Redis 配置文件(redis.conf)中,可以通过以下参数配置 RDB:
plaintext
复制
save 900 1 # 在 900 秒内,如果至少有 1 个 key 发生变化,则触发保存
save 300 10 # 在 300 秒内,如果至少有 10 个 key 发生变化,则触发保存
save 60 10000 # 在 60 秒内,如果至少有 10000 个 key 发生变化,则触发保存
dbfilename dump.rdb # RDB 文件名
dir ./ # RDB 文件保存路径
优点:
文件紧凑,适合备份和灾难恢复。
恢复速度快。
缺点:
可能会丢失最后一次快照之后的数据(取决于配置的保存间隔)。
如果数据集很大,fork 子进程可能会导致短暂的性能下降。
2. AOF(Append-Only File)
AOF 通过记录每个写操作(如 SET、DEL 等)来持久化数据。这些操作会被追加到 AOF 文件的末尾。
特点:
日志格式:AOF 文件是文本格式的,记录了所有写操作。
实时性更强:可以通过配置实现更高的数据安全性。
重写机制:AOF 文件会不断增长,Redis 提供了 BGREWRITEAOF 命令来重写 AOF 文件,减少文件大小。
配置:
在 Redis 配置文件(redis.conf)中,可以通过以下参数配置 AOF:
plaintext
复制
appendonly yes # 启用 AOF
appendfilename "appendonly.aof" # AOF 文件名
appendfsync everysec # 同步策略:everysec(每秒同步)、always(每次写操作同步)、no(由操作系统决定)
auto-aof-rewrite-percentage 100 # AOF 文件增长比例达到 100% 时触发重写
auto-aof-rewrite-min-size 64mb # AOF 文件最小重写大小
优点:
数据安全性更高,可以根据配置实现几乎零数据丢失。
AOF 文件易于理解和解析。
缺点:
文件体积通常比 RDB 大。
恢复速度比 RDB 慢。
3. RDB 和 AOF 的对比
特性 RDB AOF
持久化方式 快照 记录每次写操作
文件格式 二进制 文本
数据安全性 可能丢失最后一次快照后的数据 数据安全性更高
恢复速度 快 慢
文件大小 小 大
性能影响 fork 子进程可能影响性能 每次写操作可能影响性能
4. 混合持久化(RDB + AOF)
从 Redis 4.0 开始,支持混合持久化。即在 AOF 重写时,将当前数据集以 RDB 格式写入 AOF 文件的开头,后续的写操作仍然以 AOF 格式追加。
配置:
在 redis.conf 中启用混合持久化:
plaintext
aof-use-rdb-preamble yes
优点:
结合了 RDB 和 AOF 的优点,恢复速度快且数据安全性高。
5. 如何选择持久化方式?
如果对数据安全性要求高,可以选择 AOF 或 混合持久化。
如果对性能要求高,且可以容忍少量数据丢失,可以选择 RDB。
在实际生产环境中,通常会结合使用 RDB 和 AOF,以兼顾性能和数据安全性。
通过合理配置 Redis 的持久化机制,可以确保数据的安全性和 Redis 的高性能运行。
Redis两种持久化方式
RDB REDIS DATABASE :以二进制来保存数据,定期持久化到磁盘 在redis.conf中可以修改配置参数 一段时间内触发多少次写入就可以保存数据
优点:性能影响比较小 ,恢复数据快
缺点:容易丢失最后一次写入的数据,数据完整性不能保证
AOF APPEND-ONLY FILE:
在文件中记录每一次操作的命令,文件持久话到磁盘,恢复数据比较慢,性能慢点,文件比较大,但是数据安全性可以得到保障,几乎不丢失数据
1.6、redis过期策略
redis过期策略有两种
1、惰性删除 redis过期之后,不会马上从内存中清理出去,而是下一次访问的时候,如果过期了,就删除,没有就返回key 这个策略对cpu友好,但是对内存不友好
2、定时清理
在过期之后,定期清理内存,删除缓存中的过期key
定时清理有2个方案:
slow 定时任务,固定频率10hz,每次不超过25ms
fast 不固定频率,频率不低于2ms,每次执行不超过1ms
redis过期策略 被动删除(惰性删除) 主动删除(定期清理========定期清理有2种,slow :固定频率10hz,每次执行25ms来清理过期key fast:频率不固定,不小于2ms,每次执行不大于1ms)
1.7、缓存淘汰策略
在redis中,当redis内存满了之后,数据如何淘汰,策略有8种:
1、默认redis不在新增新的key,直接报错
2、对设置了ttl的key,ttl越小,优先淘汰
3、对所有的key进行随机淘汰
4、对设置了ttl的key,进行随机淘汰
5、对所有的key ,用lru算法淘汰
6、对设置了ttl的key,用lru算法淘汰
7、对所有的可以用lfu算法进行淘汰
8、对设置了ttl的key,用lfu算法淘汰
PS:
LRU:最近最少使用 redis中,当前时间减去上一次访问这个key的时间,越大,被淘汰的几率越大
LFU:最少频率使用 redis中的key,访问频率越大,越不容易淘汰
1.8、redis分布式锁–使用场景
分布式锁是为了解决集群环境下,并发请求,超卖问题
比如,线程1读取到了一个库存为1,去卖出的过程中,还没有修改扣减库存数量,这个时候,有另外一个线程进来也读取到了库存为1,两个线程都进行了库存数量的扣减,库存为-1了,这个过程就引起了超卖问题
不成熟的解决方案: 加本地锁synochize ,在多个nginx反向代理的环境中,这个锁是失效的,因为synochize是基于jvm的,每台机器有单独的jvm,相互独立,加本地锁没用
因此我们使用分布式锁,这样可以避免线程安全问题,解决了超卖的问题
1.9、分布式锁实现原理
1、redis分布式锁,是怎么实现的?
当一个线程获取redis分布式锁之后,其他线程重试等待,等当前线程完成业务逻辑,释放锁之后,其他线程才能获取到分布式锁,期间锁可以通过看门狗机制,实现续期,这种锁的实现方式叫做分布式锁(redission 底层原理是setnx lua脚本)
在功能机项目,赠送会员、赠送视频彩铃功能都采用了redission 的分布式锁,理由是一开始没有采用,导致重复赠送的情况发生,叠加了会员日期 ,后面加了分布式锁,问题得到解决
2、redission分布式锁如何合理控制有效时长?
看门狗机制,一个线程获取锁成功之后,看门狗机制会易10s为一个周期来对锁进行续期,默认ttl的1/3
3、redission锁,可以重入吗?
可以,redission分布式锁是可重入锁,同一个线程,可以重入,redis在hash结构种保存了线程和锁的重入次数
4、redission锁能解决主从一致性的问题吗?
不能解决,但是可以使用redission红锁来实现,就是把锁保存在其他节点种,这样性能会损耗很多,不推荐
如果业务种需要强一致性,推荐zookeeper来实现分布式锁
案例
2.0、主从同步流程
redis主从同步是基于一主多从的背景下,为了解决单机环境不能抗高并发的问题
一般都是1主2从,主负责写数据,从负责读取数据
1、写入到主节点之后,从节点请求同步数据,携带replid、offset偏移量
2、主节点收到从节点的同步数据请求,发现replid不一样,说明是第一次同步数据,这个时候主节点将replid/offset发送给从节点,主从节点使用同一个replid/offset
3、同时主节点执行 bgsave 命令,将数据备份成rdb文件发送给从节点,从节点接受到rdb文件,先清空内存,然后开始进行数据同步,期间如果有请求到主节点,先存到缓冲区repl_baklog日志里面,后续进行增量备份同步
增量备份同步:从节点请求主节点数据同步,如果replid一致、说明主从节点不是第一次同步数据,从节点只需要从主节点repli_baklog中同步offset之后的数据就行
2.1、redis哨兵、集群、脑裂
redis哨兵主要是用于监控服务健康状况、自动故障恢复(选举机制)、通知客户端服务的变化(类似nacos作用)
脑裂就是在网络环境不好的情况下,哨兵联系主节失败,这时选取一个从节点来作为新的主节点(选择规则就是按照offset越大,就说明数据同步的越多、数据量丢失的越少)。后续如果老的主节点恢复服务了,就出现两个主节点了,这个现象就是脑裂,不过旧的主节点会降级为从节点,这样会造成数据的丢失
为了避免数据的丢失,减少从节点数量为1个,缩短主从同步时间为5s,否则主节点就应该拒绝写入
1、redis怎么保证高可用?
通过哨兵实现redis服务健康检测、自动故障恢复、服务通知
2、redis使用的是单点还是集群?哪种集群?
redis是集群1主2从+哨兵模式
3、redis脑裂,该怎么解决?
redis脑裂可以通过修改redis配置
修改从节点数量为1(最少的从节点数量可以缩短同步数据的时间),修改主从同步时间为5s
2.2、分片集群
分片集群指的是多个master节点,多个slave节点组成的一个大集群
优点:
1、扩展性更好,多个master可以接收更多的数据写入,性能更好
2、多个master平均分配16384个哈希槽,数据写入或读取规则是根据key的有效值的hash值,与16384取余来选择哪个master节点
3、多个master直接可以实现心跳检查,健壮性更好
2.3、redis为什么那么快?
1、redis是基于内存的,执行速度非常快
2、redis是单线程的,避免了不必要的上线文切换,和考虑多线程线程安全问题
3、采用io多路复用模型,非阻塞IO
什么是io多路复用?
同一个线程监听多个socket,当某一个socket准备就绪的时候,直接通知操作线程将socket写入用户空间
3.0、Mysql慢查询
再我们业务场景中,经常会出现程序卡顿的情况,比如功能机项目,点击程序进入首页发现很慢
1、先找运维同事调取特定时间点nginx日志,然后从日志中可以分析得出相关接口的访问时间,可以初步判断哪个接口慢
2、然后从接口入手,分析代码逻辑是否存在逻辑上的多次for循环,或者其他冗余的查询逻辑,可以通过日志打印耗时来判断哪个环节慢,最后定位到具体的sql
3、可以通过开启慢查询日志来找到慢查询sql,慢查询sql在my.cnf中配置
3.1、慢查询分析(explain)
explain + sql
分析之后的关键字段:
possible key : 可能用到的索引
key:实际用到的索引
keylength:索引长度
extra:额外的优化建议
extra会出现的几种结果
1、using where /using index 查找走了索引,查找的数据在索引中可以找到,不走回表
2、using index condition 查找使用了索引,但是数据需要回表查询数据
type:这条sql的连接类型,有如下几种情况
null:不查表,在实际情况不存在
sys:系统表
const:根据主键查询的情况
eq_ref:主键索引或唯一索引查询
ref:索引查询
range:范围查询
index:索引树扫描
all:全盘扫描
3.2、如果这个sql很慢,如何分析
通过explain来分析sql
首先看看key keyLength这两个分析结果,是否走了索引,没有的话,创建索引
然后看type字段查看sql是否有进一步优化的空间,是否存在全表扫描或者全索引扫描的情况
通过extra看看是否有回表的情况,如果有,将需要的字段添加到索引里面去
3.3、聚集索引或者二级索引
聚集索引就是通过索引直接可以查询所有数据的索引,一般建立在主键或者唯一索引上面(没有唯一索引或者主键,每一行有一个rowId作为唯一索引)
二级索引就是建立在普通索引上面的
二级索引叶子节点关联的是对应的主键
回表就是在二级索引上面找不到对应的字段,然后通过主键再查询索引树,查询数据的过程
3.4、覆盖索引
mysql在查询的时候用到了索引,并且查询的字段在索引种都可以找到
所以,在项目种尽量不要用select * from ... 因为这样很容易回表,回表查询效率低
3.5、超大分页查询怎么处理
覆盖索引+子查询
语句:
select * from tb_sku a , (select id from tb_sku order by id limit 9000,10) b where a.id = b.id;
3.6、索引创建的原则
1、数据量比较大的表,要创建索引,单表数据量大于10w,建议使用索引
2、经常用于group by order by 的字段,要加索引
3、建立索引要选取区分度高的字段,尽量选择唯一索引,区分度越高,查询效率越高
4、尽量使用联合查询,联合查询尽量使用覆盖索引,这样可以提高查询效率
5、索引不是越多越好,因为索引的建立需要维护,还是很影响性能的,当索引数量太多
6、字段太长的情况下,可以建立前缀索引
大g区联多前
3.7、索引失效的几种情况
模:模糊查询百分号在前面,索引失效
型:数据类型对不上,要进行类型转换,索引会失效
数:有函数的时候,索引会失效
空:加索引的字段空的数据太多,索引会失效
运:数据运算的sql,不走索引
最:不遵循最佳做前缀法则 索引失效(结合key keyLength来判断)
快:根据表的大小,来判断,表数据比较少,全表扫描比走索引还有快,这个时候不会走索引
3.8、sql优化的建议
1、建表的时候,字段选取要合理(int bigint tinyint varchar char)比如char定长,效率高,但是不节省磁盘空间
2、写sql的时候,不要写select * 这样的语句,避免回表
3、查询的时候避免索引失效的几个场景
4、小表驱动大表
5、从数据库架构层面、读写分离
6、建索引
7、分库分表(数据量大于500w)
3.9、事物特性
1、原子性:事物是不可分割的整体,要么成功,要么失败
2、一致性 事物执行后,数据保持一致
3、隔离性 事物之间相互独立,不受外界影响
4、持久性 一旦提交或者回滚,对于数据库中的数据影响是永久的
4.0、并发事物问题及解决方案
数据库并发事物存在3个问题
脏读:读取到未提交的数据
不可重复读:一个事物读取某一行数据,其他线程修改了该行数据,该线程再次读取,发现数据发生变化了
示例:
事务 A 读取某一行数据,值为 100。
事务 B 修改了这一行数据,将值更新为 200,并提交。
事务 A 再次读取同一行数据时,发现值变为 200。
幻读:数据库中执行相同的sql,由于其他线程的操作,导致查询2次查询结果不一样
示例:
事务 A 查询表中所有年龄为 20 的记录,返回 2 条。
事务 B 插入一条年龄为 20 的新记录,并提交。
事务 A 再次查询年龄为 20 的记录,发现返回 3 条。
解决方案:
读未提交 都不能解决
读已提交 可以解决脏读
可重复读 可以解决脏读、不可重复读
串行化 都可以解决,但是性能很差,不推荐使用
4.1、缓冲池&数据页
在数据库中缓冲池是用于暂时将磁盘中的数据放到缓冲池中,用于用户访问,这样性能更高
用户操作缓冲池,然后再写入磁盘中的数据页
数据页是innodb中存储数据的最小单元,16kb,页里面存储的是行数据
4.2、undo与redoLog
在innodb中,redolog是用于保障数据的一致性和持久性的日志,redolog存在于缓冲区中,会实时将更新的数据同步到磁盘中redolog file中,是一个物理日志,当数据库发生宕机时,用于数据恢复
undoLog是保障数据库数据原子性的保障,当事物回滚就靠undoLog来实现,同时也是mvcc的重要依据,undoLog是逻辑日志,记录数据库中操作的反向逻辑操作,当数据发生错误需要回滚,这个时候,直接执行 undoLog是逻辑日志,记录操作的反向操作,比如新增一条数据,就记录删除一条数据,删除一条记录就记录新增一条数据 更新一条数据,就记录更新逻辑的反向操作
4.3、MVCC(多版本并发控制)
数据库的隔离性是如何实现的?(解释一下mvccc)
是基于mvcc实现的 就是多版本并发控制 数据库多个版本之间实现读写不冲突
mvcc实现的三个必要要素 隐藏字段、undoLog、readview
隐藏字段:
最近修改事务ID:插入数据时候的事物id或者最近一次修改数据的事物id
回滚指针:指向上一个版本的地址 ,用于undoLog做事物回滚
隐藏主键:如果表结构里面没有设计主键,就用行号作为隐藏主键
4.4、Mysql主从同步原理
1、主数据库写入数据,同时数据变更写入到二进制的binLog中
2、从数据库读取主数据库中的binLog文件到自己的中继日志中
3、从数据库从中继日志,重做事件,生成数据
4.5、分库分表
单表大于5000w,需要分库分表