{
    "componentChunkName": "component---src-templates-blog-blog-detail-tsx",
    "path": "/blog/schema-application-scenario-for-tidb",
    "result": {"pageContext":{"blog":{"id":"Blogs_42","title":"TiDB 常⻅架构应⽤场景","tags":["TiDB"],"category":{"name":"案例实践"},"summary":"本文将向大家介绍TiDB 的常见架构应用场景，以下内容整理自当天活动分享实录。","body":"> 作者介绍：黄潇 ， TUG 北京区 Leader ，TUG 2020 年度 MOA。\n\n如今分布式数据库百花齐放，在做数据库架构选型时应该从哪些方面进行考虑？在 TUG 陆金所企业行活动上， TUG 北京区 Leader 黄潇分享了 TiDB 的常见架构应用场景，以下内容整理自当天活动分享实录。\n\n本文主要分为以下三部分：\n\n1. 当今分布式数据库产品呈现百花⻬放的状态\n\n2. 在这种场景下数据库架构选型的一些思考\n\n3. TiDB 常⻅应⽤场景\n\n## 分布式数据库产品百花⻬放\n\n![1](https://img1.www.pingcap.com/prod/1_f5aa3e4750.png)\n\n从墨天轮发布的国产数据库流行度排行榜中可以看到，TiDB 排在首位。第二位到第五位分别是老牌的国产数据库 DM，GBase，OceanBase，PolarDB。从上图的曲线趋势可以看出，国产数据库目前处在一个蓬勃发展的时期。对于分布式数据库来说，目前我们最为关注的有以下五点：\n\n- 可以处理海量数据；\n    \n- 数据库高可用；\n    \n- 易扩展，像以前的拆库拆表、应用改造，成本很高，合并起来也非常麻烦；\n    \n- 强一致；\n    \n- OLTP。\n\n## 数据库架构选型的思考\n\n![2](https://img1.www.pingcap.com/prod/2_a252c69b99.png)\n\n对于一个线上高并发的服务来说，要考虑以下几点：\n\n- **稳定**。对于任何一个线上服务来说，可以容忍交易稍微慢一点，但一定不可能容忍频繁宕机。稳定是第一要务，脱离了稳定，效率没有任何意义。\n    \n- **效率**。在系统非常稳定的情况下，速度越快，就意味着用户体验越好。比如外卖下单，秒接单的用户感受一定很好。如果30分之后才接单，用户会想到底是系统出了问题还是外卖小哥偷懒。\n    \n- **成本**。当稳定有了，效率也有了，就要思考花的成本值不值？因为成本降下来才能赚到收益。\n    \n- **安全**。安全是一个大家都绕不开的问题。但凡做交易，大家都担心自己的数据被泄露。\n\n所以在数据库上面，我们最关注的就是保稳定、提效率、降成本、保安全。除了这四项之外，还有就是开源。在做技术选型时，我希望这个数据库是开源的，因为当我遇到一些问题，是有社区支持的。另外当我想为产品做一些贡献时，是可以和社区一起迭代的。在**稳定方面**，我们会考虑这几方面：\n\n- 这个数据库能不能做多活，能否有一些附加诊断以及高可用能力？\n    \n- 运维时，做监控告警是否容易？\n    \n- 是不是平滑滚动的升级？对业务有没有影响？\n    \n- 做数据迁移的时候，有没有问题？\n    \n- 数据校验好不好做？\n    \n- 运维上弹性扩缩容的效率如何？\n\n**性能方面**，我们最关注四点：\n\n- 第一，**低延迟**。\n    \n- 第二，**事务模型是不是我们平常使用的**。大家都知道 MySQL 是一个悲观事务模型，我希望的是迁移到新型的数据库上，还能保持原来的使用习惯。\n    \n- 第三，**高 QPS**。就是说这个数据库能否支持高的访问量。比如做一个活动，今晚流量涨了三倍，这种情况数据库能不能抗得住？抗不住会有什么样的场景？是完全挂掉还是服务端有自动的性能保护机制。\n    \n- 第四，**能支撑海量数据**。如果不能支撑海量数据，意味着需要提前跟业务沟通基础设计，确定能否先分库分表以及相应的分片数量。\n\n**成本方面**，主要考虑三个方面：\n\n- 第一，**应用接入成本**，指接入数据库容易不容易，是否需要提前沟通跟培训。\n    \n- 第二，**硬件成本，就是 CPU + 内存 + 磁盘**。像有一款分布式数据库，它是一个 Scale up 类型的数据库，它要求的内存是 384 G，但并不是所有的互联网公司都能负担这种高配机型的成本。众所周知，在一般的机型上大概要花费四万多成本，像互联网行业常用的机器，大概 128G 内存，30 核 CPU 或 40核 CPU，带一张 3.2T 的 PCIE 卡。但如果采用高密度机型，其价格会呈指数级上涨。所以在这种场景下，所选型的数据库会导致非常高的硬件成本。\n    \n- 第三，**网络带宽**。\n\n**安全方面**，有三点需要考虑：\n\n- 第一，**数据库是否有审计功能**。拿金融行业数据库举例，用户肯定希望能审计出来谁访问了这些数据，以及对这些数据做了什么操作。\n    \n- 第二，**数据是可恢复的**，不管用户做了什么异常操作，最后的数据都可以通过备份找回。\n    \n- 第三，**数据库的权限**。我们要考虑的是权限的力度会有多细，因为一些特殊场景下我们希望数据库的呈现精细到表级别，甚至字段级别。比如个人信息里的身份证信息、手机号，还有密码帐号，这些涉及个人隐私的信息是不希望展示给 DBA 或其他 RD 的。\n\n## TiDB 常用应用场景\n\n#### 业务规模和体量\n\n目前我司的 TiDB 的业务规模大概有 1700 多个节点，集群数百个。单集群最大的节点数大概是 40 多个，单表有最大上千亿条记录。目前处于小规模接入状态，还在探索更加丰富的业务场景。访问量方面，每日访问量达百亿以上。单集群的 QPS 峰值大概是 10 万以上。\n\n在什么样的场景下，我们会选择 TiDB 数据库？\n\n### 弹性伸缩场景\n\n选择分布式数据库是因为它是弹性伸缩的。我希望它能弹出去，也能收回来，不希望不停的去拆合。用过 MySQL 的同学都知道，当流量上来我们要拆库拆表，一拆二，二拆四，四拆八，越拆这个数量越可怕，成本指数级增长。但流量并不一定是指数级增长。产品流量上涨了你拆吗？拆的成本受不了，不拆外卖小哥就该抱怨以前发单 5 秒内就能接单，现在 10 秒 20 秒都看不到。除此之外，还会面临友商的竞争。所以这种场景下业界主要解决方案就是存储和计算分离。那这时候其实是因为我们计算资源不足了，而非存储资源不足。存储我只希望以 1.5 倍的比例去扩展，但是计算我希望以四倍的比例去扩展。这样两个其实不那么 match。在不 match 的情况下，把存储计算分给架构来解决这个事情。所以选择 TiDB 很大一个原因就是因为它是计算存储分离的架构。\n\n![3](https://img1.www.pingcap.com/prod/3_2793545572.png)\n\n互联网高速发展的时代，经常会有黑天鹅的事件出现。但是出现黑天鹅之后，流量在短期内处在爆发式增长的状态。但流量爆发式增长并不代表你的 DBA 人数爆发增长，也不代表你的机制是爆发式增长，这种情况下，DBA 投入再多人力，也来不及拆。另外从下单订购这个机器，机器到机房压测没问题，真正上线，到你能用的时候，可能至少一个月过去了。所以，我们遇到的一大痛点就是在**业务爆发式增长的情况下，我们来不及拆**。\n\n在活动大促期间，会出现一个非常陡峭的流量高峰。而活动之后，这个高峰会立刻下去。针对这种情况，DBA 需要在大促前配合业务方进行全链路的流量压测，该拆的拆，该扩容的扩容。如果拆出去的，需要一拆二，或者是一拆四，一拆八。拆出去那些库，最后还需要通过 DTS 把数据导回来。导回来需要考虑数据是否一致，无论业务方还是 DBA 都非常痛苦。\n\n![4](https://img1.www.pingcap.com/prod/4_2548579495.png)\n\n所以，从以上三个场景来说，**我们遇到的最大的痛点是存储计算没有分离**。\n\n我们选型 TiDB 的一个原因是它的存储分离的计算架构。在存储方面，TiDB 内存主要是负责 SQL 解析以及 SQL 引擎的执行。PD 主要提供元数据信息以及分布式数据库的时间戳功能。TiKV 提供的是无限扩展的分布式存储功能。\n\n![5](https://img1.www.pingcap.com/prod/5_c8de6d26c7.png)\n\n在这种场景下，存储是 TiKV 的一个集群，计算是 TiDB 的一个集群，它们互不关联，它们之间可以独立的扩容或者缩容，完全不影响其他的组件。这非常完美的解决了我们的诉求。所以，我们选择了 TiDB。\n\n### 金融级强一致场景\n\n除了弹性伸缩的场景，我们用 TiDB 还会考虑到金融级强一致场景。下面我来解释一下为什么要引进这个场景。\n\n先看一下在 MySQL 上遇到的一个问题。MySQL 5.6 的时候是半同步，MySQL 5.7 的时候是增强型半同步，也叫做 Loss-Less，指更少丢失数据的半同步。在 COMMIT 成功之前，先把事务的 Binlog 日志传输到某一个从库，这个从库返回给我 ACK 之后，才去改主库上的 Innodb 引擎。\n\n但这样会带来风险，相当于这个时候还没有告诉业务方 COMMIT 操作成功了。但是 Binlog 其实已经发送给了从库。这个时候如果主库 Crash 掉，从库却已经提交了，这就有风险了。\n\nLoss-Less 半同步复制也没有解决掉数据一致性的问题。\n\n![6](https://img1.www.pingcap.com/prod/6_7aca33c524.png)\n\n当把半同步的超时时间调成无限长，也并非一个强一致的场景。\n\n虽然可以把超时时间调到无限长。在这个时候，如果主从之间网络断了，任何从库都收不到 ACK。MySQL 后来引用了 MGR 来解决这个问题。**虽然 MGR 解决了数据的强一致，但是并没有解决数据的扩展性**。一个 MGR 最多只能接受九个节点，而且不管 5.7 还是 8.0 版本的 MGR 对网络抖动都是非常敏感的，秒级网络抖动会导致写节点切换。MGR 多写模式在社区里面发现了太多的 BUG，所以目前大家使用 MGR，都是使用单写模式，避免事务冲突，避免触发更多的问题。\n\nMySQL 的半同步是没有解决一致性问题的。而 TiDB 是通过 Multi-Raft 协议来解决这个问题。\n\n![7](https://img1.www.pingcap.com/prod/7_4b1b783c18.png)\n\n在 TiKV 这一层，把数据分成不同的 Region，每一组 Region 有多个副本，然后组成了一个 Raft Group。Raft Group 里面会有一个 Leader，负责读取和写入。这样就保证当这一组 Region 的 Leader 挂了的时候，那么剩下的节点会重新选取出一个 Leader 来，负责读取和写入。通过这种方式保证写到 Raft Group 里面的数据，一定不会丢失。至少单个节点挂了的话，故障是不会丢失的。\n\n下面看一下需要分布式事务的典型金融场景。\n\n#### 跨库事务场景\n\n金融体系除了强一致，还要求事务。MySQL 半同步的超时时间无限长是不行的。\n\n![8](https://img1.www.pingcap.com/prod/8_39e4f063ec.png)\n\n本地生活服务的公司强依赖于商户的履约能力，但商户的履约能力取决于系统的数据一致性和高可用。以外卖订单举例，订单在分别记录到用户端与商家端，这就涉及到了跨库业务，这个时候单纯依赖 MySQL 的数据一致性是搞不定的。\n\n#### 分库分表场景\n\n分布式数据库的一个典型场景就是分库分表。\n\n![9](https://img1.www.pingcap.com/prod/9_7be08030f1.png)\n\n比如用户维度下的转帐场景，在用户 A 的账户上减了一百元，在用户 B 的账户上加了一百元，他们可能在不同的数据分片上。这个事务肯定不希望一笔提交成功而另外一笔不成功。所以这个时候在分库分表的场景下，要保持分布式事务的一致性。\n\n#### 服务化 SOA 场景\n\n分布式事务典型的场景是服务化 SOA。\n\n![10](https://img1.www.pingcap.com/prod/10_f8b76373fa.png)\n\n如上图所示，在微服务化的过程中，黄色、蓝色和绿色这三个数据库我们希望整体保持一致性。那么，在这种场景下应该如何保证事务整体的一致性？\n\n在没有分布式数据库以前，订单类业务是可以写多笔的，当用户端 MySQL 集群挂了的时候，商家端的 MySQL 集群未必同时挂掉，这个时候通过校验服务可以发现商家端存在这笔订单，但是用户端没有这笔订单，那这时可以通过旁路补单的方式来把数据补回来。**但这样的方式非常依赖于业务场景，而且非常复杂**。下图是补单的逻辑，首先是轮询集群状态，判断是否宕机。\n\n如果是，则判断是商家端还是用户端。如果是商家端的，就检查用户端，把用户端的数据拉过来补，如果发现是商家端的掉了，那么就在用户端查 Binlog，看能否把 Binlog 拉回来，推动 BCP （Business Check Platform），它相当于是商业上的一个事务校验机制。把 Binlog 解析并补偿到另一个维度，这也是一种补单逻辑。\n\n![11](https://img1.www.pingcap.com/prod/11_30c451bb7e.png)\n\n业界现在推崇的一个概念叫做 BASE 的柔性事务。BA 代表业务的基本可用性，S 代表是柔性事务，相当于单性事务，E 代表的是最终的一致性。在 BASE 场景上，业界基本采用两种方式。一个是 TCC，即 Try / Confirm / Cancel 。这里面有多个参与方，都去试一下，可以做就提交，不可以做就 Cancel 掉。另外当一个事务持续时期很长的话，可能用 Saga 模式去做这个事情。这是业界的一些常规方案，但是我们发现订单业务单纯依赖这种补单的逻辑，实现的效果并不好。\n\n比如说在这种场景下，北京的一个机房宕了该怎么办？上海的一个机房网络不好该怎么办？所以这个时候就需要在整个配送链路上做 SET 化。SET 化是指从流量入口就把流量按用户维度进行分配，例如 A、B、C 三个用户全部分到第一个 SET 里面，D、E、F 三个用户分到第二个 SET 里面去。两个 SET 之间通过 DTS 进行双向的数据同步，当一个SET 出问题，会有短暂时间不可用，这时可以把全部的流量迁到第二个 SET 上。这样就能保证另外一个 SET 也可以继续下单，服务是可用的。但是 SET 化是一个比较伤筋动骨的解决方案，因为要从流量入口、当前的业务，数据库进行完整的改造。\n\n在这种情况下，并不是所有的业务都愿意做这个改造，因为这是一件很痛苦的事情。除了订单类的业务，实际上还有一种业务叫做帐户类型的业务。订单类的业务是说下了订单，多个维度都写入记录。但对于帐户类型的业务来说，对于金融层的业务是有强制诉求的。而且对金融来说，有异地多活和异地容灾的强诉求。\n\n![12](https://img1.www.pingcap.com/prod/12_d19f42a56b.png)\n\n这里面有绕不开的三个问题：\n\n1. 余额不能小于零，一定要是一个刚性事务，数据一致。\n\n2. 遇到 IDC 电力 或者是网络故障，整个机房宕了，该怎么办？\n\n3. 用 SET 化解决订单的业务的时候，单个机房可以用 SET 化来解决，但如果是帐户类业务则解决起来相对困难，在 SET 化双向复制场景下，写坏的数据已经扩散到多个集群。这时想要找回数据是非常困难的。\n\n以上就是我们在交易型事务上会遇到的两个痛点。首先是订单类的业务，通过补单的方式效果欠佳，而且业务方不一定愿意去配合做整套修改。其次帐户类型的业务在对数据有强一致性诉求的情况下，不能通过补单，而且数据写坏的情况下我们该如何去做。这就是我们对金融级强一致的数据的强烈诉求。\n\n**解决方案：Percolator 分布式事务模型**\n\n所以，基于上述场景诉求，我们选择了 Percolator 分布式事务模型。\n\n![13](https://img1.www.pingcap.com/prod/13_06e005015d.png)\n\n在金融级的产品数据库上，建议选择悲观模式，因为这和原来的 MySQL 是保持一致的，对业务方的改动量比较少，更容易做兼容。另外，把 RD 从烦琐的补单逻辑、拆分逻辑里面解脱出来，这样他们就可以专注自身的业务，也节省了成本。在使用 TiDB 分布式事务时，有两点建议：\n\n- 第一，小事务打包。TiDB 是分布式事务，要进行非常多的网络交互，如果把小事务拆分成一条条去执行，多次网络交互会导致网络延迟会非常长，对性能影响非常大。\n    \n- 第二，大事物要做拆分。事务模型如果特别大，更新时间就会很长。因为比较大的事务更新的 Key 比较多，期间发起的读取要等待事务的提交。这样对读取的响应延迟有比较严重的影响，所以建议大家把大事务进行拆分。\n\n### 数据中台场景\n\n我们遇到的第三个场景对数据中台的场景，也就是对于海量数据，其数据的场景开始慢慢的模糊化、复杂化。\n\n![14](https://img1.www.pingcap.com/prod/14_9ea14d2350.png)\n\n这里的模糊化、复杂化是指有些之前偏 AP，或偏数据分析的一些请求，现在希望能够拿到实时的数据，放在 TiDB 上来实现。\n\n以我们实际应用的场景为例，当我们要计算酒店房间价格是否有竞争力时，会抓取大量数据进行计算。要求实时数据，并且计算时也不能影响线上房间的价格。如果不拆分出去，而在同一个库上就会导致实时计算能力不停的拉线上 OLTP 的数据，就会造成一定的响应延迟。\n\n这种场景下，通过 Binlog 同步，把数据拉到 TiDB 里面，在这个集群上我们进行大量计算、高频点查的操作。对于比较大的数据量来说，写入量也是非常高的，TiDB 底层的存储 RockDB 采用 LSM-Tree 模型，对写入来说是友好的一个数据结构。所以这种场景 TiDB 写入可以满足我们的需求。\n\n在这样的集群上面也会有少量的报表类请求。第一个就是实时计算的场景。第二个在构建搜索引擎时也是采用这样的方案来实现。\n\n![15](https://img1.www.pingcap.com/prod/15_266f80b9b5.png)\n\n我们在金融上有一些付款、收单等凭证相关的消费的数据，我们希望这些数据能够从各个系统里抽取出来，汇总在一起形成数据大盘。这份数据形成后，可以多次使用。比如去做一个运营报表、实时大盘、数据大盘，或者作为一个数据订阅方去使用。数据同步需要介入的系统是非常多的，通过 Binlog 、消息队列或者业务双写这种方式进行同步都可以。\n\n### 其他场景\n\n以下是我们使用 TiDB 的时候可能还会考虑的一些其他场景。\n\n![16](https://img1.www.pingcap.com/prod/16_3b627b2244.png)\n\n第一，数据冷热分离。我们的线上数据量随着公司的运营历史数据增加会非常多，我们会把一部分历史数据导到 TiDB 集群里面去，这样也能适当降低成本。\n\n第二，公司内部的日志类和业务监控的数据。这种是因为 TiDB 是底层的 LSM-Tree 数据模式，对写入非常友好，基本上可以无限扩容。所以拿这个日志去做分析是比较合适的。\n\n第三， MySQL 改表存在非常多的限制条件。这种场景下，为了保证数据不延迟，同时能够控制在线改表过程中遇到业务高峰期，或者主从延迟时可以暂停改表操作，目前线上大部分还是使用 pt-osc 或者是 gh-ost 进行改表。但分表过多耗时会非常长。或者让业务方接受在高峰时间段改表，降低写入能力，要不就是想其他办法解决这个问题。所以 TiDB 秒级的 DDL 解决了我们非常大的痛点。\n\n第四，是一个比较特殊的场景，即从 ES 或者 HBase 迁移过来的业务。从 HBase 迁移的主要问题是 HBase 不支持二级索引，而从 ES 迁移过来的业务是由于 ES 可用性欠佳，于是就迁到了 TiDB 。\n\n第五，是今年比较火的一个场景，随着 5G、物联网的兴起，数据量爆炸式增长，我们会遇到非常多 TP 和 AP 类结合的诉求。这种场景下，我们其实在一个系统里面同时实现 TP 和 AP 类的 T+0 分析需求。比如在做大促活动时会针对优惠券的发放计算活动效果，这其中有非常多的大数据 T+0 分析诉求，仅仅依赖 T+1 报表是很难实现的，但是如果有 HATP，就能在线上传数据，提供给市场进行判断，降低了试错成本以及营销成本。\n\n以上就是 TiDB 的常见架构应用场景，希望能对大家有所帮助。","date":"2021-01-12","author":"黄潇","fillInMethod":"writeDirectly","customUrl":"schema-application-scenario-for-tidb","file":null,"relatedBlogs":[]}}},
    "staticQueryHashes": ["1327623483","1820662718","3081853212","3430003955","3649515864","4265596160","63159454"]}