1) 【一句话结论】针对百万级作者、作品及用户行为数据,采用“垂直分库+水平分表”策略,以作者ID/用户ID为分表键(范围分片+时间分片),结合覆盖索引、联合索引优化查询,通过批量写入、异步队列优化写入性能,并引入缓存与分布式事务保障性能与一致性。
2) 【原理/概念讲解】分库分表是数据库拆分技术。分库:按业务模块拆分数据库实例(如作者库、作品库、行为库),降低单库压力;水平分表:在单库内按数据量或规则拆分表(如按ID范围、时间)。类比:分库像把大仓库分成作者区、作品区、行为区,水平分表像在作者区按作者ID从1-100万、100万+分两个货架。索引策略:主键自增(唯一标识,支持快速插入);覆盖索引(包含查询所需所有字段,减少I/O,如作者ID查作品时,覆盖索引包含author_id和work_id,直接返回结果,无需回表);联合索引(按查询条件组合,提升多条件查询效率,如用户ID查阅读历史时,联合索引包含user_id和action_time,快速排序)。写入性能优化:批量插入(如MyBatis-Plus的batchInsert)和异步写入(如Kafka缓冲),减少单次写入开销,提升吞吐量。
3) 【对比与适用场景】
分库分表类型对比(垂直分库 vs 水平分表):
| 方案类型 | 定义 | 特性 | 使用场景 | 注意点 |
|---|
| 垂直分库 | 按业务模块拆分表(作者表、作品表、行为表分别在不同库) | 业务关联弱,单表数据量小 | 业务模块独立性强(如作者管理、作品管理) | 需跨库查询,关联复杂 |
| 水平分表 | 在单库内按数据量/规则拆分表(如按ID范围、时间) | 单表数据量增大,查询范围缩小 | 数据量大(如百万级作品) | 需选择合适的分表键(避免数据倾斜) |
分表策略对比(范围分片 vs 时间分片):
- 范围分片:按ID范围(如作者ID 1-100万、100万+)分表,适合ID有序增长的业务。
- 时间分片:按时间(如按月、按天)分表(如行为表_202401),适合日志类数据,便于归档。
4) 【示例】
表结构设计:
- 作者表(author):author_id (INT, 主键, 自增), name (VARCHAR), status (INT)
- 作品表(work):work_id (INT, 主键, 自增), author_id (INT, 外键), title (VARCHAR), publish_time (DATETIME)
- 用户行为表(user_action):user_id (INT, 主键, 自增), work_id (INT, 外键), action_type (INT, 1=阅读, 2=收藏), action_time (DATETIME)
分库分表方案:
- 作者表和作品表按author_id范围分库:库A(作者ID 1-100万,对应作品库A),库B(作者ID 100万+,对应作品库B)。
- 用户行为表按user_id范围分库:库C(用户ID 1-100万),库D(用户ID 100万+)。
- 用户行为表按时间分表:库C下分表user_action_202401(2024年1月数据),库C下分表user_action_202402(2024年2月数据)。
索引设计:
- 作者表:主键索引(author_id)
- 作品表:主键索引(work_id),外键索引(author_id)
- 用户行为表:主键索引(user_id, work_id, action_time),联合索引(user_id, action_time)
查询优化示例:
- 作者ID查询作品列表:SELECT work_id, title FROM work WHERE author_id = ?(覆盖索引包含author_id和work_id,无需回表)
- 用户ID查询阅读历史:SELECT work_id, action_time FROM user_action WHERE user_id = ? AND action_type = 1 ORDER BY action_time DESC(联合索引(user_id, action_time)覆盖查询条件,快速排序)
5) 【面试口播版答案】面试官您好,针对百万级作者、作品和用户行为数据,我设计如下方案:首先分库分表,作者表和作品表按作者ID范围分库(比如库A存ID1-100万,库B存100万+),用户行为表按用户ID范围分库,同时按时间分表(如按月);然后索引策略,作者表主键+覆盖索引(如author_id, name),作品表主键+外键索引,用户行为表主键+联合索引(user_id, time);接着优化写入性能,采用批量插入(如MyBatis-Plus批量操作)和异步写入(如Kafka缓冲),提升吞吐量;最后查询优化,作者ID查作品用覆盖索引(author_id, work_id),用户ID查阅读历史用联合索引(user_id, time),结合Redis缓存作者-作品关联和Seata分布式事务保障性能与一致性。
6) 【追问清单】
- 问题1:分库分表的具体实现细节(如分片键的选择依据、分片规则)?回答要点:分片键选择作者ID(作品关联)、用户ID(行为关联),范围分片(作者ID)和时间分片(行为表)结合,动态调整分片规则避免数据倾斜(如作者ID增长不均匀时,增加新分片)。
- 问题2:数据一致性如何保证?回答要点:采用Seata分布式事务(两阶段提交)保证强一致性(如作者信息变更),用户行为表允许最终一致性(如阅读记录允许短时延迟),结合缓存提升读取性能。
- 问题3:跨库查询如何优化?回答要点:通过雪花算法保证ID唯一性,使用连接查询(JOIN)或分步查询(先查作者表,再查作品表),结合Redis预取关联数据减少JOIN延迟。
7) 【常见坑/雷区】
- 坑1:分库分表导致关联查询复杂,未考虑跨库查询性能。反问:如何处理跨库关联查询?
- 坑2:分表键选择不当(如按作者ID范围分表,但作者ID增长不均匀),导致数据倾斜。反问:如何避免分表后的数据倾斜?
- 坑3:未优化写入性能(如单表写入量过大),导致写入瓶颈。反问:如何优化写入性能?
- 坑4:数据一致性未考虑(如用户行为表写入延迟),影响业务体验。反问:如何保证用户行为数据的实时性?