面试题答案
一键面试1. 表结构设计
假设我们有如下表结构来记录员工工作时间变化:
CREATE TABLE employee_work_time (
id INT AUTO_INCREMENT PRIMARY KEY,
employee_id INT NOT NULL,
work_period PERIOD NOT NULL,
work_hours DECIMAL(5, 2) NOT NULL,
INDEX idx_employee_id (employee_id),
INDEX idx_work_period (work_period)
);
employee_id
:标识员工。work_period
:记录工作时间周期,例如202301
表示2023年1月。work_hours
:该周期内的工作时长。
2. 复杂查询实现
SELECT
YEAR(FROM_PERIOD(work_period)) AS year,
MONTH(FROM_PERIOD(work_period)) AS month,
AVG(work_hours) AS average_work_hours
FROM
employee_work_time
WHERE
work_period >= PERIOD_DIFF(CURRENT_DATE, INTERVAL 1 YEAR) * 100 + 1
GROUP BY
YEAR(FROM_PERIOD(work_period)),
MONTH(FROM_PERIOD(work_period));
上述查询首先通过PERIOD_DIFF
计算出过去一年的起始时间周期,然后筛选出该时间段内的数据,最后按年和月分组并计算平均工作时长。
3. 优化策略
- 索引优化:
- 我们已经在
employee_id
和work_period
字段上创建了索引。在大数据量下,这些索引可以加速WHERE
子句中的条件筛选。例如,当执行WHERE work_period >=...
时,idx_work_period
索引可以快速定位符合条件的记录。 - 如果查询经常需要同时按
employee_id
和work_period
筛选,可以考虑创建复合索引CREATE INDEX idx_employee_period ON employee_work_time(employee_id, work_period);
。这样可以在查询中同时利用这两个字段的过滤条件时,进一步提高查询效率。
- 我们已经在
- 查询缓存:如果查询结果不经常变化,可以开启MySQL的查询缓存。MySQL会缓存查询语句及其结果,下次相同的查询执行时,直接从缓存中返回结果,而不需要再次执行查询。可以通过修改MySQL配置文件(如
my.cnf
)中的query_cache_type
和query_cache_size
参数来启用和配置查询缓存。 - 分区表:当数据量非常大时,可以考虑对表进行分区。例如,按
work_period
进行范围分区,每个分区存储一定时间段的数据。这样在查询过去一年的数据时,MySQL可以只扫描相关的分区,而不是全表扫描,从而大大提高查询性能。
CREATE TABLE employee_work_time (
id INT AUTO_INCREMENT PRIMARY KEY,
employee_id INT NOT NULL,
work_period PERIOD NOT NULL,
work_hours DECIMAL(5, 2) NOT NULL
)
PARTITION BY RANGE (work_period) (
PARTITION p0 VALUES LESS THAN (202301),
PARTITION p1 VALUES LESS THAN (202302),
-- 以此类推,根据实际需求划分更多分区
);
4. MySQL底层存储和查询原理
- 底层存储:MySQL使用存储引擎来管理数据存储。常见的存储引擎如InnoDB,数据以页(Page)为单位进行存储。每个页大小通常为16KB,页内存储数据行和索引项。索引采用B+树结构,B+树的叶子节点包含所有数据行的指针或数据本身(聚簇索引)。在我们的表中,
idx_employee_id
和idx_work_period
索引会以B+树结构存储,使得根据employee_id
或work_period
查找数据时,可以通过树的快速查找算法定位到相关记录。 - 查询原理:当执行查询时,MySQL首先解析SQL语句,生成解析树。然后通过优化器对查询进行优化,决定使用哪些索引、是否进行表连接优化等。在我们的查询中,优化器会根据
WHERE
子句中的条件,判断是否使用idx_work_period
索引。如果使用索引,会从B+树的根节点开始,通过比较索引值快速定位到叶子节点,获取符合条件的记录。然后再进行分组和聚合操作,最终返回结果。