后端面试题

23 篇文章 0 订阅

Mysql

- mvcc

- 死锁

- 组复制

传统复制

传统的 MySQL 复制形式为主从复制,由一个主库(master)和一个或多个从库(slave)组成。事务只能在主库执行,主库执行事务,提交(commit)之后,将会传播给从库执行。一般这个过程是异步的,由 binlog 提供基于语句的复制(实际执行的 sql),或者基于行的复制(增删改的行 sql)。这是一个 shared-nothing 的系统,所有主从都有一套完整独立的数据。

由于纯异步复制可能会造成数据丢失(以后详细分析),5.5 中以插件形式引入了半同步复制,在主库向客户端返回事务已提交的信号之前,多了一个同步步骤,要等从库通知主库事务已接收。这个同步可以发生在 server 层 sync binlog 之后、innodb 引擎 commit 之前(after sync),或者在 innodb 引擎 commit 之后、返回给客户端之前(after commit)。

组复制

组复制是一种可以用来实现高可用集群的技术,组内的各个服务器相互通信协作,来保证事务的 ACID 特性。组复制实现了多主写入的特性,即,任意一个主库都可以做数据的更新,事务会被复制到其余的主库以及从库中。在被写入的主库返回给客户端事务已提交之前,组复制插件会保证将被写入的数据,以及写集(write set,写入数据的唯一标识符,一般为主键)有序传播到其他的主库中。注意,这个**“有序”**非常重要,它将保证所有主库接收到的都是一致的事务,而不至于发生数据错乱。

显而易见的是,多主写入的特性下,多个客户端并发更新数据,必然导致事务冲突,如何解决冲突就显得尤为重要。这个时候,写集(write set)的作用就体现出来了,如果在不同主库上的两个事务,更新了同一行数据,它们将产生相同的写集,MySQL 就能检测到事务冲突。MySQL 解决冲突的策略是,谁先提交以谁为主,后提交的事务回滚,这样来保证数据的一致性。

和传统复制一样,组复制也是一个 shared-nothing 的系统,所有主库都有同等的完整数据。

组复制不解决数据分片问题

多点写入

由于不存在传统意义上的主库,组中的任意一个节点都可以用来执行事务,包括写入类事务。就像前面提到过的,在事务提交前,要做一些额外的检查工作:

  1. 检查事务是否有冲突

  2. 将事务传播给其他节点,其他节点需要确认已接收到事务


 MySQL组复制-初见 - 墨天轮

mysql 复制_MySQL 组复制介绍_weixin_39742727的博客-CSDN博客

- 主从延迟


MySQL主从同步延迟的原因及解决办法-木庄网络博客

Redis

- Redis 支持哪些数据类型?

  1. 字符串:redis没有直接使用C语言传统的字符串表示,而是自己实现的叫做简单动态字符串SDS的抽象类型。C语言的字符串不记录自身的长度信息,而SDS则保存了长度信息,这样将获取字符串长度的时间由O(N)降低到了O(1),同时可以避免缓冲区溢出和减少修改字符串长度时所需的内存重分配次数。
  2. 链表linkedlist:redis链表是一个双向无环链表结构,很多发布订阅、慢查询、监视器功能都是使用到了链表来实现,每个链表的节点由一个listNode结构来表示,每个节点都有指向前置节点和后置节点的指针,同时表头节点的前置和后置节点都指向NULL。
  3. 字典hashtable:用于保存键值对的抽象数据结构。redis使用hash表作为底层实现,每个字典带有两个hash表,供平时使用和rehash时使用,hash表使用链地址法来解决键冲突,被分配到同一个索引位置的多个键值对会形成一个单向链表,在对hash表进行扩容或者缩容的时候,为了服务的可用性,rehash的过程不是一次性完成的,而是渐进式的。
  4. 跳跃表skiplist:跳跃表是有序集合的底层实现之一,redis中在实现有序集合键和集群节点的内部结构中都是用到了跳跃表。redis跳跃表由zskiplist和zskiplistNode组成,zskiplist用于保存跳跃表信息(表头、表尾节点、长度等),zskiplistNode用于表示表跳跃节点,每个跳跃表的层高都是1-32的随机数,在同一个跳跃表中,多个节点可以包含相同的分值,但是每个节点的成员对象必须是唯一的,节点按照分值大小排序,如果分值相同,则按照成员对象的大小排序。
  5. 整数集合intset:用于保存整数值的集合抽象数据结构,不会出现重复元素,底层实现为数组。
  6. 压缩列表ziplist:压缩列表是为节约内存而开发的顺序性数据结构,他可以包含多个节点,每个节点可以保存一个字节数组或者整数值。

基于这些基础的数据结构,redis封装了自己的对象系统,包含字符串对象string、列表对象list、哈希对象hash、集合对象set、有序集合对象zset,每种对象都用到了至少一种基础的数据结构。

redis通过encoding属性设置对象的编码形式来提升灵活性和效率,基于不同的场景redis会自动做出优化。不同对象的编码如下:

1. 字符串对象string:int整数、embstr编码的简单动态字符串、raw简单动态字符串
2. 列表对象list:ziplist、linkedlist
3. 哈希对象hash:ziplist、hashtable
4. 集合对象set:intset、hashtable
5. 有序集合对象zset:ziplist、skiplist

- Redis 缓存要注意的五大经典问题? 

列举了亿级系统,高访问量情况下Redis缓存可能会遇到哪些问题?以及对应的解决方案。

  1. 缓存雪崩(当较大的流量洪峰到来时,大量的redis缓存在同一时间失效)
    1.   - 方案一:在缓存的过期时间入口,将原来的固定过期时间,调整为过期时间=基础时间+随机时间,让缓存慢慢过期,避免瞬间全部过期,对DB产生过大压力。
    2.   - 方案二:热点的key放在不同的结点上去,让热点的缓存平均的分配在不同的redis结点上
    3.   - 方案三:将集中化流量打散,避免一个缓存节点过载。由于只有一个key,我们可以在key的后面拼上`有序编号`,比如`key#01`、`key#02`。。。`key#10`多个副本,这些加工后的key位于多个缓存节点上。或者是 每次请求时,客户端随机访问一个节点即可
    4.   - 方案四:定时的跑任务,在缓存失效前刷新缓存
  2. 缓存穿透(一堆肉鸡访问一个不存的id,或缓存中的数据过期、或因某些原因被删除等,达到数据库上,一般redis和数据库内都没有这个数据)
    1.   - 方案一:构造一个布隆过滤器,初始化全量数据,当接到请求时,在布隆过滤器中判断这个key是否存在,如果不存在,直接返回即可,无需再查询缓存和DB
  3. 缓存击穿(突然key失效,一瞬间该key的请求打到数据库上)
    1.   - 方案一:引入一把`全局锁`,当缓存未命中时,先尝试获取全局锁,如果拿到锁,才有资格去查询`DB`,并将数据预热到缓存中。虽然,client端发起的请求非常多,但是由于拿不到锁,只能处于等待状态,当缓存中的数据预热成功后,再从缓存中获取
    2.   - 方案二:提前预热,缓存数据创建多个备份,当一个过期失效后,可以访问其他备份。
    3.  可以设计一个缓存服务治理管理后台,实时监控缓存的SLA,并打通分布式配置中心,对于一些`hot key`可以快速、动态扩容。
  4. 缓存大Key(value过大,读写、加载很容易超时,容易引发网络拥堵)
    1. 方案一:设置一个阈值,当value的长度超过阈值时,对内容启动压缩,降低kv的大小
    2. 方案二:评估大key所占的比例,由于很多框架采用池化技术,如:Memcache,可以预先分配大对象空间。真正业务请求时,直接拿来即用。
    3.  方案三:颗粒划分,将大key拆分为多个小key,独立维护,成本会降低不少
    4.  方案四:大key要设置合理的过期时间,尽量不淘汰那些大key
  5. 缓存数据的一致性
    1. 方案一:将key写入MQ消息队列,通过异步任务补偿缓存,保证数据的一致性。

- redis淘汰机制

  1. allkeys-lru 所有键最久没用
  2. volatile-lru 过期的键久没用
  3. allkeys-lfu 所有键最少使用
  4. valatile-lfu 过期的键最少使用
  5. volatile-random 随机淘汰
  6. volatile-ttl 优先淘汰回收存活时间较少的键

     https://blog.csdn.net/weixin_43979923/article/details/124432089

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值