51mee - AI智能招聘平台Logo
模拟面试题目大全招聘中心会员专区

假设在万兴PDF中,有一个用户文件表(存储用户上传的PDF文件信息),该表每天有数十万条写入,且查询用户文件列表(按时间排序)的请求频繁,如何优化查询性能?请说明索引设计、分库分表、缓存等优化措施。

万兴科技后端开发难度:中等

答案

1) 【一句话结论】:针对用户文件表的高频按时间排序查询,核心优化策略是通过为upload_time字段创建包含user_id的覆盖索引(减少回表与排序开销),结合按天水平分库分表(降低单表数据量与查询负载),并利用Redis缓存热点数据(提升读性能),同时配置读写分离(主从分离,分散查询压力),从而显著提升查询性能。

2) 【原理/概念讲解】:
数据库索引是数据结构的“目录”,能加速数据定位。对于按时间排序查询,时间字段(如upload_time)的B+树覆盖索引(包含upload_time、user_id等查询所需列)能高效处理范围查询,减少磁盘I/O,提升排序性能。
分库分表是将大表拆分为多个小表。当数据量每天数十万写入时,单表性能下降。水平分表(按时间维度拆分,如按天分表),每个分表存储特定时间段的文件,查询时仅扫描对应分表,减少数据量与扫描时间。
缓存(如Redis)将热点数据存储在内存中,响应时间极短(毫秒级)。对于高频查询(如用户文件列表),将查询结果缓存,降低数据库压力。例如,为用户缓存文件列表,key为user_id,过期时间根据更新频率设置(如30分钟),查询时先从缓存获取,若缓存不存在则从数据库查询并更新。

3) 【对比与适用场景】:

优化措施定义特性使用场景注意点
覆盖索引(时间+用户ID)为upload_time和user_id字段创建的B+树索引,包含查询所需所有列(如upload_time、user_id、file_path)支持高效范围查询,减少回表操作,提升排序性能;索引维护成本较低(仅写入时维护)高频时间排序查询(如用户文件列表)避免索引过多影响写入性能;覆盖索引需包含所有查询列
按天水平分库分表将大表按日期拆分为多个小表(表名格式:user_files_YYYYMMDD),每个表存储一天数据减少单表数据量,提升查询与写入性能;数据量增长时动态扩展数据量每天数十万写入,查询按时间范围需处理跨表关联(如外键),分表策略影响查询范围
Redis缓存(用户文件列表)将用户文件列表(按时间排序的文件ID数组)存储在Redis中,key为user_files:user_id内存存储,响应时间低(毫秒级),降低数据库压力高频读操作(用户频繁查看文件列表)需设计缓存更新机制(写时/读时),避免数据不一致;需考虑缓存雪崩(随机过期时间)
读写分离主库负责写入,从库负责查询分散查询压力,提升读性能高频查询场景(如用户文件列表)需保证数据一致性(如事务隔离级别),避免脏读

4) 【示例】:

  • 表结构(MySQL):
    CREATE TABLE user_files (
        user_id INT NOT NULL,
        file_id INT PRIMARY KEY,
        upload_time TIMESTAMP NOT NULL,
        file_path VARCHAR(255),
        INDEX idx_upload_time_user_id (upload_time, user_id)  -- 覆盖索引
    );
    
  • 分库分表策略(按天水平分表):
    每个表存储特定日期的文件数据,例如user_files_20240101存储2024年1月1日上传的文件。查询时,根据upload_time范围选择对应表,例如查询2024年1月1日的文件:
    SELECT * FROM user_files_20240101 
    WHERE user_id = ? AND upload_time BETWEEN '2024-01-01 00:00:00' AND '2024-01-01 23:59:59' 
    ORDER BY upload_time DESC;
    
  • 缓存设计(Redis):
    • 缓存用户文件列表,key为user_files:user_id,value为按upload_time降序排列的文件ID数组(JSON格式),过期时间30分钟。
    • 写时更新:数据库写入新文件时,通过Redis事务(MULTI/EXEC)或消息队列(如Kafka)异步更新缓存。
    • 读时更新:查询时先检查Redis,若缓存存在则直接返回,否则从数据库查询并更新缓存(设置缓存互斥锁,避免并发更新冲突):
      # 缓存内容示例(JSON数组,文件ID按时间降序)
      user_files:1001
      # 值:[123, 124, 122] (假设123,124,122按upload_time降序排列)
      

5) 【面试口播版答案】:
面试官您好,针对用户文件表的高频按时间排序查询优化,我的思路是:首先,为upload_time字段创建覆盖索引(包含user_id),因为时间排序属于范围查询,索引能大幅减少回表与排序的I/O开销;其次,采用按天水平分库分表,表名带日期后缀(如user_files_20240101),每个表存储一天数据,查询时仅扫描对应表,降低数据量;然后,用Redis缓存用户文件列表,key为用户ID,过期30分钟,写时通过Redis事务异步更新缓存,读时检查缓存并异步补全;最后,配置读写分离,主库写入,从库查询,分散查询压力。这样通过索引、分表、缓存、读写分离,整体提升查询性能。

6) 【追问清单】:

  • 问:为什么选择按天分表而不是按月或按用户分表?
    答:按天分表更贴合查询场景(按时间排序),且时间维度更稳定,分表后每个表数据量均衡,查询时能快速定位时间范围内的数据;按月分表会导致查询时扫描多个表,按用户分表则用户数据不均衡,部分表负载过高。
  • 问:如何处理缓存更新时的并发一致性问题?
    答:采用写时更新(数据库写入时通过Redis事务或消息队列异步更新缓存),或读时更新(查询时先检查缓存,若缓存过期则从数据库查询并更新,设置缓存互斥锁避免并发更新冲突)。
  • 问:分库分表后,关联用户表(如通过user_id外键)的跨表查询如何处理?
    答:采用外键分片(将用户表按相同分片键分表,与文件表保持关联),或使用分布式连接(如通过user_id关联,查询时从对应分表获取数据),确保跨表查询的一致性。

7) 【常见坑/雷区】:

  • 索引失效:未为时间字段创建索引,或覆盖索引未包含所有查询列(如缺少file_path),导致索引失效;或创建过多索引影响写入性能。
  • 分表策略不当:按时间分表但范围过大(如按月分表),导致查询时扫描多个表;或按用户分表但用户数据不均衡,部分表负载过高。
  • 缓存更新机制错误:未设置写时/读时更新,导致缓存与数据库数据不一致;或缓存过期时间设置不当,引发缓存雪崩。
  • 忽略读写分离:未配置从库,查询压力仍集中在主库,无法提升读性能。
  • 跨表查询处理不当:分库分表后,外键关联未考虑分片,导致跨表查询失败或数据不一致。
51mee.com致力于为招聘者提供最新、最全的招聘信息。AI智能解析岗位要求,聚合全网优质机会。
产品招聘中心面经会员专区简历解析Resume API
联系我们南京浅度求索科技有限公司admin@51mee.com
联系客服
51mee客服微信二维码 - 扫码添加客服获取帮助
© 2025 南京浅度求索科技有限公司. All rights reserved.
公安备案图标苏公网安备32010602012192号苏ICP备2025178433号-1