MySQL和Redis的面经

总结2024年中50+场次的真实面试过程中的被问到的MySQL和Redis的真实面试问题(高频面试问题)

一、MySQL问题

1、数据库层面你有没有做过 MySQL 的优化?索引失效的场景?(得物)

MySQL 的优化从以下三个点去回答:

索引失效的场景:

2、请你设计一张用户表,我想把用户的手机号码作为表的主键ID,这个可以吗?如果不可以会有什么问题?(零食很忙)

3、为什么我们在查询的时候会建议我们少使用left join?(零食很忙)
  1. 性能问题
    LEFT JOIN 会返回左表的所有记录,即使右表中没有匹配的记录也会返回空值,这意味着 MySQL 在执行时需要扫描左表的所有行,而不仅仅是匹配的行。这增加了查询的计算量,尤其是在左表很大时,性能影响明显。

  2. 不可预知的优化
    MySQL 的优化器在处理 LEFT JOIN 时,可能不会像处理 INNER JOIN 那样有效,因为 LEFT JOIN 的匹配条件比较复杂,优化器有时难以决定最佳的执行计划,从而影响查询效率。

  3. 索引的使用
    当查询涉及到 LEFT JOIN 时,MySQL 有时不能有效利用右表的索引,尤其是在查询需要从多个表中关联数据时。对于 INNER JOIN,由于只需要匹配的行,因此可以更好地利用索引进行优化。

  4. 数据冗余
    如果 LEFT JOIN 的右表没有匹配项,查询结果中可能会包含很多空值,导致不必要的数据冗余。尤其是在复杂查询中,如果使用了多个 LEFT JOIN,空值和数据重复问题会更加严重。

  5. 需求误用
    有时候开发者选择 LEFT JOIN 是因为想保留左表的所有记录,但实际上并不需要这些记录。此时,INNER JOIN 可能已经足够,因此使用 LEFT JOIN 会导致不必要的性能开销。

因此,在设计查询时,如果确实需要保留左表的所有记录,那么 LEFT JOIN 是必须的,但如果只是简单的关联匹配,INNER JOIN 通常性能更好且结果更明确。

4、MySQL深度分页你是怎么做优化的?union和union all怎么去优化它的一个查询?(得物)
5、我们有一张主表 数据量大(有一百万多的数据),这张表需要加索引。但是这张表,时时刻刻都在新增数据。你如何添加索引将影响降到最小?(零食很忙)
6、MVCC工作原理?(数字马力)
7、数据库索引怎么合理的建立?(数字马力)

        7.1、现明确什么是索引:索引是数据库中一个独立的存储结构,用于快速查找表中的数据。通过为列创建索引,数据库可以在搜索时减少磁盘I/O操作,加快查询速度。然而,索引并不是越多越好,因为每次对表进行增删改操作时,索引也会同步更新,增加了额外的开销。

        7.2、然后说一下一般有那些类型的索引:

                  按照数据结构分:B+树、hash

                  按照物理存储结构分:聚簇索引、非聚簇索引

                  按照特性进行分类:唯一、全文、普通、组件

                  按照索引字段个数分:单列索引、联合索引

        7.3、再说建立索引的基本原则

        频繁作为查询条件的字段:如果某个字段经常在 WHEREJOINGROUP BYORDER BY 等子句中使用,那么为该字段建立索引可以显著提高查询性能。例如,用户查询系统中,user_id 常用作 WHERE 条件,则可以为 user_id 建立索引。

        区分度高的字段:索引的效率与字段的区分度(即该字段的不同值的多少)直接相关。一般情况下,区分度高的字段适合作为索引,而区分度低的字段(如性别、布尔类型的状态字段)不适合建索引。例如,email 字段的区分度较高,而 gender 字段的区分度较低,不建议为 gender 字段单独建立索引。

        避免为频繁更新的字段建立索引:更新操作不仅涉及数据本身,还需要维护相关的索引。如果某个字段非常频繁地被更新,那么为它建立索引可能得不偿失。此时应该慎重考虑是否需要索引,或选择其他优化方案。

        避免过多索引:虽然索引可以提升查询效率,但也会占用额外的存储空间并增加写操作的开销。为表建立过多的索引会影响插入、更新、删除等操作的性能。因此,索引应该精简且有针对性。

        联合索引的左前缀原则:如果某个查询需要多列作为条件,应该为这些列建立联合索引。例如对于 WHERE a = ? AND b = ? 的查询,可以建立 (a, b) 的联合索引。但是在查询中,必须遵循“最左前缀”原则:如果索引的列是 (a, b),查询条件必须包含 a,否则无法有效利用该索引。

        覆盖索引:覆盖索引是指查询的所有字段都能从索引中直接获取,而不需要访问表数据。例如 SELECT name FROM users WHERE user_id = ?,如果 user_idname 都在索引中,那么这个查询可以直接从索引获取数据,提升查询性能。合理设计覆盖索引可以减少回表操作的开销。

总结:合理的索引设计应结合实际的查询需求,针对高频查询和高区分度的字段创建索引。同时要考虑到更新的开销,避免不必要的索引过多。索引设计是一个平衡的过程,需要在查询效率和数据维护成本之间做出权衡。

二、Redis问题

1、你用Redis会有那些问题?(缓存穿透、缓存击穿、缓存雪崩)(零食很忙)

        1.1、缓存穿透:当大量的用户去访问一个不存在的值的时候,正常访问的时候,先去查询缓存,缓存没有,再去查询数据库,发现数据库也没有,那么也就不会去写缓存。就直接落到数据库上,大量请求的情况下打到数据库就会奔溃

        1.2、缓存击穿:有个很热门的key,然后突然过期了,这个时候大量的请求就直接落到数据库中,数据库就会奔溃

                解决方案:1、可以设置热点key永不过期;2、可以过期,但是再热门的时间段不能过期

        1.3、缓存雪崩:大量的key再同一时间过期了,这个时候有很多请求来访问,请求就直接落到数据库中,数据库就会奔溃

               

2、Redis有那些数据类型?set和zset有什么区别?(数字马力)

具体细节可参考以下文章:Redis - 免费思维导图 - 知犀

3、Redis 它是单线程的还是多线程的?为什么设计成单线程?以及就是设计成单线程,为什么它的一个性能还这么高呢?redis的单线程是用于哪?它的哪些操作展示单线程?哪些又是多线程?你既然谈到Redis的IO多路复用,那么IO还有哪些东西?这几种有什么区别?(百拓创新)

        Redis 的核心是单线程处理网络请求,这意味着 Redis 在处理客户端的读写请求时,只有一个线程在工作。然而,从 Redis 6.0 开始,Redis 引入了多线程,用于处理 I/O 相关的工作(如接受客户端连接、读写数据等),以减少网络瓶颈,尤其是在高并发场景下。尽管如此,核心的命令执行部分依然是单线程的。

Redis 选择单线程设计主要有以下几个原因:

  • 数据结构的高效操作:Redis 内部使用的是诸如哈希表、跳表等数据结构,它们的操作速度非常快。而在单线程模型下,不需要考虑复杂的线程同步问题,这大大简化了开发过程并减少了潜在的并发 Bug。
  • CPU 并非 Redis 性能瓶颈:Redis 的瓶颈通常不是在 CPU,而是在网络 I/O 和内存带宽上。因为 Redis 的所有数据都在内存中,绝大多数操作都是内存读写,单线程已经足够处理非常高的并发请求,达到每秒上百万的操作。
  • 减少上下文切换的开销:多线程模型会带来上下文切换、线程锁等开销,尤其是在处理频繁的锁竞争时。Redis 采用单线程避免了这些复杂性和开销,确保了其处理请求的效率。

尽管 Redis 核心是单线程的,但它依然能够实现非常高的吞吐量,原因如下:

  • 完全基于内存的操作:Redis 的所有数据都存储在内存中,读写操作非常快,避免了磁盘 I/O 的瓶颈。
  • 高效的数据结构:Redis 内置了如字典(哈希表)、列表(双向链表)、集合(哈希表+跳表)、有序集合(跳表)等高效的数据结构,使得操作非常迅速。
  • IO 多路复用技术:Redis 采用了 epoll(在 Linux 系统下)等 I/O 多路复用技术,能够同时处理大量的客户端连接,而不用为每个客户端连接都分配一个线程。I/O 多路复用能够让 Redis 同时监听多个网络连接并处理就绪的 I/O 事件,大幅提升了并发性能。
  • 单线程减少了锁的开销:多线程环境下经常需要锁来保证数据一致性,而锁的竞争和上下文切换带来了额外的开销。单线程模型避免了这种情况,使得处理请求更加轻量和高效。

Redis 的单线程主要用于处理客户端请求的执行,也就是包括读写操作的逻辑部分。这些操作是 Redis 的核心功能,涉及对数据结构的增删改查,保证每个命令的执行顺序和一致性。

具体的操作包括:

  • 命令的解析和执行:Redis 的所有命令(如 SETGETINCR 等)都是在单线程中执行的。
  • 网络事件的处理:尽管 Redis 使用了 I/O 多路复用技术,所有网络事件的处理仍然由单线程来完成。每次网络事件触发时,Redis 的主线程会根据事件类型进行相应的处理。

从 Redis 6.0 开始,引入了部分多线程功能,用于处理网络 I/O 操作。多线程主要用于以下方面:

  • 网络数据的读写:在高并发场景下,Redis 多线程用于加速网络数据的接收和发送,减少了网络瓶颈。
  • 异步任务:某些异步任务可以交给后台线程来处理,比如持久化(RDB 快照、AOF 写入),以避免阻塞主线程。

尽管如此,核心的命令执行部分仍然是单线程的。这确保了 Redis 的命令执行逻辑保持简单、无锁,从而提高了整体的处理效率。

Redis 使用的 I/O 多路复用是通过 epoll(在 Linux 系统上)或者 selectpoll 等系统调用来实现的。I/O 多路复用是一种异步 I/O 处理技术,它能够同时监控多个 I/O 通道,并在某个通道就绪时进行相应的操作,而不需要阻塞在某个 I/O 上等待。

I/O 多路复用的几种方式:
  • select:最古老的 I/O 多路复用机制,最多可以监视 1024 个文件描述符。性能和可扩展性较差,但具有较好的兼容性。
  • pollpollselect 的功能类似,但没有文件描述符数量限制。然而,poll 每次调用时都需要将所有文件描述符重新传递给内核,开销较大。
  • epollepoll 是 Linux 下最为高效的 I/O 多路复用机制。它通过内核支持的事件通知机制,不需要每次调用时重复传递所有的文件描述符,具有更好的性能和可扩展性。

除了 I/O 多路复用,还有以下几种常见的 I/O 模型:

  • 阻塞 I/O:每个 I/O 操作都会阻塞进程,直到数据准备好。这个模型简单但效率低,无法处理高并发场景。
  • 非阻塞 I/O:应用程序可以在发起 I/O 请求后立即返回,但需要不断轮询查看数据是否就绪。这种模型减少了阻塞,但浪费了 CPU 资源。
  • 信号驱动 I/O:通过信号通知应用程序 I/O 准备完成,不需要轮询,但编程较为复杂。
  • 异步 I/O(AIO):应用程序发起 I/O 请求后可以继续执行其他任务,等数据准备好后,内核会主动通知应用程序。AIO 是一种真正的异步 I/O 模型,效率很高,但实现复杂,且不同操作系统支持不一致。

总结:Redis 的高性能很大程度上得益于其单线程设计和 I/O 多路复用机制。单线程的设计减少了锁的竞争和复杂的同步问题,同时结合 I/O 多路复用技术,使得 Redis 在处理大量并发连接时仍然保持高效。而引入多线程优化了 I/O 读写操作,进一步提升了性能。

4、Redission分布式锁怎么用的具体怎么去实现的?(数字马力)
5、Redis 有哪些淘汰策略?Redis 的持久化有哪些?Redis 跟 MySQL 的数据一致性呢?强一致性怎么保证?(得物)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值