news 2026/3/28 2:54:08

后端面经记录

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
后端面经记录

3.那既然括号里的数字,不是用来限制取值范围的那为什么要设置长度呢,我不设置行不行
4.如果表中有INT(11),而插入的是100,你会看到什么样的显示效果?INT(11) 是不是就是存储11位的数字,如果你插入了一个大于99999 的数字(如100000),你认为INT(5) 会如何显示
5.MySQL里面date,datetime,TIMESTAMP有什么区别,假设这个时候我要做一个跨国的系统,那么你觉得选哪个字段会更合适?为什么
6.varcahr最大长度是多少能设置多少就括号里面那个数值
7.唯一索引和普通索引mysql综合读写来看哪个更快
8.唯一索引他是怎么做那个唯一检查的
9.Doublewrite机制了解么
10.mysql 的主从复制它的流程
11Ok那这个时候有问题了,就是说我从库拉到了binlog之后转成Relaylog准备重放的时候,那么它的执行是单线程还是多线程?
12.它怎么执行到区分不同的group去复制呢?
13. 那mysql在做双主复制时,双主它会不会导致循环复制,就是a给BB又给A这种,如果会该怎么去避免这种情况呢
14你基础确实掌握的挺好的,然后我来问一下你设计相关的,就像我们做系统,经常有那个菜单树的概念,比如左边有个菜单有很多层级。对吧,那现在我不设置,比如最深要求多少级,如果说让你设置一个菜单表,尽可能的少的层级也不要care他的性能的前提下,最少需要多少列?
15如果我这时候又要考虑性能呢?比如说我有两个场景,第一个场景是我选择一个父节点父菜单,我能查出全部的子孙然后第二种呢,是我选一个子孙,我能查询出全部,他的父和爷爷以及所有的先祖,你觉得应该怎么设计
16第二个设计题:如果我想要设计一个浏览用户的历史记录,比如说淘宝那种历史记录,我可以看我过去看到的是什么?这个浏览得有两个维度,第一个维度是以个人C端的角度去看这是我自己的历史记录,第二个角度是用供应商的角度去看这个商品被哪些人浏览了,必须要用到分库分表你决定应该怎么设计

问题3:INT括号里的长度为什么设置?不设置行不行?

核心回答

可以完全不设置,且日常开发中建议不设置(因为显示宽度几乎无实际价值)。

  1. 为什么会有这个“长度”(显示宽度)?
    这是 MySQL 的历史设计残留:早期数据库工具(如 mysql 命令行)依赖显示宽度做格式化展示,核心场景只有一个——配合ZEROFILL做零填充补位(比如 INT(5) ZEROFILL 插 123 显示 00123);此外,显示宽度可作为“字段注释”,让维护者直观知道该字段预期显示的位数(比如 INT(5) 暗示该字段存 5 位以内的编号),但仅为可读性,无强制约束。

  2. 不设置的后果?
    不设置时 MySQL 会用默认显示宽度:INT 默认 11,TINYINT 默认 4,SMALLINT 默认 6,MEDIUMINT 默认 9,BIGINT 默认 20。这些默认值仅为“能完整显示该类型所有数值的最小宽度”(比如 INT 最小负数 -2147483648 是 11 位),不影响任何存储/计算逻辑。

总结:显示宽度是“非必要设计”,不设置完全没问题,现代业务中更推荐靠注释说明字段用途,而非依赖显示宽度。

问题4:INT(11)插100的显示效果;INT(11)是否存11位;INT(5)插100000的显示

核心结论(先破误区)

INT(11) 绝对不是“存储11位数字”,INT 类型固定4字节,最多存 10 位正数(2147483647)或 11 位负数(-2147483648),括号内数字仅影响零填充场景的显示

  1. INT(11) 插入 100 的显示效果

    • 不加 ZEROFILL:显示100(和 INT(5)/INT(20) 无区别);
    • 加 ZEROFILL:显示00000000100(补0到11位,且自动转为无符号数)。
  2. INT(5) 插入 100000(6位数字)的显示效果

    • 不加 ZEROFILL:显示100000(位数超过5也正常显示,无截断/报错);
    • 加 ZEROFILL:显示100000(零填充仅补位“不足显示宽度”的情况,超过则原样显示)。

问题5:date/datetime/TIMESTAMP区别;跨国系统选哪个?

第一步:三者核心区别(表格更清晰,面试口述可简化)
特性datedatetimeTIMESTAMP
存储格式‘YYYY-MM-DD’‘YYYY-MM-DD HH:MM:SS’时间戳(秒级,4字节)
取值范围1000-01-01 ~ 9999-12-311000-01-01 00:00:00 ~ 9999-12-31 23:59:591970-01-01 00:00:01 ~ 2038-01-19 03:14:07
时区感知否(纯日期,无时区)否(固定时间,不转换)是(存储时转UTC,查询时转当前会话时区)
存储字节3字节8字节4字节(5.6+支持毫秒,7字节)
自动赋值可设为 CURRENT_TIMESTAMP(更新/插入自动填当前时间)
第二步:跨国系统选型

优先选 TIMESTAMP(需注意范围),其次用 datetime + 应用层时区转换

理由:

  1. 跨国系统核心痛点是时区不一致(比如中国用户(UTC+8)和美国用户(UTC-5)操作同一条数据,时间需自动适配本地时区);
  2. TIMESTAMP 天然支持时区转换:存储时会将会话时区的时间转为 UTC 存到库中,查询时再根据当前会话的时区转回本地时间,无需应用层做复杂的时区计算;
  3. 注意事项:TIMESTAMP 范围有限(到2038年),如果业务需要存储超过2038年的时间,可改用datetime+ 应用层维护时区(比如额外加一个timezone字段记录用户时区,查询时应用层转换)。

问题6:varchar最大长度(括号内的数值)

核心结论:分“理论最大值”和“实际可用值”,核心受3个因素限制
  1. 底层限制:MySQL 规定行的最大字节数为 65535(所有列的总字节,含字段长度标识、NULL标记等),varchar 本身的“长度”是字符数,但存储时占用“字符字节数 + 1/2字节(长度标识)”。
  2. 分场景说明
    • 字符集影响:
      • utf8(3字节/字符):理论最大字符数 = (65535 - 2) / 3 ≈ 21844(减2是长度标识);
      • utf8mb4(4字节/字符):理论最大字符数 = (65535 - 2) / 4 ≈ 16383;
    • InnoDB 额外限制:
      • 若 varchar 是表的唯一可变长度列,且行格式为 COMPACT/DYNAMIC,超过768字节的 varchar 会存到溢出页,此时“实际常用值”:utf8mb4 下建议设 191(避免索引长度超限),utf8 下建议设 255;
    • 括号内数值:直接填“字符数”(如 varchar(20) 表示存20个字符,无论utf8/utf8mb4)。

总结:括号内可设的最大值受字符集和行总字节限制,utf8mb4 下理论最大16383,实际开发中建议按业务需求设(如手机号设11,姓名设50),而非拉满。

问题7:唯一索引 vs 普通索引 综合读写速度

核心结论:读几乎无差,写普通索引更快,综合看普通索引更优(写多场景)
  1. 读性能:几乎无区别(唯一索引微优,但可忽略)

    • 普通索引:找到匹配行后,会继续往后扫一小段(确认是否有下一个匹配行);
    • 唯一索引:找到匹配行后直接返回(无需确认下一行);
    • 实际场景中,这个差异极小(尤其是InnoDB的聚簇索引结构),用户感知不到。
  2. 写性能:普通索引显著更快

    • 唯一索引:写入/更新时,必须先做“唯一性检查”(加锁+索引查找),且无法使用“变更缓冲(Change Buffer)”(变更缓冲仅对普通索引生效,可延迟写入磁盘,提升写性能);
    • 普通索引:无需唯一性检查,可利用变更缓冲,写操作的磁盘IO更少。
  3. 综合读写

    • 读多写少:两者几乎无差,唯一索引可避免重复数据,略优;
    • 写多读少:普通索引优势明显,综合速度更快。

问题8:唯一索引的唯一检查机制

核心流程(分插入/更新场景)
  1. 检查时机:写操作(INSERT/UPDATE)的“执行阶段”(而非提交阶段),先检查唯一性,再执行写入。
  2. 检查步骤
    • 步骤1:根据要写入的值,走唯一索引快速查找(B+树查找,O(logn)),确认是否存在相同值;
    • 步骤2:加锁防止并发冲突(关键!避免幻读导致重复):
      • 插入场景:加“间隙锁/Next-Key锁”(隔离级别≥RR),锁定要插入的位置,防止其他会话同时插入相同值;
      • 更新场景:先锁定旧值行,再检查新值的唯一性;
    • 步骤3:若找到重复值,直接返回报错(1062 Duplicate entry);若未找到,执行写入。
  3. 特殊情况
    • 批量插入(INSERT … VALUES (…)):逐行检查唯一性,一行失败则整个语句回滚(5.7+可设innodb_duplicate_key_error_is_recoverable=1跳过错误行);
    • 联合唯一索引:检查所有索引列的组合值是否唯一。

问题9:Doublewrite机制(双写缓冲区)

核心:解决InnoDB“部分写页”崩溃问题(比如写页时断电,页只写了一半)
  1. 为什么需要:InnoDB 页大小默认16KB,写入磁盘时若只写了一部分(如8KB),页会损坏,且redo log仅记录“页的修改”,无法修复损坏的页。
  2. 核心流程
    • 步骤1:InnoDB 准备将内存中的脏页刷到磁盘时,先把页内容写入“双写缓冲区”(内存中2MB,对应磁盘共享表空间的doublewrite区,连续的128个页);
    • 步骤2:双写缓冲区刷到磁盘(顺序写,性能高),确保页完整写入;
    • 步骤3:再将双写缓冲区的页刷到数据文件的对应位置(随机写);
    • 步骤4:崩溃恢复时,检查数据文件的页是否完整:若完整则正常恢复;若损坏,从doublewrite区读取完整页覆盖,再用redo log恢复数据。
  3. 性能影响:看似多写一次,但doublewrite区是顺序写,实际性能损耗仅5%-10%,换来了页的完整性。

问题10:MySQL主从复制流程

核心三步(面试可分阶段讲,逻辑更清晰)

阶段1:主库生成Binlog

  1. 主库执行增删改操作,事务提交时,将修改记录按顺序写入Binlog(二进制日志);
  2. 主库维护一个 dump 线程,等待从库连接并请求Binlog。

阶段2:从库拉取Binlog

  1. 从库启动 IO 线程,连接主库的 dump 线程;
  2. 从库向主库请求“指定位置后的Binlog”,主库 dump 线程推送Binlog到从库;
  3. 从库 IO 线程将收到的Binlog写入本地 Relaylog(中继日志),并记录主库的Binlog位置(master_log_file + master_log_pos)。

阶段3:从库重放Relaylog

  1. 从库启动 SQL 线程,读取Relaylog中的日志事件;
  2. 按顺序执行日志中的增删改操作,还原主库的数据,保持主从数据一致。

总结:主库(Binlog + dump线程)→ 从库IO线程(拉取→写Relaylog)→ 从库SQL线程(重放Relaylog)。

问题11:Relaylog重放是单线程还是多线程?

核心:分MySQL版本,从单线程进化到多线程
  1. 5.6及之前:单线程(SQL线程)→ 性能瓶颈(主库高并发时,从库重放慢,导致主从延迟);
  2. 5.6:支持“基于库的并行复制”→ 多线程(每个库一个线程),但如果所有写操作都在一个库,还是单线程;
  3. 5.7:支持“基于GTID的并行复制”(逻辑时钟)→ 无冲突的事务可并行执行(按事务的提交顺序,同一组的事务并行);
  4. 8.0:支持“基于写集(Write Set)的并行复制”→ 更智能,按事务修改的数据行哈希分组,无冲突的事务跨库/跨表并行,性能大幅提升。

总结:现代MySQL(5.7+)默认多线程重放,老版本单线程。

问题12:如何区分不同group复制?(MySQL Group Replication,MGR)

核心:基于“事务的冲突检测”和“逻辑时钟/写集”分组
  1. 核心原理:MGR 是分布式一致性复制(Paxos协议),将事务分为“可并行”和“需串行”的组:
    • 步骤1:每个事务提交前,生成“写集”(Write Set,即事务修改的行的哈希值);
    • 步骤2:节点间交换写集,检测事务是否冲突(比如两个事务修改同一行,则冲突);
    • 步骤3:无冲突的事务分到同一个“组”(Logical Clock),可并行执行;有冲突的事务分到不同组,串行执行。
  2. 传统并行复制(非MGR)
    • 5.6:按库分组(不同库的事务并行);
    • 5.7:按GTID的“事务序列号”分组,同一逻辑时钟的事务并行。

总结:核心是“冲突检测”,无冲突的事务归为同一group并行,冲突的串行。

问题13:双主复制的循环复制及避免

第一步:会导致循环复制!

双主复制(A←→B)中,A的Binlog同步到B,B执行后生成新的Binlog,又会同步回A,A执行后再同步到B,无限循环。

第二步:避免方案(核心是“识别并丢弃自己生成的Binlog”)
  1. 核心方案:设置唯一的server_id + 过滤自身server_id的Binlog
    • 给主库A和B设置不同的server_id(如A=1,B=2);
    • 开启log_slave_updates(从库执行Relaylog后,将操作写入自己的Binlog)时,主库收到Binlog后,先检查Binlog中的server_id:如果是自己的server_id,则丢弃该Binlog,不执行;
  2. 辅助方案1:使用GTID
    MySQL 5.6+的GTID(全局事务ID)包含server_id,从库执行Relaylog时,会记录已执行的GTID,若收到的Binlog的GTID已存在,则跳过执行,天然避免循环;
  3. 辅助方案2:业务层面隔离
    比如A主库写奇数ID的数据,B主库写偶数ID的数据,Binlog同步后,对方执行时无匹配数据,不会产生新的Binlog。

问题14:菜单树表 最少列(不考虑性能)

核心:最少3列(满足任意层级,无性能要求)
列名类型说明
idBIGINT菜单主键(唯一标识)
menu_nameVARCHAR(50)菜单名称(必选,展示用)
parent_idBIGINT父菜单ID(顶级菜单设为0或NULL)

理由

  • id:唯一标识每个菜单,是层级关联的基础;
  • menu_name:菜单的核心业务属性,必须有;
  • parent_id:通过“子菜单的parent_id=父菜单的id”维护层级关系,无论多少层,仅靠这一列就能递归遍历(比如顶级菜单parent_id=0,一级菜单parent_id=0,二级菜单parent_id=一级菜单id,以此类推);
  • 无其他必选列:比如排序、状态等属于业务扩展,题目要求“最少列”,可省略。

问题15:菜单树表 考虑性能(查子孙/查先祖)

核心方案:放弃纯递归,改用“物化路径法”(最优平衡方案)
第一步:表结构设计(新增1列,共4列)
列名类型说明
idBIGINT菜单主键
menu_nameVARCHAR(50)菜单名称
parent_idBIGINT父菜单ID(兼容旧逻辑)
pathVARCHAR(1000)物化路径(如“0,1,5,8”)

path字段规则

  • 用分隔符(如逗号)拼接所有祖先ID,从根到当前菜单;
  • 顶级菜单path=“0”;
  • 一级菜单(父=0)path=“0,1”;
  • 二级菜单(父=1)path=“0,1,5”;
  • 三级菜单(父=5)path=“0,1,5,8”。
第二步:满足两个性能场景
  1. 查父节点的所有子孙
    SQL:SELECT * FROM menu WHERE path LIKE CONCAT(#{parentPath}, ',%')
    示例:查父节点1的子孙 →path LIKE '0,1,%',直接命中所有子、孙、曾孙,无需递归,一次查询搞定;
  2. 查子孙的所有先祖
    SQL:拆分path字符串,按分隔符分割后,直接查询所有ID对应的菜单;
    示例:path=“0,1,5,8” → 拆分出[0,1,5,8],查询id in (0,1,5) 就是所有先祖,拆分可在应用层做(如Java用split),或用MySQL的SUBSTRING_INDEX函数。
进阶方案(极致性能):闭包表

如果数据量极大,可新增“闭包表”(menu_closure),存储所有祖先-后代关系:

列名类型说明
ancestor_idBIGINT祖先菜单ID
descendant_idBIGINT后代菜单ID
depthINT祖先到后代的层级(可选)
  • 查子孙:SELECT descendant_id FROM menu_closure WHERE ancestor_id=#{parentId}
  • 查先祖:SELECT ancestor_id FROM menu_closure WHERE descendant_id=#{childId}
  • 缺点:新增/删除菜单时,需维护闭包表(比如新增二级菜单,要给所有祖先添加一条关联记录),但查询性能极致。

问题16:用户浏览历史记录 分库分表设计(C端+供应商端)

核心思路:双维度分片 + 双写保证一致性
第一步:核心诉求拆解
  • C端:按用户ID查自己的浏览记录(用户维度,高频读);
  • 供应商端:按商品ID查浏览用户(商品维度,低频读,需全局视角);
  • 分库分表:解决单表数据量过大(如亿级浏览记录)的问题。
第二步:表结构设计(基础表)
CREATETABLEuser_browse_record(idBIGINTNOTNULLAUTO_INCREMENTCOMMENT'主键',user_idBIGINTNOTNULLCOMMENT'用户ID(C端分片键)',goods_idBIGINTNOTNULLCOMMENT'商品ID(供应商端分片键)',supplier_idBIGINTNOTNULLCOMMENT'供应商ID(快速过滤)',browse_timeDATETIMENOTNULLCOMMENT'浏览时间',goods_nameVARCHAR(100)COMMENT'商品名称(冗余,避免联表)',PRIMARYKEY(id),KEYidx_user_time(user_id,browse_time)COMMENT'C端查询索引',KEYidx_goods_time(goods_id,browse_time)COMMENT'供应商查询索引')ENGINE=InnoDBDEFAULTCHARSET=utf8mb4;
第三步:分库分表策略
  1. C端维度(优先)
    • 分片键:user_id;
    • 分片规则:user_id % 分库数 = 库号,user_id % 分表数 = 表号;
    • 优势:用户查自己的历史记录时,直接定位到对应的库表,查询速度极快;
  2. 供应商端维度
    • 方案1:双写(推荐)→ 写入C端库表的同时,通过消息队列(如RocketMQ)异步写入“商品维度”的分库分表(分片键=goods_id);
    • 方案2:全局索引 → 用Elasticsearch存储“goods_id → user_id列表”,供应商查询时先查ES,再到C端库表拉取详情;
    • 优势:供应商查商品浏览记录时,直接定位到商品维度的库表,无需全库扫描。
第四步:细节补充
  • 过期数据:定时清理(如超过3个月的记录),避免表过大;
  • 读写分离:分库分表后,读请求走从库,写请求走主库;
  • 一致性:双写时用消息队列保证最终一致,失败重试;
  • 分片数:分库数建议8/16,分表数建议64/128(避免后续扩容麻烦)。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/27 19:09:19

【课程设计/毕业设计】基于springboot+微信小程序的羽球快讯爱好者平台小程序 体育资讯软件平台【附源码、数据库、万字文档】

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围:&am…

作者头像 李华
网站建设 2026/3/27 9:59:56

计算机毕业设计springboot社交网络平台 基于SpringBoot的校园生活互动平台 面向高校用户的SpringBoot在线社交与活动组织系统

计算机毕业设计springboot社交网络平台vhhgy6uu (配套有源码 程序 mysql数据库 论文) 本套源码可以在文本联xi,先看具体系统功能演示视频领取,可分享源码参考。当“点赞”成为日常仪式,“活动”从线下搬到指尖,校园里的…

作者头像 李华
网站建设 2026/3/27 15:31:51

【前端知识点总结】请求/响应拦截器的介绍

在现代前端应用中,与后端服务的 HTTP 通信是项目的命脉。我们频繁地发起请求、处理响应。但如果每个请求都需要手动处理通用逻辑(如添加 token、错误处理),代码将变得冗余、难以维护。这时,拦截器便应运而生&#xff0…

作者头像 李华
网站建设 2026/3/27 16:37:43

零基础使用网络安全工具的方法

第❶步:工具认知(第1个月)- 别被工具吓倒,先当“普通软件”用核心心态:忘掉“黑客工具”的标签,把它们看作帮你完成特定任务的“瑞士军刀”。必装三件套(虚拟机环境内操作)&#xff…

作者头像 李华
网站建设 2026/3/27 3:42:10

校园人体工学深度解析:固定高度课桌椅如何成为学生“隐形推手”

引言在现代化校园建设中,标准化的管理模式往往被视为高效与秩序的象征。为了追求视觉上的整齐划一,许多学校在教室家具配置上采取了“一刀切”的策略:无论班级里的学生身高是1.2米还是1.6米,配备的课桌椅高度往往是固定的。这种为…

作者头像 李华
网站建设 2026/3/27 9:11:42

Vu3 打包问题

Vu3 打包问题 npm run build 时出现原因 TS 验证比较严格 解决方案 :在tsconfig.app。json中添加 “exclude”: [“node_modules/unplugin-element-plus/dist/vite.d.ts”]彻底解决 在package.json 中添加 “type-check”: “echo “Skipping type check””, c…

作者头像 李华