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

设计一个数据库存储方案,用于存储视频处理任务的状态(如待处理、处理中、已完成、失败),系统需要支持高并发写入(每秒数千次任务提交),并保证状态一致性。请说明数据库选型、表结构设计以及索引策略。

万兴科技算法工程化难度:中等

答案

1) 【一句话结论】为支持高并发写入(每秒数千次任务提交)并保证状态一致性,推荐采用TiDB(分布式事务型关系型数据库)结合Redis缓存的混合方案,通过TiDB的事务保证状态转换的原子性,Redis缓存状态变更并分担写入压力,同时利用TiDB的version字段实现乐观锁,结合数据库事件通知(或消息队列)实现缓存与数据库的强同步。

2) 【原理/概念讲解】面试官您好,我们来拆解核心需求:视频处理任务的状态(待处理、处理中、已完成、失败)属于状态机模式,每个任务的状态转换有严格顺序(如待处理→处理中→(完成/失败)),需要保证状态变更的原子性和一致性。高并发写入(每秒数千次)意味着数据库需要支持高吞吐,而传统单机数据库(如MySQL)在高并发下行级锁会导致性能瓶颈,分布式数据库(如TiDB)通过分片(水平拆分数据)和多副本复制将写入压力分散,提升并发能力。Redis作为内存数据库,具备低延迟、高并发写入特性,适合作为状态变更的缓存层,减少数据库压力。状态机表设计核心是唯一标识(task_id)和状态枚举(status),通过事务保证状态转换原子性。此外,为避免并发冲突,引入乐观锁(版本号),通过检查版本号是否一致来确保同一时间只有一个线程更新状态。为保持缓存与数据库一致,采用数据库事件通知(如TiDB的触发器)或消息队列(如Kafka),当数据库状态更新时,通知Redis更新缓存,实现强同步。

3) 【对比与适用场景】以下是不同数据库方案的对比:

方案定义关键特性适用场景注意点
分布式事务型关系型(TiDB)基于MySQL协议的分布式数据库,支持分片、复制、ACID事务、版本号乐观锁ACID事务、高并发写入(分片后QPS可达数万)、强一致性、分布式存储、内置乐观锁(version字段)状态机、需要事务的复杂业务(如任务调度、订单处理)、高并发写入场景部署复杂,分片策略影响写入性能,需合理设计分片
NoSQL(如Cassandra)分布式NoSQL数据库,最终一致性最终一致性、高写入吞吐、分布式存储、无事务或弱事务大规模数据写入,对一致性要求不高的场景(如日志、缓存)状态转换复杂时,事务支持弱,可能导致数据不一致
Redis内存数据库,支持事务(简单)低延迟、高并发、内存存储、支持发布订阅状态变更的快速写入、缓存内存限制,持久化需额外考虑(如RDB/AOF),不支持复杂事务
传统单机MySQL单机关系型数据库ACID事务、行级锁、单节点存储低并发场景,数据量不大高并发写入时,行级锁导致性能急剧下降,无法满足数千次/秒

4) 【示例】表结构设计(以TiDB为例):

  • 表名:video_task_status
  • 字段:
    • task_id: UUID(主键,唯一标识任务)
    • status: ENUM('pending', 'processing', 'completed', 'failed')(当前状态)
    • version: INT(乐观锁版本号,每次更新递增)
    • created_at: TIMESTAMP(任务创建时间)
    • updated_at: TIMESTAMP(状态更新时间)
    • error_msg: VARCHAR(失败时的错误信息,仅当status='failed'时有效)
  • 索引:
    • 主键索引:task_id(快速查询)
    • 复合索引:status, updated_at(用于查询当前状态和最新更新时间)
  • 数据库事件通知到Redis的示例(假设使用TiDB的触发器或Kafka):
    当数据库更新状态时,触发器将更新事件发送到Kafka,消费者(Redis服务)消费后更新Redis缓存。
  • 事务示例(状态转换):
    -- 提交任务(待处理)
    INSERT INTO video_task_status (task_id, status, version, created_at) 
    VALUES ('task-20240101-001', 'pending', 1, NOW());
    
    -- 更新为处理中(乐观锁检查)
    UPDATE video_task_status 
    SET status='processing', version=version+1, updated_at=NOW() 
    WHERE task_id='task-20240101-001' AND version=1;
    
    -- 处理失败(更新为失败并记录错误)
    UPDATE video_task_status 
    SET status='failed', version=version+1, updated_at=NOW(), error_msg='处理超时' 
    WHERE task_id='task-20240101-001' AND version=2;
    

5) 【面试口播版答案】面试官您好,针对视频处理任务的状态存储,我建议采用TiDB(分布式事务型关系型数据库)结合Redis缓存的方案。核心思路是:用TiDB保证状态转换的强一致性(通过ACID事务),用Redis缓存状态变更并分担数据库写入压力。表结构设计为状态机表,包含task_id(唯一标识)、status(枚举状态)、版本号(乐观锁)、时间戳等字段,并建立复合索引覆盖状态查询。具体来说,任务提交时写入TiDB的pending状态,处理中时更新为processing(检查版本号避免冲突),完成后或失败则更新为completed或failed,同时记录错误信息。数据库更新状态时,通过事件通知(或消息队列)同步到Redis,确保缓存与数据库一致。这样既能满足每秒数千次的高并发写入,又能保证状态一致性。

6) 【追问清单】

  1. 为什么选TiDB而不是传统MySQL?
    回答要点:TiDB是分布式数据库,支持分片和复制,能将高并发写入压力分散到多个节点,而传统MySQL单机在高并发写入时行级锁会导致性能瓶颈,无法满足数千次/秒的写入需求。
  2. 多个线程同时更新同一个任务的状态(如从“处理中”更新为“失败”),如何避免冲突?
    回答要点:采用乐观锁(TiDB的version字段),每次更新时检查版本号是否一致,若不一致则回滚或重试,确保同一时间只有一个线程能成功更新状态。
  3. 失败任务的重试机制,如何保证不会重复处理?
    回答要点:失败任务标记为“failed”,重试时检查数据库中该任务的status是否为“failed”,若为失败则跳过,避免重复处理。
  4. 数据库分片策略,如何分配任务到分片?
    回答要点:按task_id的哈希值(如MD5(task_id) % 分片数)进行分片,确保同一任务的task_id始终映射到同一个分片,避免跨分片更新冲突。
  5. 状态查询的延迟问题,如何优化?
    回答要点:Redis缓存最新状态,查询时优先从Redis获取,缓存失效后查询TiDB,减少数据库查询压力,提升响应速度。

7) 【常见坑/雷区】

  1. 直接用传统MySQL,忽略高并发写入的瓶颈,导致写入性能下降,无法满足每秒数千次的要求。
  2. 忽略状态转换的原子性,导致状态不一致(如部分线程更新成功,部分失败),破坏状态机逻辑。
  3. 没有设计乐观锁,导致并发更新时冲突,影响写入性能。
  4. 缓存与数据库同步机制缺失,导致状态查询结果与数据库不一致。
  5. 分片策略不合理,导致任务跨分片,更新时需要跨节点协调,增加延迟和冲突风险。
51mee.com致力于为招聘者提供最新、最全的招聘信息。AI智能解析岗位要求,聚合全网优质机会。
产品招聘中心面经会员专区简历解析Resume API
联系我们南京浅度求索科技有限公司admin@51mee.com
联系客服
51mee客服微信二维码 - 扫码添加客服获取帮助
© 2025 南京浅度求索科技有限公司. All rights reserved.
公安备案图标苏公网安备32010602012192号苏ICP备2025178433号-1