背景

在日益数据量增长的情况下,影响数据库的读写性能,我们一般会有分库分表的方案和使用newSql方案,newSql如TIDB。那么为什么需要使用TiDB呢?有什么情况下才用TiDB呢?解决传统分库分表的什么问题呢?还会解释一些关键点和踩坑点。下面我会用比较白话的形式解读,当做对TiDB进行推广。

点赞再看,关注公众号:【地藏思维】给大家分享互联网场景设计与架构设计方案

掘金:地藏Kelvin https://juejin.im/user/5d67da8d6fb9a06aff5e85f7

目前痛点

目前分库表无论使用原生JDBC+ThreadLocal方案,还是使用中间件proxy、还是SDK嵌入代码的形式,即使用sharding-jdbc、zdal、mycat都存在着以下问题。

  1. 分库分表算法方案的选型
  2. 分库分表后带来的后续维护工作,每次增加节点,都需要申请磁盘、机器
  3. 新增节点需要进行停机、然后迁移数据,停机迁移对线上用户造成实时的读写影响。迁移失败还有代码回滚。迁移前还要等mysql没有binlog产生后才能迁移。
  4. 分库分表后,跨库一致性问题,都是使用最终一致性,代码维护繁琐。
  5. 数据存储压力、数据存放量偏移于某个节点
  6. 数据索引查询效率:即使是分库了,要走索引查询,其实还是需要查询多个库后得出结果后汇聚的。
  7. 不支持跨库left join其他表
  8. 唯一索引在跨库跨表不能保证唯一,场景如:支付流水号。现在分库分表的唯一key都很靠应用层代码控制。
  9. 表加字段麻烦。每个库每个表都要加
  10. 接入elastic-search需要查多个库才能入elastic-search节点

目的

分析TiDB如何解决痛点

TiDB整体架构

TiDB是一种分布式数据库。其实形式上来讲比较像Hadoop的做法,把数据分布在不同的机器上,并且有副本,有负责计算的机器、也由负责存储的机器。

入口层为tidb-server,图中TiDB,是客户端接入的入口,负责处理请求接口,这一层对存储要求不高,用于计算所以对CPU要求高,还有记录每个region的负责的范围。

第二层是PD,负责调度,如zookeeper的形式,负责数据迁移的调度、选举的调度。

第三层是tikv,也叫store,负责真实存储数据的一层。其中tikv由1个或者多个region组成,Region为最小的存储单元,就如JVM G1算法的Region的意思。每个Region将会打散分布在各个tikv下。

数据存储模型

  1. 行数据(元数据)

    一个表将会由一个或者多个Region存储。不同的表将会在不同的Region,而不是如传统分库那样每个库里的表都是相同。

    那么一个表下,每一行数据存储在哪个Region下是如何确定呢?

    首先,Region里面是一个Map, key 由 table_id表id、rowid主键组成。如:

t[table_id]_r[row_id]

map的value为表中每行数据的真实数据。

  1. 索引数据

    索引数据将会在另外一个Region存储,每建一个索引,就会有那个索引对应的Region。它的Map的 key 由 table_id、index_id 以及索引列的值编码组成。如:

t[table_id]_i[index_id][index_value]

value为rowid,这样就能用rowid来找到上面的表数据的位置。

就如mysql按索引查询,会先去找索引记录,再去找到主键聚簇索引来获取真实数据一个逻辑。

数据切分

定位在哪个Region,就是靠Key来算出落在哪个Region里面。和分库分表的根据某个字段来一致性hash算法方案不同。

TiDB的负责行真实数据的Region是使用主键范围来划分的。

有索引情况下,负责索引的Region会根据索引字段范围来划分。

基于Key通过计算,将会得出一个数字,然后按范围划分多个区间,每个区间由一个Region管理。

如:一个表数据主键rowid落在3个Region。

[0,10000)

[10001,20000)

[20001,30000)

这个范围需要数据入表前确定这个规则。

因为Region将会分布在所有TiKV上,也就有多个服务器去存数据,所以利用多机器CPU和磁盘,解决了痛点5存储压力,也解决了痛点1分库分表用哪个算法方案,只需要确定主键范围即可。

后续会说如何扩容。

提高索引效率

现有问题:

传统分库分表的索引都是在每个mysql实例里,跟着表走的。分库分表规则,一般都是根据表中的userid用户字段或组合性较高的字段来做切分库或者表的键,相同的userid将会落在相同的库或者表。

但是上述情况下,表中的索引字段假设为code,则code="aaa"的可能会因为不同的userid落在不同的库中,需要查询全量的库和表后,再重新聚合,这样就会增加CPU查询的消耗、还有TCP连接握手的消耗。

TiDB解决:

然而TiKV的有专门用于存储索引的Region,它数据结构的Key是由 表id+索引id+索引值id来决定的,value是rowid数据行主键,并且一个Region管理一个范围的Key,所以同一个索引同一个值都会在一个Region里面,这样就比较好快速定位相同的索引值的Region,得出对应的rowid,再根据rowid去存储表数据的Region中更快速找到表真实数据。就不需要走全量库的索引查找,因为mysql索引查找机制是先找到索引值,然后再找聚簇的主键后返回整行数据,从而提高性能。

这种做法有点像elastic-search的倒排索引,先根据value值再定位数据原来位置。这里解决痛点6减少索引查询压力

TIDB特性

1. 提供乐观事务模型和悲观事务模型

在3.0.8之前只有乐观事务模型,都是通过2PC两次提交的方式来进行事务提交。如果开启悲观事务模型,会比较像sharding-jdbc的柔性事务,有重试的功能,但是依然重试过多次(256次)失败仍然会丢失。

1.1 优缺点分析

TiDB 事务有如下优点:

  • 实现原理简单,易于理解。
  • 基于单实例事务实现了跨节点事务。
  • 锁管理实现了去中心化。

    但 TiDB 事务也存在以下缺点:
  • 两阶段提交使网络交互增多。
  • 需要一个中心化的版本管理服务。
  • 事务数据量过大时易导致内存暴涨。

1.2 事务的重试

使用乐观事务模型时,在高冲突率的场景中,事务很容易提交失败。而 MySQL 内部使用的是悲观事务模型,在执行 SQL 语句的过程中进行冲突检测,所以提交时很难出现异常。为了兼容 MySQL 的悲观事务行为,TiDB 提供了重试机制。

这种加重试就是悲观事务。

上述解决痛点4,不用再去自己维护跨库处理的事务最终一致性的代码,如A用户转账到B用户,也如商家和买家的情况,商家比较多收入时的交易情况。

虽然重试多次仍然会失败,但是这部分由TiDB处理。如果跨库事务以前的系统有框架处理,那现在就不需要如sharding-jdbc的sdk方式需要靠程序运行时才能重试,不然如果我们程序down机重试就没了。

2. 自动扩容

2.1 Region分裂

Region为最小的存储单元,当数据进入一个Region后达到一定数量,就会开始分裂(默认是超过现有Region负责范围的1/16)。

注意:数据表 Key 的 Range 范围划分,需要提前设置好,TiKV 是根据 Region 的大小动态分裂的。

这里是解决痛点2的每次都需要申请资源,不再运维来做上线前迁移数据,痛点3迁移时又要停机影响生成用户

因为TiDB作为中间件,不带任何业务属性,所以就不能使用userid等字段来做分片规则的键和自定义算法,使用主键是最通用的选择。(其实我觉得如果TiDB能做到就最好了)

2.2 新增存储节点

  1. 新增节点或者分裂Region,都有可能会触发迁移Region,由TiDB自动完成。不再需要入侵代码、或者使用中间件做分库分表逻辑和数据迁移、上线演练,全程交给运维(手动甩锅)。
  2. 并且不需要代码服务停机,不需要等没有新sql执行后才能迁移,这个是运行过程中实时迁移数据的。

这里就解决了痛点3停机迁移数据、痛点5存储压力

3. 副本容灾

每个 Region 负责维护集群的一段连续数据(默认配置下平均约 96 MiB),每份数据会在不同的 Store 存储多个副本(默认配置是 3 副本),每个副本称为 Peer。同一个 Region 的多个 Peer 通过 raft 协议进行数据同步,所以 Peer 也用来指代 raft 实例中的成员。

所以如果有1亿数据,将会由3亿数据落在磁盘中,虽然消耗磁盘,但是提高了可靠性。

TIDB成本

  1. 官方推荐至少部署 3 个 TiKV, 3 个 PD,2 个 TiDB。
  2. TiDB需要能使用线程数多的,PD需要CPU比较好的,TiKV需要SSD和CPU比较好的。
  3. 在论坛看到大家用的内存都是100G的,磁盘都是2T 的SSD。因为每行数据都总共有3个副本,消耗磁盘多。所以一个系统使用一套TiDB需要不少的成本。
  4. 然而这只是一个系统所需,一个项目中有多个系统组成情况下,就消耗更多资源了。并且随着数据日益增多将会越来越多资源。

使用场景

  1. 数据量达到一定量级,需要减少查询压力或者连接池不够等等因素后才需要进行。因为官方建议需要有2个tidb-server、至少两个PD、三个tikv,而且tikv需要都是SSD固态硬盘。所以在这种成本下,不一定所有项目都会使用,公司不一定愿意花成本去使用。而在一些数据量小的情况,建议还是使用mysql,等到数据量上来后,再做数据同步到TiDB。
  2. 已经分库分表后,希望改为使用TiDB,也能进行合并,需要使用TiDB Data Migration。
  3. 先在公司的架构组的项目使用,再到不是核心业务的项目使用,最后铺开给核心项目使用。
  4. 入门成本高,实验起来需要成本,因为官方推荐的部署方式需要多台好的机器。

注意事项与坑点

  1. 建议使用3.0.4、3.0.8或者4.0.0 (现在是2020年4月2日),不建议使用2.0版本,不然会出现升级不兼容的问题,需要去解决。
  2. 在增加节点扩容时,或者Region分裂时,同时有SQL执行insert或者更新数据,如果命中到对应的Region正在迁移,就可能会出现insert或者update出错 说“not leader”没有找到对应的位置的意思。但是会有重试的形式,把数据最终提交。
  3. 提前设置路由Region的分片范围规则,不然导入数据时会都落在一个节点上。如果你以前的主键数据时雪花算法得出的,那就需要求出最大最小值自己算范围手动设置好范围规则。
  4. TiDB 不支持 SELECT LOCK IN SHARE MODE。使用这个语句执行的时候,效果和没有加锁是一样的,不会阻塞其他事务的读写。
  5. 不要再使用Syncer同步数据或迁移到TiDB,因为如果在已经分库分表的情况下,使用Syncer同步,在某个帖子看的说会出问题。建议使用TiDB Data Migration
  6. TiDB无法修改字段类型

总结

其实有了上述功能,就可以减少分库分表的开发、运维维护成本,主要是平常分库分表到一定量要迁移,经常需要监控是否到迁移的量了,迁移时需要演练,迁移时要更新代码或者配置并且停业务是影响最大的。

虽然完成分库分表好像解决了一些问题,但是带来的后续还是很多的,TiDB就给我们解决了上面的问题,这样就可以更加专注的做业务了。


欢迎关注,文章更快一步

我的公众号 :地藏思维

掘金:地藏Kelvin

简书:地藏Kelvin

我的Gitee: 地藏Kelvin https://gitee.com/kelvin-cai

最新文章

  1. Hadoop学习笔记—13.分布式集群中节点的动态添加与下架
  2. JAVA实现打印机
  3. 前端构建之gulp与常用插件
  4. 003_kafka_主要配置
  5. Topcoder SRM 584 DIV1 600
  6. JavaScript数据结构——栈和队列
  7. HDU 5696 区间的价值 暴力
  8. 3DMAX 建立场景 工作流程
  9. C#获取远程计算机硬件信息实例(支持linux和windows)
  10. STM32的时钟树深入详解以及RCC配置
  11. angular : direative :comunication 指令之间的通讯
  12. 在HTML页面中加载js文件和css文件的方法
  13. 如何给网站添加IE浏览器升级提示
  14. [01-01]oracle数据库汉化
  15. 学习linux—— VMware 安装 ubantu 18 如何连接wifi
  16. Sudo的用法和Visudo设置
  17. COMSOL
  18. HashMap如何做循环遍历
  19. PAT 乙级 1061. 判断题(15)
  20. ZooKeeper实现分布式队列Queue

热门文章

  1. Java数据结构——AVL树
  2. Java数据结构——二叉搜索树
  3. Java数据结构——双端队列
  4. 更改默认Xcode
  5. SEO外包好还是自己组建团队
  6. Qt 子窗口监听主窗口信号
  7. React技术实践(1)
  8. 听过N次还是不会之:浏览器输入url后到底经历了什么
  9. java的方法详解和总结
  10. Java内存模型分析