当前位置: 首页 > news >正文

Day08

1. MySQL表中有十个字段,你主键用自增ID还是UUID,为什么?

选择自增 ID 作为主键。因为 InnoDB 的聚簇索引要求数据尽量顺序插入,自增 ID 是单调递增的,写入时总是追加在数据页的尾部,能避免也分裂,提升写入性能。而 UUID 是无序的,每次插入都需要到 B+ 树的不同位置,容易导致以下问题:

  1. 大量随机 IO:目标页不在内存中时,需要频繁从磁盘加载。
  2. 频繁页分裂:新数据插入中间,InnoDB 为腾出空间会分裂数据页。
  3. 数据碎片严重:页的空间利用率降低,查询和维护成本提升。

最终会导致写入性能下降,并影响查询效率。因此,InnoDB 推荐使用单调递增的主键,保持聚簇索引的结构稳定,提高系统整体性能。

2. 为什么自增ID更快一些,UUID不快吗,它在B+树里面存储是有序的吗?

自增 ID 更快,因为是递增的,插入数据时可以顺序写入 B+ 树的末尾,定位快、写入高效,几乎不会触发页分裂,页的填充率也更高。而 UUID 是无序的,每次插入都可能落在索引中间,容易导致频繁页分裂、页面碎片多、写入效率低。此外,UUID 占用更多内存和存储空间,导致索引树更高,查询性能下降。

3. 查询数据时,到了B+树的叶子节点,之后的查找数据是如何做?

  1. 如果是主键索引(聚簇索引):叶子节点就存储了整行数据。查找先从根节点开始,根据主键值一路查找知道叶子节点,叶子节点中直接就存有这一行的全部数据,无需再查找其他地方。
  2. 如果是普通二级索引(非聚簇索引):叶子节点只存储索引列值 + 对应主键的值(回表用)。查找先从二级索引树中查找目标索引值的叶子节点,拿到主键值,再去主键索引那棵树中查找对应主键,回表取出整行数据。

4. 可重复读有没有幻读的问题?

有的。
在标准的 SQL 定义中,可重复读是无法阻止幻读的;但 MySQL 的 InnoDB 引擎下的可重复读默认不会出现幻读,因为它通过 Next-Key Lock(间隙锁)机制进行了额外机制。

比如事务 A 查询金额大于 10 的订单数返回 1,事务 B 插入一条金额为 20 的订单后提交,再次查询时事务 A 会读到新插入的数据,导致结果变成2,发生幻读。

5. MySQL的锁有哪些?

(1)全局锁:使用FLUSH TABLES WITH READ LOCK命令实现,会让整个数据库进入只读状态,阻塞所有写操作,常用于全库备份以保证数据一致性。

(2)表级锁:

  1. 表锁:通过LOCK TABLES加锁,锁住整个表,其他线程无法读写该表。
  2. 元数据锁(MDL):系统自动加锁,保证表结构操作与数据读写互斥。读写操作加读锁(共享),结构修改加写锁(独占)。
  3. 意向锁:在加行锁之前自动加在表上的锁,快速判断是否存在行锁冲突。分为意向共享锁(IS)和意向排他锁(IX)。

(3)行级锁:

  1. 记录锁(Record Lock):锁住某条具体的记录,分为共享锁(S)和排他锁(X),满足读写互斥、写写互斥。
  2. 间隙锁(Gap Lock):锁住一个范围之间的空隙,不包含记录本身,用于防止幻读,仅在可重复读隔离级别下使用。
  3. Next-Key Lock:是记录锁与间隙锁的组合,所著某条记录以及其前后的间隙,避免幻读并保证唯一性约束。

6. 设计一个行级锁的死锁,举一个实际的例子

死锁是指两个或多个事务在执行过程中,因争夺资源而互相等待,导致系统无法继续执行。
场景:事务 A 先更新 id = 1 的记录,再试图更新 id = 2;而事务 B 相反,先更新 id = 2,再试图更新 id = 1。此时两个事务各自持有对方需要的锁,形成互相等待,最终数据库检测到死锁并主动回滚其中一个事务。

7. Mybatis的 # 和 $ 有什么区别?

#会将参数替换为?占位符,生成预编译 SQL,执行时通过 PreparedStatement设置参数,不仅执行效率更高,还能有效防止 SQL 注入,适合传递值。
$是将参数直接拼接进 SQL 字符串中,不具备预编译功能,也无法防止 SQL 注入,适合拼接字段名、表名等结构性内容。

8. 设计一个 SQL 注入,具体说表中的字段,然后 SQL 语句是怎样的?

// 表结构
CREATE TABLE users (id INT PRIMARY KEY AUTO_INCREMENT,username VARCHAR(50),password VARCHAR(50)
);

漏洞SQL语句

String sql = "SELECT * FROM users WHERE username = '" + username + "' AND password = '" + password + "'";
<select id="login" resultType="User">SELECT * FROM users WHERE username = '${username}' AND password = '${password}'
</select>

注入场景:用户在登录页面输入如下内容:

  • 用户名:' OR '1'='1
  • 密码:任意

拼接后 SQL 变成:

SELECT * FROM users WHERE username = '' OR '1'='1' AND password = '任意'

由于'1' = '1'永远为真,这条 SQL 实际执行的是:

SELECT * FROM users WHERE TRUE

等价于全表扫描,绕过了用户名密码校验,成功登录。

正确做法(使用#占位符):

<select id="login" resultType="User">SELECT * FROM users WHERE username = #{username} AND password = #{password}
</select>

这样就能自动使用 PreparedStatement 进行参数绑定,避免拼接 SQL,从而有效防止 SQL 注入。

9. 本地缓存和Redis缓存的区别

本地缓存是指将数据直接存储在应用服务器的内存中,访问速度非常快,延迟低,适合频繁访问、对实时性要求高的场景,如热点数据读取、本地计算中间结果等。但由于本地缓存是“单机私有”的,不具备跨节点共享能力,在分布式部署中可能会造成缓存不一致或数据重复加载问题,同时它受限于本地内存大小,扩展性有限。
分布式缓存是指将数据缓存到独立的缓存服务中,支持多客户端共享访问,具备良好的可扩展性和高并发处理能力。Redis 提供了丰富的数据结构、持久化机制以及分布式支持,适用于大规模数据访问、共享会话、排行榜等场景。缺点是由于访问需要跨网络,性能略慢于本地缓存,同时部署和维护成本相对较高。
因此,在实际应用中,我们往往采用本地缓存 + 分布式缓存双层缓存架构:热点数据优先查本地缓存,未命中则查 Redis,再回源数据库。这种结构兼顾了访问速度、数据一致性和系统可扩展性,是现代微服务架构中常用的缓存策略之一。

10. Redis的Key过期了是立马删除吗?

不是,Redis 的过期删除策略是选择惰性删除 + 定期删除。惰性删除是不主动删除过期键,每次从数据库访问 key 时,都检测 key 是否过期,如果过期则删除该 key。定期删除是每隔一段时间随机从数据库中取出一定数量的 key 进行检查,并删除其中的过期 key。

11. Redis大Key问题是什么?

大 Key 问题是指某个 key 对应的 value 体积或元素数量过大,容易造成阻塞、延迟、内存浪费等问题。常见判定标准包括:字符串类型超过 1MB、集合类元素超过 1 万条,但并无绝对值,应结合具体业务场景、Redis 节点配置和延迟要求综合评估。大 Key 会影响命令执行时长、主从同步、内存淘汰、删除效率等多个方面,需提前规避或拆分处理。

12. 大Key的缺点?

  1. 阻塞主线程,影响性能:Redis 是单线程模型,大 key 的读写操作(如 get, lrange, hgetall, del 等)会占用主线程较长时间,导致其他请求阻塞,影响整体响应速度。
  2. 删除耗时,可能引发卡顿:对大 key 执行 DEL 操作时,Redis 会一次性释放大量内存,造成 CPU 突升或主线程卡顿,甚至导致客户端连接超时。
  3. 主从复制延迟大,影响高可用性:Redis 主从同步是命令级复制,如果主节点操作一个大 key,会导致主从间网络传输大数据包,引发复制延迟甚至阻塞。
  4. AOF 重写/持久化耗时变长:大 key 会增加 AOF 文件的大小,导致持久化时间变长,甚至导致 rewrite 时阻塞或阻塞时间过久。
  5. 内存分布不均,容易触发淘汰或OOM:如果一个 key 占用了大量内存,会导致内存分布不均,触发 Redis 内存淘汰机制不及时或频繁,甚至引发OOM。
  6. 迁移困难,影响扩容和运维操作:Redis 集群中,迁移 slot 时大 key 会导致迁移耗时变长,甚至迁移失败,影响扩容或实例间的负载均衡。

13. Redis的持久化

  1. RDB 是 Redis 的快照机制,会在特定时间点把 Redis 内存中的数据保存成一个二进制文件(.rdb文件)到磁盘中。
  2. AOF 是 Redis 的操作日志机制,会把每一个写操作(如 set、del)都追加记录到日志文件(.aof文件)中,重启时通过“重放操作”恢复数据。
  3. 混合持久化(RDB + AOF):Redis 4.0 之后引入的新机制,将 RDB 的数据快照和 AOF 的操作日志合并到一个文件中,用来兼顾两者的优点。

14. RDB是怎样做出来的?

RDB 是 Redis 提供的一种快照式持久化机制,它会将某一时刻的内存数据整体保存到一个二进制文件中(通常为 dump.rdb),用于在服务重启时快速恢复数据。RDB 的生成可以通过两种方式触发:一是SAVE命令在主线程中执行,直接保存快照,但会阻塞服务;二是BGSAVE命令,Redis 会 fork 出一个子进程在后台生成快照,避免阻塞主线程,是生产环境的推荐方式。BGSAVE利用了操作系统的写时复制(COW)机制,使子进程可以在内存页未变更前共享主线程的数据,从而提高效率。Redis 还可以配置在一定时间内有若干 key 被修改自动触发 BGSAVE。RDB的优点是恢复速度快、文件体积小、适合做全量备份,但其缺点是数据不够实时,一旦 Redis 异常宕机,最后一次快照之后的数据更改将丢失,不适用于对数据完整性要求极高的场景。

15. AOF的写入策略

AOF 通过将写命令追加记录到日志文件中,在服务器重启时可以重新执行这些命令来恢复数据。为了在性能和数据安全之间取得平衡,Redis 提供了三种 AOF 写入策略,通过配置参数 appendfsync 控制。

策略机制说明优点缺点
always每执行一条写命令,就立刻调用 fsync() 将日志写入磁盘。安全性最高,几乎不丢数据性能最差,每次写操作都涉及磁盘 I/O,非常耗资源。
everysec写命令先写入内存缓冲区,每秒钟调用一次 fsync() 刷入磁盘。默认策略,兼顾性能与安全,最多丢失 1 秒数据。在宕机时可能丢失最近 1 秒的数据
no写命令只写入内存缓冲区,由操作系统自行决定何时刷盘(操作系统缓冲区)。性能最好,写入开销低。安全性最低,宕机或断电时可能丢失大量数据,不可控。
项目alwayseverysec(默认)no
刷盘时机每条写命令后立即每秒由后台异步执行一次依赖操作系统内核调度
调用 fsync主线程同步执行后台线程异步执行操作系统自动触发
宕机丢数据不丢最多丢 1 秒数据可能丢很多数据
性能影响最大(同步 IO)较小(异步 IO)最小

相关文章:

  • Hbase
  • Web开发实战:HTML+CSS+JS期末复习全梳理
  • 设计模式——抽象工厂设计模式(创建型)
  • BFD 基本工作原理与实践:如何与 VRRP 联动实现高效链路故障检测?
  • 使用PowerBI个人网关定时刷新数据
  • Springcloud Alibaba自定义负载均衡详解
  • ESP8266常用指令
  • Kerberos面试内容整理-会话密钥的协商与使用
  • 华为OD机试真题——生成哈夫曼树(2025A卷:100分)Java/python/JavaScript/C/C++/GO六种最佳实现
  • 华为OD机试真题——模拟消息队列(2025A卷:100分)Java/python/JavaScript/C++/C语言/GO六种最佳实现
  • 工业物联网中的事件驱动采样架构及优化
  • 墨香阁小说阅读前端项目
  • 基于Sqoop的MySQL-Hive全量/增量同步解决方案(支持多表批量处理
  • 训练中常见的运动强度分类
  • 大语言模型值ollama使用(1)
  • WPS快速排版
  • uni-app学习笔记十六-vue3页面生命周期(三)
  • 【Java】分页工具类
  • Kafka ACK机制详解:数据可靠性与性能的权衡之道
  • 让大模型看得见自己的推理 — KnowTrace结构化知识追踪
  • 最有设计感的网站/守游网络推广平台登陆
  • 徐州城乡建设招投标网站/网站策划书模板
  • 广州网站建设多少钱/seo搜索引擎优化技术
  • 沈阳网络建网站个人/优化设计四年级上册数学答案
  • 长沙做痔疮东大医院L网站/广告宣传
  • 独立站和平台/河南省干部任免最新公示