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

假设有一个高并发写场景的数据库表(如订单表),每秒写入数千条数据,请设计索引策略、分库分表方案,并说明如何优化事务性能(如减少锁竞争)。

信步科技研发难度:中等

答案

1) 【一句话结论】
针对高并发写订单表,核心设计是分库分表(水平分库用全局ID+时间分表,解决自增冲突)+ 复合索引(按查询条件顺序优化,提升多列查询效率)+ 乐观锁(带版本号+重试机制)+ 读已提交隔离级别,通过分散写入压力、减少锁竞争并解决数据一致性问题,提升事务性能。

2) 【原理/概念讲解】
老师:咱们先拆解几个关键点,别空谈理论。

  • 分库分表与全局ID:水平分库若用自增主键哈希分库,会导致跨库自增冲突(如库1和库2都生成相同ID),需用全局ID(如雪花算法,包含时间、机器ID、序列号),保证唯一性,同时按时间分表(如按月)避免单表数据过大。
  • 复合索引列顺序:复合索引的列顺序影响查询效率,应将查询条件列放在最前(如查询order_id和order_status时,索引为order_id+order_status,能高效定位;若顺序颠倒,如order_status+order_id,查询order_id时索引失效,导致全表扫描)。
  • 乐观锁与重试机制:乐观锁通过版本号判断冲突,更新时检查版本是否一致,若不一致则重试(如设置最大重试次数,避免死循环,比如最多重试3次,失败则报错)。
  • 缓存失效策略:缓存订单状态时,用TTL(如5分钟)或更新机制(如更新时同时更新缓存),避免缓存与数据库不一致(如缓存过期后读数据库,但需考虑一致性,比如读时检查数据库状态)。

类比:全局ID像给每个订单一个唯一身份证号,避免分库后ID冲突;复合索引列顺序像查字典,先查主要条件(如姓名),再查次要条件(如年龄),顺序错了就查不到;乐观锁重试像重试买票,如果票被别人买走了,就再试一次,但次数有限,避免一直等。

3) 【对比与适用场景】

对比项水平分库(哈希+时间分表)垂直分库(业务拆表)复合索引(正确顺序 vs 错误顺序)乐观锁(版本号+重试)
定义按订单ID哈希分库,库内按时间分表按业务拆表(如订单+商品)查询条件列在前(如order_id+status)版本号判断冲突,重试机制
特性数据分散,需全局ID,避免自增冲突单表字段少,关联查询复杂多列查询高效 vs 索引失效全表扫描冲突概率低 vs 锁等待高
使用场景订单量极大(秒杀),单表字段少订单表字段多(含商品信息)经常按ID+状态查询写多读少(订单创建)
注意点哈希热点库(动态扩容),全局ID生成成本表关联复杂(需分布式事务)索引列顺序影响效率乐观锁可能导致重试失败

4) 【示例】
订单表(order)结构:order_id (主键,全局ID,如雪花算法生成),user_id,order_status,create_time,update_time,version。
分库分表逻辑:按order_id哈希到库1-8(如order_id % 8),库内按时间分表(如order_202401、order_202402)。
索引:order_id(主键)、order_id+order_status(复合索引,查询条件列在前)。
事务(乐观锁+重试):

def write_order(order, max_retry=3):
    db = getDBByHash(order.order_id)  # 按ID哈希到对应库
    table = getTableByTime(order.create_time)  # 按时间分表
    retry = 0
    while retry < max_retry:
        try:
            db.table.update(
                order_id=order.order_id,
                status='paid',
                version=order.version+1
            )
            break
        except DBVersionConflictError:
            retry += 1
            if retry == max_retry:
                raise
    # 缓存更新(TTL 5分钟)
    cache.set(f'order_status_{order.order_id}', order.status, 300)

缓存失效策略:若订单状态更新,同时更新缓存(如TTL),若缓存过期,读数据库时检查状态一致性(如读时检查数据库状态是否与缓存一致,不一致则更新缓存)。

5) 【面试口播版答案】
好的,针对高并发写订单表,核心设计思路是分库分表+索引优化+事务性能优化。首先,分库分表:采用水平分库按订单ID哈希(如order_id % 8),将数据分散到8个库,每个库再按时间分表(如按月),每秒写入数千条时,单个库压力降低;同时用全局ID(如雪花算法)解决自增主键冲突。然后,索引策略:主键用订单ID(唯一,快速定位),复合索引用order_id+order_status(查询状态变更常用,提升多列查询效率)。接着,事务性能优化:采用乐观锁(加version字段),更新时检查版本是否一致,不一致则重试(最多3次,避免死循环);设置事务隔离级别为“读已提交”,减少锁等待。此外,缓存订单状态时用TTL(5分钟),更新时同步更新缓存,避免不一致。这样既能分散写入压力,又能减少锁竞争,提升事务性能。

6) 【追问清单】

  • 问:分库分表后,哈希分库是否会导致热点库?如何解决?
    答:哈希分库可能产生热点库(如某个ID哈希到同一库),可通过动态扩容(增加库数,重新哈希)或轮询策略缓解。
  • 问:复合索引的选择依据是什么?比如是否需要考虑查询频率?
    答:根据查询语句,若经常按订单ID和状态查询,就建复合索引,提升多列查询效率;若查询频率低,可不用。
  • 问:乐观锁的版本号如何更新?是否每次都+1?
    答:是的,更新时版本号递增,下次查询比较版本号,不一致则重试,避免脏读。
  • 问:缓存如何配合?比如是否需要缓存订单状态?
    答:可缓存订单状态(TTL),但需防缓存穿透(布隆过滤器)、雪崩(限流),并设置缓存失效策略(如更新时同步更新缓存)。
  • 问:监控指标有哪些?如何评估优化效果?
    答:监控库表写入QPS、锁等待时间、事务成功率、缓存命中率,对比优化前后的指标,评估性能提升。

7) 【常见坑/雷区】

  • 分库分表后主键冲突:水平分库用哈希分表,但自增主键可能导致跨库冲突,需用全局ID(如雪花算法)。
  • 索引选择错误:复合索引未包含查询第一列(如查询order_id时建order_id+status,索引无效),导致全表扫描。
  • 事务隔离级别设置不当:读未提交可能导致脏读,读已提交可能锁等待,需根据业务需求选择(如订单支付用可重复读,但锁竞争严重)。
  • 乐观锁版本号回滚:更新失败需重试,避免死循环,需设置重试次数限制(如最多3次)。
  • 缓存与数据库不一致:未考虑更新策略(如TTL),导致读缓存过期,需用缓存失效或更新机制。
51mee.com致力于为招聘者提供最新、最全的招聘信息。AI智能解析岗位要求,聚合全网优质机会。
产品招聘中心面经会员专区简历解析Resume API
联系我们南京浅度求索科技有限公司admin@51mee.com
联系客服
51mee客服微信二维码 - 扫码添加客服获取帮助
© 2025 南京浅度求索科技有限公司. All rights reserved.
公安备案图标苏公网安备32010602012192号苏ICP备2025178433号-1