会用数据库,找大厂工作是远远不够的。
本人2025美团暑期AI面试好几个MySQL场景问题不会答,已脏面评。遂在此整理学习!
文章目录
- 分片分区
- [2025 美团AI面](#2025 美团AI面)
-
- 1.数据库分片sharding的概念,它有什么优势和挑战?
-
-
- 优势
- [Sharding 挑战](#Sharding 挑战)
-
- [2. 分库分表的常见策略有哪些](#2. 分库分表的常见策略有哪些)
-
-
- [a. 取模分片](#a. 取模分片)
- [b. 范围分片](#b. 范围分片)
- [c. 按业务](#c. 按业务)
- [d. 按时间分](#d. 按时间分)
- [e. 混合分片](#e. 混合分片)
-
- 3.数据库分页,千万级数据,如何解决深度分页的效率问题
-
-
- [a. 避免 OFFSET 直接跳过大量数据](#a. 避免 OFFSET 直接跳过大量数据)
- [b. 分区优化查询](#b. 分区优化查询)
- [c. 缓存分页结果](#c. 缓存分页结果)
-
- [4. 如果一条SQL查询变慢,你的优化步骤是什么?](#4. 如果一条SQL查询变慢,你的优化步骤是什么?)
分片分区
-
分片 Sharding 是跨多个物理机器,把数据拆分到不同的服务器上
-
分区 Partitioning 是在单个数据库内部,把一张大表拆成多个物理存储的分区。
对用户仍然表现为一张表,查询时数据库自动优化查询范围。
分区语法
PARTITION BY
范围分区 (RANGE Partitioning)
-- 创建一个按范围分区的表
CREATE TABLE sales (
id INT,
sale_date DATE,
amount DECIMAL(10, 2)
)
PARTITION BY RANGE (YEAR(sale_date)) (
PARTITION p2020 VALUES LESS THAN (2021),
PARTITION p2021 VALUES LESS THAN (2022),
PARTITION p2022 VALUES LESS THAN (2023),
PARTITION p2023 VALUES LESS THAN (2024),
PARTITION p2024 VALUES LESS THAN (2025),
PARTITION p_other VALUES LESS THAN MAXVALUE
);
列表分区(LIST Partitioning)
根据指定列是否为特定值,分区
-- 创建一个按列表分区的表
CREATE TABLE products (
id INT,
category VARCHAR(50),
price DECIMAL(10, 2)
)
PARTITION BY LIST (category) (
PARTITION p_electronics VALUES IN ('Laptop', 'Smartphone', 'Tablet'),
PARTITION p_clothing VALUES IN ('T-Shirt', 'Jeans', 'Dress'),
PARTITION p_home VALUES IN ('Furniture', 'Appliance')
);
哈希分区(HASH Partitioning)
适用于数值类型
不强制要求主键或索引
-- 创建一个按哈希分区的表
CREATE TABLE orders (
id INT,
order_date DATE,
total_amount DECIMAL(10, 2)
)
PARTITION BY HASH (id)
PARTITIONS 4;
键分区(KEY Partitioning)
使用的 MySQL 内部的哈希函数,适用于任何数据类型(VARCHAR),允许 多个列组合 作为 KEY。
MySQL 要求分区键是主键或唯一索引
(经过分析,必要性只体现在数据分布均匀和查询效率上。)
-- 创建一个按键分区的表
CREATE TABLE customers (
id INT,
name VARCHAR(100),
email VARCHAR(100)
)
PARTITION BY KEY (id)
PARTITIONS 3;
子分区
SUBPARTITION
先分一步,再分第二步
-- 创建一个带有子分区的表
CREATE TABLE events (
id INT,
event_date DATE,
event_type VARCHAR(50)
)
PARTITION BY RANGE (YEAR(event_date))
SUBPARTITION BY HASH (MONTH(event_date))
SUBPARTITIONS 2 (
PARTITION p2023 VALUES LESS THAN (2024),
PARTITION p2024 VALUES LESS THAN (2025)
);
2025 美团AI面
1.数据库分片sharding的概念,它有什么优势和挑战?
我当时甚至不清楚分片啥意思------其实就是分布式数据库,将数据拆分到多个数据库节点上。
每个数据库节点存储数据的一个子集,被称为分片(Shard)。
优势
-
水平扩展,能够支持海量数据
-
提高性能:
- 多个数据库节点可以并行处理查询和写入请求,减少单个数据库的负载。
- 每个分片只存储部分数据,索引体积更小,提高查询效率。
- 隔离
单个分片出现故障,不会影响其他分片的数据
Sharding 挑战
- 怎么分?
需要选择合适的分片键(Shard Key),否则:
数据倾斜:部分分片存储的数据量过大,导致查询压力集中,影响性能。
查询跨分片:如果查询涉及多个分片(如 JOIN、GROUP BY),可能导致性能下降。
- 维护
随着数据增长,可能需要重新分片。数据迁移过程复杂,可能影响在线业务。
需要额外的工具监控各个分片的健康状况,防止单点瓶颈。
- 跨分片问题
联合只能在应用层。
跨分片事务,需要引入两阶段提交(2PC)等机制。而且要保证数据一致性。
2. 分库分表的常见策略有哪些
延续上一个问题了,,
a. 取模分片
num % 3 == 0/1/2
✅ 数据均匀
❌ 拓展困难,模数想跳转只能重新计算。
范围查询表现不佳
b. 范围分片
每10W个编号分到一个数据库中
✅ 支持数据查询,扩展方便
❌ 数据倾斜风险:可能部分连续数据访问量大,分片压力大
c. 按业务
常规,不要宽列
d. 按时间分
日志,订单等
e. 混合分片
先分用户表,再对其取模分片,再按时间分
3.数据库分页,千万级数据,如何解决深度分页的效率问题
a. 避免 OFFSET 直接跳过大量数据
假设 id 有索引,获取 id 1000000 ~ 1000020
SELECT id, name, age FROM users
ORDER BY id LIMIT 1000000, 20;
这里即使跳过了前100万行,仍需扫描其他列,导致类似回表的性能开销。
(除非这三列是联合索引)
利用子查询索引加速分页:
SELECT id, name, age FROM users
WHERE id > (SELECT id FROM users ORDER BY id LIMIT 1000000, 1)
ORDER BY id LIMIT 20;
子查询 SELECT id FROM users ORDER BY id LIMIT 1000000, 1 仅需访问主键索引(覆盖索引),无需回表。
如果 id 是递增,可以不使用 OFFSET:
SELECT * FROM users
WHERE id > 5000000 -- 这个值从前一页查询获取
ORDER BY id
LIMIT 20;
b. 分区优化查询
(分区不是分片,见1.)
PARTITION BY RANGE (created_at)
指定了按 created_at 进行范围分区
CREATE ... PARTITION OF [table] FOR VALUES FROM
CREATE TABLE users (
id BIGINT NOT NULL,
name VARCHAR(255),
age INT,
created_at TIMESTAMP,
PRIMARY KEY (id, created_at)
) PARTITION BY RANGE (created_at);
CREATE TABLE users_2024 PARTITION OF users FOR VALUES FROM ('2024-01-01') TO ('2025-01-01');
c. 缓存分页结果
适用于只读或更新较少的数据。
CREATE ... AS
CREATE TABLE user_pagination_cache AS
SELECT id FROM users ORDER BY id LIMIT 10000000;
4. 如果一条SQL查询变慢,你的优化步骤是什么?
保存 复现语句,看是特定查询慢还是整个库慢
EXPLAIN
EXPLAIN 关键字可用于分析 SQL 查询的执行计划,了解数据库是如何执行查询的:表的访问顺序、使用的索引、数据扫描方式
EXPLAIN SELECT * FROM users WHERE age > 25;
-
优化查询语句,在多表连接查询中,合理安排表的连接顺序,让数据库先连接小表,减少中间结果集的大小。
-
避免在查询条件中使用函数或表达式,因为这可能会导致索引失效。
-- 不好的写法,索引可能失效
SELECT * FROM users WHERE YEAR(birth_date) = 1990;
-- 好的写法,可使用索引
SELECT * FROM users WHERE birth_date BETWEEN '1990-01-01' AND '1990-12-31'; -
确保数据库的数据文件和日志文件存储在高性能的磁盘上,避免磁盘 I/O 成为性能瓶颈
-
确保表的结构合理,避免数据冗余和过度规范化