MQ
MQ消息中间件
-
问题引出与MQ作用
- 常见面试问题:面试官常针对项目中使用MQ技术的候选人提问,如如何确保消息不丢失,该问题可考察候选人技术能力。
- MQ应用场景及作用:以京东系统下单扣减京豆为例,MQ用于交易服和京豆服务通信。其主要作用是系统解耦和流量控制,实现系统高可用,如隔离上下游不稳定因素、服务降级;还能在流量突增时削峰填谷 。
-
消息丢失问题
-
消息丢失环节:消息从生产到消费分为生产、存储、消费三个阶段。生产阶段处理好返回值和异常可避免丢失;存储阶段由MQ保证,如broker做副本;消费阶段接收消息后执行完业务逻辑再确认可防止丢失。
-
消息丢失检测:在生产端给消息指定消息版本号,通过拦截器注入消息;消费端用拦截器检测版本连续性或消费状态,实现不侵入业务代码的消息检测。
-
解决方案:MQ的可靠消息投递机制
-
-
消息重复消费问题
- 问题转化:消息重复消费问题可转化为消费端幂等性问题。
- 实现方案:以扣减京豆为例,在数据库建消息日志表,含消息id和执行状态字段,消费消息前检查是否已存在,避免重复执行,实现幂等操作。还可基于关系数据库实现唯一约束方案。
- 全局唯一ID生成:解决消息丢失和重复消费问题,需掌握全局唯一ID生成技术,如数据库自增主键、UUID、Redis、Twitter Snowflake算法等,选型要结合业务平衡考虑,作者倾向Snowflake算法并会改造以适应业务。
-
消息积压问题
- 问题分析:消息积压反映性能问题,主要出在消费阶段。因为消息发送后才可能积压,且消息队列单节点存储性能较高,不易出现问题。
- 解决思路:现场突发问题时,临时扩容消费端数量并降低非核心业务,抗住流量;排查消费端业务逻辑问题,通过监控日志分析;若消费端处理能力不足,水平扩容消费端,同时同步扩充分区数量,确保消费者实例数和分区数相等,如Kafka中一个分区只能被一个消费者消费,增加分区可提高消费能力。
-
总结与思考
- 重点回顾:总结MQ消息队列热门问题解决方案,包括保证消息不丢失(了解各阶段丢失情况、监控及基于可靠消息投递解决)、不被重复消费(实现消费端幂等性)、处理消息积压(按应急处理、排查优化、扩容的思路)。强调面试时展示解决问题的思维过程更重要。
- 拓展知识:应聘基础架构还需掌握消息中间件其他知识,如选型、模型区别、高吞吐原理、序列化协议及内存管理等。
- 思考题:卡夫卡实现高性能的方式,鼓励观众留言讨论。
MySQL
MySQL索引原理与优化
- 索引在面试中的重要性:面试常考察数据库知识,索引原理和优化方法是重要切入点。以电商订单中心系统为例,为避免文件排序,应建立status和create time的组合索引。面试官还会追问索引底层数据结构、InnoDB选择B+树的原因、查看执行计划、索引失效情况及优化方法等问题。
- MySQL索引原理:MySQL常用索引有B+树索引、哈希索引、全文索引,InnoDB是默认存储引擎且常用B+树索引。创建表时,若有主键则用其作为主键索引;若无,InnoDB会生成隐藏主键。通过创建商品表并插入数据,展示B+树索引的构建和查询过程。B+树节点含多个子节点,父节点数据值会出现在子节点中,叶子节点包含所有数据值且形成链表。查询数据时,B+树自顶向下查找,如查询数据值15,仅需三次IO操作,体现其查询效率优势。若通过辅助索引查询,需先找到主键值,再通过主键索引查询,此过程称为回表。
- B+树索引的优势:与其他索引类型相比,B+树只在叶子节点存储数据,单个节点数据量小,相同磁盘IO次数可查询更多节点;叶子节点的双向链表结构适合范围查找,而B树不具备;B+树搜索复杂度为O(logdN) ,数据量达千万级别时,树高仍能维持在3 - 4层,磁盘IO操作次数少,优于二叉树;哈希表适合等值查询,不适合范围查找,B+树适用场景更广泛。
- 索引执行计划:通过查看执行计划(如possible key、key、key length等参数),可分析索引详情,其中type字段描述数据扫描类型,不同扫描类型效率不同。以like查询为例,分析索引失效原因,当查询优化器预估走索引代价比全表扫描大时,会放弃使用索引。
- 常见索引优化方法:
- 前缀索引:用字段前几个字符建立索引,可减少索引字段大小,提高查询速度,但存在局限性,如无法用于order by、不能用作覆盖索引。
- 覆盖索引:查询字段在B+树叶子节点都能找到,可避免回表,减少IO操作,如建立商品id、名称、价格的组合索引,查询这几个字段时无需回表。
- 联合索引:遵循最左匹配原则,建立时应将区分度大的字段排在前面,提高索引过滤概率。区分度是字段不同值个数除以表总行数,性别区分度小,不适合建索引或放联合索引前列;ID区分度大,适合建索引或放前列。
MySQL事务隔离级别和锁
- MySQL事务隔离级别与锁机制基础
- 事务隔离级别:用于多线程操作数据库时确保数据准确性,分为读未提交、读已提交、可重复读和串行化读。读未提交隔离度低,虽性能最高但会导致脏读、不可重复读和幻读,实际项目很少使用;读已提交解决了脏读问题,但仍存在不可重复读和幻读可能;可重复读是InnoDB引擎默认级别,能解决脏读和不可重复读,但幻读问题依旧存在;串行化读隔离级别最高,通过加锁实现,性能最低 。
- 锁机制概念:锁机制分为悲观锁和乐观锁。悲观锁认为数据冲突可能性大,利用select for update语句加锁避免数据意外修改;乐观锁认为冲突可能性小,借助CAS机制,通过对比时间戳或版本号实现版本控制。
- 脏读、不可重复读和幻读详解
- 脏读:脏读指读取到未提交事务的数据。并发事务A和B,A读取并更新数据未提交时,B读取到A更新后的数据,若A回滚,B读到的数据就是过时的,这种情况仅在读未提交隔离级别下出现。
- 不可重复读:不可重复读是指事务A读取数据后,事务B更新了该数据,导致A再次读取时数据不一致。读未提交和读已提交隔离级别会出现此问题,升级事务隔离级别可解决,如MySQL InnoDB默认的可重复读级别。
- 幻读:幻读是在同一事务内,相同查询语句不同时间执行得到不同结果集。解决幻读不能简单升级隔离级别,MySQL InnoDB引入间隙锁,面试时需掌握间隙锁及与行锁结合的相关知识。
- 死锁的产生与解决
- 死锁产生原因:死锁多发生在多线程争抢资源时,线程相互等待形成死锁状态。其产生有互斥、持有并等待、不可剥夺、循环等待四个必要条件,只有四个条件同时满足才会发生死锁。
- 死锁避免方法:避免死锁可破坏产生条件,如一次性申请所有资源破坏持有并等待条件;申请不到资源时主动释放已占资源破坏不可剥夺条件;按需申请资源,按资源序号顺序申请,破坏循环等待条件。
- 数据库领域应用开发者需掌握的内容及作业
- 需掌握内容:从数据库领域应用开发者角度,需掌握数据库设计基础(基本范式、表视图索引外键等概念、数据类型使用、业务实体关系与数据库结构映射、ORM开发)、数据库隔离级别(4种隔离级别基础知识、MVCC和锁机制进阶内容、不同索引类型及底层结构算法)、SQL优化(调试技巧、利用索引、分析执行计划)、数据库架构设计(高并发场景解决方案,如读写分离、分库分表)。
- 作业:思考并回答在面试中如何回答好MVCC和乐观锁的区别。
MySQL 优化查询方案
- MySQL在高流量场景下的优化问题
- 缓存的局限性:在电商订单中心场景,用Redis作MySQL缓存无法解决高流量问题,因其缓存命中率低,大量请求仍会到达数据库。
- 读写分离的必要性:互联网系统读写请求差异大,读写分离是提升MySQL并发的首选方案,通过将MySQL集群拆分成主从结构实现。
- 应对面试官套路的准备要点
- 架构设计思路:读多写少场景下,缓存有局限,读写分离才是提升系统并发能力的关键。
- 主从复制原理:深入了解MySQL主从复制的原理、问题及解决方案,其依赖bin log,分为写入bin log主库、同步binlog、回放binlog三个阶段。
- 技术认知抽象:从实践出发,做到技术的认知抽象,从方法论层面看待设计。
- MySQL主从复制的详细过程
- 集群结构:一个主库通常搭配2 - 3个从库,主库收到事务请求后,先写入bin log,提交事务更新数据并响应客户端;从库创建IO线程接收主库bin log日志写入中继日志并响应主库;从库再创建回放线程读中继日志更新数据,实现主从数据一致。
- 复制模型:MySQL主从复制有同步复制、异步复制、半同步复制三种模型。半同步复制介于同步和异步之间,能降低主库宕机时的数据丢失风险。
- 解决MySQL主从复制延迟及扩展问题
- 延迟问题解决方案:包括使用数据冗余(注意参数大小影响)、使用缓存(会带来数据和缓存一致性问题)、直接查询主库(需谨慎,避免影响主库性能)。
- 数据库使用变化及实现方式:主从分离后数据库使用需区分主从库地址和读写操作。工程代码实现数据访问有两种方式,一是提前配置数据源在代码逻辑中判断;二是独立部署代理中间件(如My Cat),但存在性能损耗和运营复杂的问题。
- 技术抽象能力:中高级研发工程师面试时,除掌握上述内容外,还应展示技术抽象能力,如理解复制状态机机制及其在MySQL主从复制中的应用,许多存储系统或数据库都采用类似方法实现数据复制和备份恢复。
分库分表
- 分库分表问题引入
- 在业务发展、数据量增加及读写分离后,写入压力增大导致数据库性能下降,引出分库分表方案。
- 分库分表常见策略有垂直拆分、水平拆分、垂直水平拆分。面试中不仅要知晓策略,更要能阐述整体设计方案与技术实现思路。
- 分库分表场景与策略选择
- 分库分表场景:数据量过大致使事务执行缓慢时考虑分表;单库性能无法满足高并发需求时进行分库。
- 垂直拆分:依据数据业务相关性拆分,常伴随系统架构调整,可隔离业务数据、利于架构扩展,但无法解决单一业务数据膨胀问题。
- 水平拆分:将单库表数据按规则拆分到多个库表,如哈希取模(把单表数据按哈希取模拆分到多个相同结构表中)和范围分片(按某字段区间拆分,常见按商品所属品类分配 )。范围分片可预估业务,但存在数据分布不均和热点数据问题,处理手段有垂直扩展(提升单机处理能力)和分配原数据(灵活性高,但实现复杂,需保证分配原数据服务高可用)。
- 垂直水平拆分:先垂直拆分不同类型数据到不同库,再水平拆分使单表数据量合理,提升性能。
- 分库分表后的数据查询问题:分库分表后传统查询方式受限,可通过将聚合查询数据同步到ES、单独存储计数数据、利用大数据技术处理报表数据等方式解决。
- new sql在面试中的应用及作业
- new sql:作为下一代存储技术,具备高性能、高可用、弹性扩容且兼容SQL标准和保障事务等优势,有可能取代MySQL。面试时可借此展示技术视野,提前了解其数据库原理,从与现有关系数据库区别切入讨论。
- 作业:思考数据库分片时商品关联表的处理方式。
文章目录
- MQ
- MQ消息中间件
- MySQL
- MySQL索引原理与优化
- MySQL事务隔离级别和锁
- MySQL 优化查询方案
- 分库分表
- 缓存
- Redis
- 缓存常见问题
缓存
Redis
- Redis线程模型
- 面试回答要点:回答Redis线程模型问题不能仅停留在单线程层面,应补充相关知识,如Redis并非完全单线程,网络IO和读写操作由一个线程完成,但持久化、集群同步等由其他线程执行。
- Redis单线程速度快的原因:主要原因包括大部分操作在内存中完成且采用高效数据结构;单线程模型避免多线程竞争和线程切换开销,不会出现死锁问题;采用IO多路复用机制处理大量客户端socket请求,基于非阻塞IO模型,读写流程不阻塞。
- 不同版本线程模型特点:Redis 4.0之前是典型单线程模型;4.0之后增加多线程支持,体现在数据异步删除功能上;6.0之后采用多个IO线程处理网络请求,但读写命令仍由单线程处理,以提高网络请求处理并行度。
- Redis数据持久化
- 持久化目的与方式:Redis为防止内存数据随服务器重启丢失,将数据存储到磁盘,有AOF日志、RDB快照、混合持久化三种方式。
- AOF日志原理与特点:AOF日志记录Redis收到的每一条命令,以文本形式追加到文件中,是写后日志,先执行命令再记录日志。优点是避免记录错误命令且不阻塞当前写操作,缺点是数据可能丢失,写入磁盘时可能阻塞后续操作。
- RDB快照原理与考点:RDB快照将某一时刻的内存数据以二进制写入磁盘,做数据恢复时直接读入内存,能快速恢复。考点包括RDB做快照时会阻塞线程,Redis提供save和bg save命令,默认使用bg save命令创建子线程操作避免阻塞;RDB做快照时数据可修改,利用bgcl子线程处理,写操作时修改数据会复制副本,子线程将副本写入RDB文件,主线程可直接修改原数据。
- 混合持久化:Redis 4.0后增加混合持久化方式,先以RDB方式写入文件,再将后续命令以AOF格式存储,既保证重启速度又降低数据丢失风险。
- Redis高可用方案
- 主从复制:是Redis高可用服务的基础保障,实现一主多从模式,可读写分离承载更多并发操作,但主从服务器出现故障时需手动恢复。
- 哨兵模式:为解决主从复制故障手动恢复问题,Redis增加哨兵模式,可监控主从服务器并提供自动恢复功能。
- Redis集群(Redis Cluster):是分布式去中心化运行模式,在Redis 3.0版本推出。采用哈希槽处理数据和实例之间的映射关系,一个分片集群有16384个哈希槽。哈希槽映射到具体Redis实例有平均分配(使用cluster create命令创建集群时自动分配)和手动分配(使用cluster meet等命令手动创建连接并指定哈希槽个数)两种方案,能提高Redis服务读写性能 。
缓存常见问题
- 缓存设计问题引入及面试套路
- 基于上一集redis原理学习,本集从应用案例切入,引出缓存雪崩、并发、穿透等常见设计问题。
- 面试中常给定场景,让候选人找出问题并提供解决方案,以电商平台商品详情页缓存问题为例展开后续探讨。
- 缓存穿透问题及解决方案
- 问题:查询特定key时,缓存和数据库都不命中,每次都查询数据库,若被恶意利用,会使数据库压力剧增甚至当机。
- 解决方案:给指定key预设默认值(如空字符串),业务代码据此判断是否查询数据库,避免无效请求穿透缓存到数据库。
- 缓存并发问题及解决方案
- 问题:缓存失效时多个客户端并发请求同一key,都查询数据库并更新缓存,增加数据库压力且占用缓存资源。
- 解决方案:客户端请求先读缓存,未命中则用redis的setnx方法设置锁定状态,设置成功的请求查询数据库并更新缓存,设置失败的请求等待后重新查询,确保同一时间只有一个请求操作数据库和缓存。
- 缓存雪崩问题及解决方案
- 问题:开发中常将缓存过期时间设为固定常量,大量缓存key同时过期,高并发请求下会使数据库压力瞬间增大,引发缓存雪崩。
- 解决方案:一是随机打散缓存失效时间,在原失效时间基础上加随机值;二是设置缓存不过期,通过后台服务更新缓存数据,可避免雪崩和一定程度上的并发问题。
- 动态缓存热点数据策略设计
- 场景:在电商平台,只缓存用户经常访问的top1000商品。
- 策略:通过缓存系统的排序队列,按商品访问时间排名,定期过滤排名靠后的商品并从数据库读取新商品加入。请求到达时,先从队列获取商品id,再从另一缓存结构读取商品信息。
- 缓存操作与业务分离架构设计
- 问题:缓存操作与业务代码耦合会导致代码可维护性差,不符合高内聚低耦合设计原则。
- 解耦思路:通过mysql binlog、Kafka、MQ实现解耦。如用户添加配置信息到mysql,binlog记录更新,Kafka获取日志解析后通过MQ发送数据,应用系统将MQ数据更新到redis。
- 总结与作业布置
- 总结:推荐预设值方案解决缓存穿透,用redis的setnx方法解决缓存并发,通过随机打散失效时间或设置缓存不过期解决缓存雪崩。同时强调使用缓存要考虑缓存与数据库一致性、缓存容量限制、数据大小等问题。
- 作业:要求用redis实现一个计数器。