蔚来汽车 x TiDB丨单表超 20 亿条数据,从 MySQL 到 TiDB 的迁移思考与实践

导读


本文来自 TiDB 社区合肥站走进蔚来汽车——来自吴记老师的演讲《TiDB 在新能源车企的实践:MySQL 到 TiDB 的迁移思考》。这次分享深入探讨了新能源车企蔚来汽车从 MySQL 迁移到 TiDB 的过程与实践,包括迁移过程中的挑战和动机,以及面对单表数据量增长至 20 亿条时的应对策略;此外,也将分享使用 TiDB 过程中常见的问题与解决方法,帮助大家更有效地应用 TiDB 解决企业数据库管理中的挑战。
蔚来汽车 x TiDB丨单表超 20 亿条数据,从 MySQL 到 TiDB 的迁移思考与实践-每日运维蔚来汽车 x TiDB丨单表超 20 亿条数据,从 MySQL 到 TiDB 的迁移思考与实践-每日运维
蔚来是一家全球化的智能电动汽车公司,致力于通过提供高性能的智能电动汽车与极致用户体验。2023 年第三季度中国汽车市场销量 566.8 万辆,同比增长 2.4%,其中新能源车型销量合计接近 200 万辆,同比增长 36%。其中,蔚来在中国 30 万元以上的纯电汽车市场中位列第一,市场份额占比 45%。蔚来汽车 x TiDB丨单表超 20 亿条数据,从 MySQL 到 TiDB 的迁移思考与实践-每日运维随着业务的快速扩张,蔚来公司内部某些业务的数据量急剧增加,部分业务的日增数据量达到千万级别。在 MySQL 数据库中,一些表的记录数已超过 20 亿条。在多种业务场景中,对这些大型表进行联接查询导致严重的性能瓶颈,查询效率低下,甚至经常超时。由于查询需求的多样性,传统基于 hash 的分表策略已无法满足业务需求。我们目前面临的数据库挑战主要包括:1. 性能问题:在执行包含 20 亿记录的大表与不同规模的其他表(百万、几十万、几万)的联接查询时,性能显著下降,特别是对于聚合函数如count的查询几乎不可行。2. 时间维度跨度大:大多查询场景需要结合时间维度进行时间范围查询,通常要查询中过滤最近半年的数据,但也有可能需要查询历史数据。3. 表结构复杂性:大型表初始包含 20 多亿条记录,拥有 30 多个字段,其中约 10 个字段需要与其他三个表进行联接查询。4. 写入与同步延迟:部分数据库表的单表写入数据量巨大,导致主从复制(master-slave replication)出现延迟,影响多个业务流程。5. DDL 执行缓慢:在 MySQL 中,由于单表数据量过大,执行数据定义语言(DDL)操作变得非常缓慢,有时需要数小时才能完成。为了解决这些问题,可能需要考虑以下策略:

  • 优化查询:重写查询逻辑,减少不必要的联接和数据扫描。
  • 索引优化:为常用于联接和查询的字段创建索引,提高查询效率。
  • 分区表:根据业务逻辑对表进行分区,以提高查询和维护的性能。
  • 读写分离:通过读写分离来减轻主数据库的压力,提高查询响应速度。
  • 分布式数据库:考虑使用分布式数据库解决方案,以支持水平扩展和负载均衡。
  • 异步处理:对于不需要即时返回结果的查询,采用异步处理方式。
  • 分布式协调器 PD(Placement Driver):PD 是 TiDB 的元数据管理组件,负责存储集群的元信息,包括数据分布和节点状态。它与 TiDB Server 交互,协调数据的分布和负载均衡。
  • 分布式存储 TiKV:TiKV 是一个分布式的键值存储系统,负责存储实际的数据。TiDB Server 通过 PD 与 TiKV 进行交互,获取或写入数据。
  • 执行器:在获取到数据后,TiDB Server 的执行器负责进行数据的进一步处理,包括合并、排序、分页和聚合等操作。
  • 高可用性:TiDB Server 设计为无状态,可以快速故障转移,保证服务的连续性。
  • 强一致性:通过分布式事务和 MVCC 机制,TiDB 保证了事务的 ACID 属性。
  • 单一数据存储:数据存储在本地磁盘或连接的存储系统中,没有分布式存储的概念。
  • 垂直扩展依赖:由于是单体架构,MySQL 通常通过增加单个服务器的硬件能力(如 CPU、内存、存储)来提升性能,这称为垂直扩展。
  • 扩展性限制:垂直扩展有其物理限制,当达到硬件极限时,性能提升会遇到瓶颈。
  • 事务和并发处理:MySQL 通过行锁和表锁等机制来处理并发和事务,但在高并发场景下可能会遇到性能瓶颈。
  • 容错能力:TiDB 通过多节点和副本机制提供高可用性,MySQL 则依赖于主从复制和故障转移机制。
  • 性能:TiDB 通过分布式计算和存储优化了大规模数据集的性能,MySQL 在大规模数据集下可能会遇到性能瓶颈。
  • 复杂性与灵活性:TiDB 的架构较为复杂,但提供了更高的灵活性和扩展性;MySQL 架构简单,但在处理大规模和高并发场景时可能需要额外的优化措施。
  • 所有数据都存储在表空间中,表空间可以包含多个数据文件和日志文件。
  • 表数据以 B+树的索引结构存储,这为快速的数据访问提供了基础。
  • B+树的每个节点可以存储更多的键值,这意味着相比 B 树,B+树的高度更低,查询效率更高。
  • 通过 Undo 日志来实现 MVCC,允许在不锁定资源的情况下读取历史数据版本。
  • TiKV 将数据分散存储在多个节点上,通过 Raft 协议保证数据的强一致性和高可用性。
  • 通过这种方式,TiKV 可以支持同一时间点的多个事务读取到一致的数据快照。
  • 唯一索引的存储格式为tablePrefix{tableID}_indexPrefixSep{indexID}_indexedColumnsValue,Value为对应的行 ID。
  • 非唯一索引的存储格式与唯一索引类似,但每个索引值后附加行 ID,Value可能为null。
  • 通过 Raft 协议,TiKV 能够在多个副本之间同步数据,提高了数据的可用性和容错能力。
  • 并发控制:TiDB 使用 MVCC 和 TSO(Timestamp Ordering)来实现并发控制,而 MySQL 使用行级锁定和 MVCC。
  • 数据一致性:TiKV 通过 Raft 协议保证跨多个节点的数据一致性,InnoDB 则依赖于单个服务器的事务日志和恢复机制。
  • 存储效率:TiKV 的 RocksDB 存储引擎优化了写入性能和压缩,而 InnoDB 的 B+树结构优化了读取性能。
  • B+树的所有数据都存储在叶子节点,内部节点仅存储键值和指向子节点的指针,这减少了查找过程中的磁盘 I/O 操作。
  • 聚簇索引和非聚簇索引的设计,优化了数据的物理存储,减少了冗余和空间使用。
  • 这种映射允许 TiDB 通过主键值直接访问对应的行数据,提供了高效的数据检索。
  • 这种设计确保了索引的唯一性,并且可以通过索引值快速定位到具体的数据行。
  • 这允许 TiDB 处理具有相同索引值的多行数据。
  • 由于 TiDB 的存储层 TiKV 使用 RocksDB,索引数据也被优化存储,以减少磁盘空间的使用。
  • 分布式适应性:TiDB 的索引实现更适合分布式环境,易于水平扩展。MySQL 的 B+树索引则优化了单个服务器上的数据访问。
  • 查询效率:TiDB 的索引实现允许快速的数据检索,特别是在分布式查询中。MySQL 的 B+树索引通过减少 I/O 操作提高了查询效率。
  • 存储优化:TiDB 的 RocksDB 存储引擎优化了索引数据的存储,而 MySQL 的 B+树结构减少了索引的存储空间需求。
  • InnoDB 使用行级锁定机制来处理并发写入,确保事务的隔离性。
  • Undo Log 记录了数据在事务开始前的状态,这样即使在其他事务修改了数据之后,当前事务仍然可以读取到事务开始前的数据状态。
  • InnoDB 的 MVCC 主要通过 Read View 来实现,Read View 是一个快照,包含了在事务开始时所有已提交的数据的可见性信息。
  • 行锁在 SELECT ... FOR UPDATE 或 INSERT/UPDATE/DELETE 操作时自动加锁,以保证事务的原子性和隔离性。
  • 表锁在某些特定的操作,如全表扫描或某些类型的索引操作中使用。
  • 乐观锁:适用于写冲突较少的环境,通过检测在事务开始后数据是否被其他事务修改来避免锁的争用。如果检测到冲突,事务会进行重试。
  • 悲观锁:适用于高冲突环境,通过在事务开始时就锁定涉及的数据行,防止其他事务修改这些数据。
  • MVCC 通过为每个事务分配一个全局唯一的时间戳(TS),并使用这个时间戳来确定数据的可见性。
  • 在 TiDB 中,每个数据行都保存了多个版本,每个版本都有一个开始和结束的时间戳。查询操作会根据当前事务的时间戳来确定应该读取哪个版本的数据。
  • MVCC 实现:TiDB 使用时间戳和版本控制来实现 MVCC,而 InnoDB 使用 Undo Log 和 Read View。
  • 并发性能:TiDB 的 MVCC 机制通过减少锁的争用来提高并发性能,特别是在高并发读写的场景下。InnoDB 的 MVCC 通过 Undo Log 减少锁的使用,但在高冲突环境下可能仍然会遇到锁争用。
  • 历史数据访问:TiDB 和 InnoDB 都允许在不锁定资源的情况下访问历史数据版本,提高了系统的并发读取能力。
  • 性能分析工具:使用EXPLAIN和EXPLAIN ANALYZE分析 SQL 执行计划和实际执行情况。
  • 分区表分析:自动分析分区数据分布,优化查询计划。