35

作为数据库核心成员,如何让淘宝不卡顿?

 3 years ago
source link: http://mp.weixin.qq.com/s?__biz=MzIzOTU0NTQ0MA%3D%3D&%3Bmid=2247499767&%3Bidx=1&%3Bsn=96facc4c6b730b2792c88b0b248355dd
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

aiUru2I.jpg!mobile

阿里妹导读: TDDL(Tabao Distributed Data Layer)是淘宝开源的一个用于访问数据库的中间件,集成了分库分表,主备,读写分离,权重调配,动态数据库配置等功能。本文以2007年TDDL初诞生时的视角,介绍TDDL是如何一步步设计成型的,希望能帮助同学们简单收获:常规数据库效率问题解决思路、TDDL框架设计基本思路以及分布式数据库设计思路等。

文末福利:《MySQL实操》技术公开课。

时间倒转穿越回2007年年底

一觉醒来,我还是照常去上班,走到西溪湿地附近,马路没有,高楼没有,有的是小山坡和金色的稻田。一番打听之后,才知道此时没有什么西溪园区。没办法,硬着头皮去滨江上班,一刷卡,才发现我并不是我,我现在的身份是淘宝数据库团队 核心成员。

此时全国上下在迎接着奥运的到来,一片祥和。淘宝网成交额突破400亿,日活用户达1000万。工程师们都非常兴奋,磨刀霍霍。但是也遇到了棘手的问题。

一  分析当前的现状

1.1  现有业务背景

  • 淘宝网给中国市场提供了全新的购物形式,在互联网的大潮下,用户暴增,成交量暴增,公司持续飞速增长。

  • 截止2007年,淘宝网成交额突破400亿,日活用户达1000万。

  • 全天有1000万用户访问淘宝。而绝大多数用户都是在网上逛,什么也不买。

1.2  当前的问题

1.2.1  用户体验与反馈

用户普遍反馈逛淘宝卡顿,操作延迟特别明显。

1.2.2  分析核心原因

  • 大量的用户在浏览商品,并不下单。这个人数和场景的比例有20:1。

  • 说明:数据库模式事务,写操作会对表或者行加写锁,阻塞读操作。

  • 业务数据集中在一张表里,如user表。一张表里数据破几千万。查询一条数据需要好几秒(单表数据量太大)

  • 说明:一张表数据提升,必然会导致检索变慢, 这是必然事实。不论如何加索引或者优化都无法解决的。

  • 所有表集中在一个库里,所有库集中在一个机器里。数据库集中在一台机器上,动不动就说硬盘不够了(单机单库)

  • 说明:所有业务共用一份物理机器资源。机器存在瓶颈:磁盘和CPU不够用且后期拓展性不佳。

1.2.3  总结问题

  • 20:1读写比例场景。

  • 单表单库数据量太大。

  • 小型机与单机场景,抗不住当前规模。

be6Rfe.jpg!mobile

当前现状

二  我要做什么?

  • 如何满足当前每天1000万用户逛淘宝的需求,且用户体验好(最基本要求:响应快)。

  • 如何满足未来有上亿用户的访问,甚至是同时访问,且用户体验好(最基本要求:响应快)。

高筑墙,广积粮,积极做好准备。

提炼核心:

  1. 提高数据库操作速度。

  2. 同时能应对未来规模变化。

三  我能做什么?

为实现以上两大目标,我能做什么?

3.1  提高数据库操作速度,通用方法

提炼常见的通用方法:

  • sql优化

  • 排除语法问题,烂sql

  • 下推优化

下推的目的:提前过滤数据 -> 减少网络传输、并行计算。

  • 提前过滤数据

  • 小表驱动大表等

  • 建立索引

  • 查询频率高的热点字段

  • 区分度高的(DISTINCT column_name)/COUNT(*),以主键为榜样(1/COUNT(*))

  • 长度小

  • 尽量能覆盖常用查询字段

  • 注意索引失效的场景

  • 分库分表

  • 垂直分库分表

  • 水平分库分表

  • 读写分离

  • 缓存的使用

等等。

3.2  如何应对未来的持续变化?

  • 必须支持动态扩容。

  • 必须走分布式化路线,百分百不动摇。

3.3  结合定位,分析自己能做的

3.3.1  分析我们的架构定位

(1)大前提

  • 们要做通用型框架,不参与业务。

  • 从软件设计原则出发,开闭原则:对扩展开放,对修改关闭。

说明:大修改就意味着不稳定,因此:我们要做到尽可能少的修改原来的代码。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。

(2)当前架构现状

淘宝网主要使用hibernate/ibatis传统框架:

RfQrAv.jpg!mobile

初始框架

(3)分析我们的架构定位

淘宝数据库团队当时使用映射框架(hibernate/ibatis)作为数据库交互入库,为了不让他们修改代码,那我们只能在ibatis/hibenate这类映射框架之下。

同时jdbc是与底层数据库交互的Java数据库连接驱动程序,是基础能力,我们要使用它,而不是改造它。

结论:我得把TDDL安插于ibatis/jdbc之间,于是有了第一张架构图:

qUfIfqf.jpg!mobile

TDDL的定位

3.4  总结,我们能做什么?

vE3QNnv.png!mobile

结合我们的目标,通用方法,大前提以及架构定位,分析下我们能做和不能做的。

不能做的:

  • 索引,因为这个是设计阶段,强业务相关。与大前提冲突:我们不参与业务。

能做的:

  • 语法优化

  • 排除sql问题

  • 下推优化

  • 分表分库(自动水平分表,水平分库)

  • 读写分离(读写分离/分布式化与动态扩容)

四  我们如何做?

4.1  语法优化

为达到语法优化的目的,我们需要具备什么能力?

简单来说:

  • 我们需要认识这个别人提交给我的sql。

  • 我能拆解sql。

  • 优化与重组这个sql。

uUBreuV.png!mobile

专业点来说:语义分析能力。

  • sql解析

  • sql规则制定

  • sql优化

  • sql重组

umEvIfZ.png!mobile

因此:我们需要设计一个sql解析器,sql优化器。

4.1.1  解析器

解析器的核心是词法分析、语法语义分析,也就是说来了一条 select/update/insert/delete语句,你能认识它,而且你知道下一步该怎么处理,同时为后面的优化器打下基础。

核心:将sql解析为一棵语法树。

例:

SELECT id, member_id FROM wp_image WHERE member_id = ‘123’

aiuI3ue.png!mobile

sql语法树:

u2maeir.png!mobile

4.1.2  优化器

核心:

  • 在sql解析成sql语法树后,使用sql优化规则(1. 语法优化 2. 下推优化), 通过对树进行左旋,右旋,删除子树来对语法树进行重构sql语法树。

  • 将重构的语法树进行遍历得到优化后的sql。

(1)语法优化

  • 函数提前计算

a. id = 1 + 1  => id = 2
  • 判断永真/永假式

1 = 1 and id = 1 => id = 1
0 = 1 and id = 1 => 空结果
  • 合并范围

id > 1 or id < 5 => 永真式
id > 1 and id = 3 => id = 3
  • 类型处理

id = ‘1’  => id为数字类型,自动Long.valueof(1)
create=‘2015-02-14 12:12:12’ => create为timestamp类型,解析为时间类型

(2)下推优化

  • Where条件下推

select from (A) o where o.id = 1
=>
select from (A.query(id = 1))

说明:提前条件过滤,提前获取数据,减少后期计算/IO/网络成本。

  • JOIN中非join列的条件下推

A join B on A.id = B.id where A.name = 1 and B.title = 2
=>
A.query(name = 1) join B.query(title = 2) on A.id = B.id

说明:提前过滤,减轻后期join计算成本,达到“小表驱动”的目的。

  • 等值条件的推导

A join B on A.id = B.id where A.id = 1 => B.id = 1
=>
A join B.query(B.id=1) on A.id = B.id

说明:同理,提前过滤。

4.1.3  总结

  • sql解析器

  • 负责将sql语句化为sql语法树。

  • sql优化器

  • 负责将sql语法树利用sql优化规则,重构sql语法树。

  • 将sql语法树转化为sql语句。

eeiuIjZ.png!mobile

4.2  分表分库

单库单表的问题:

几年前,业务简单,应用的数据比较少,表结构也不复杂。只有一个数据库,数据库中的表是一张完整的表。而到了今天,2007年了,业务复杂起来了,数据量爆增,单表数据破千万甚至上亿条,一条DML语句,死慢死慢的。这种情况下加索引已不再有显著的效果。

这个时候,数据库效率瓶颈不是靠加索引,sql优化能搞定的。

正确出路: 分表 分库 通过将表拆分,来降低单表数据量,进而提高数据库操作效率。

分表分为:

  • 垂直分表

  • 水平分表

分库分为:

  • 垂直分库

  • 水平分库

由于TDDL不参与业务,而垂直分库分表是强业务相关的,因此TDDL暂不参与垂直分库分表,只在水平分库分表方向上努力。

4.2.1  垂直分表

垂直拆分是将一张表垂直拆成多个表。往往是把常用的列独立成一张主表。不常用的列以及特别长的列拆分成另一张拓展表。

V3U7bei.jpg!mobile

简单垂直分表举例

核心要素:

  • 冷热分离,把常用的列放在一个表,不常用的放在一个表。

  • 大字段列独立存放,如描述信息。

  • 关联关系的列紧密的放在一起。

它带来的提升是:

  • 为了避免IO争抢并减少锁表的几率,查看详情的用户与商品信息浏览互不影响。

  • 充分发挥热门数据的操作效率,商品信息的操作的高效率不会被商品描述的低效率所拖累。

4.2.2  水平分表

水平分表是在同一个数据库内,把同一个表的数据按一定规则拆到多个表中。

RjYRriM.jpg!mobile

简单水平分表举例

简单点的技巧:按照枚举类型区分。

作用总结:

  • 库内的水平分表,解决了单一表数据量过大的问题,分出来的小表中只包含一部分数据,从而使得单个表的数据量变小,提高检索性能。

  • 避免IO争抢并减少锁表的几率。

4.2.3  垂直分库

垂直分库是指按照业务将表进行分类,分布到不同的数据库上面,每个库可以放在不同的服务器上,它的核心理念是专库专用。

Y3ui6bZ.jpg!mobile

垂直分库

作用总结:

  • 解决业务层面的耦合,业务清晰。

  • 高并发场景下,垂直分库一定程度的提升IO、数据库连接数、降低单机硬件资源的瓶颈。

  • 能对不同业务的数据进行分级管理、维护、监控、扩展等。

  • 垂直分库通过将表按业务分类,然后分布在不同数据库,并且可以将这些数据库部署在不同服务器上,从而达到多个服务器共同分摊压力的效果,但是依然没有解决单表数据量过大的问题。

4.2.4  水平分库(TDDL 核心)

水平分库是把同一个表的数据按一定规则拆到不同的数据库中,每个库可以放在不同的服务器上。

n26r6r.png!mobile

水平分库

作用总结:

  • 解决了单库单表数据量过大的问题,理论上解决了高并发的性能瓶颈。

水平分库核心要解决的问题:

  • 如何知道数据在哪个库里?-  路由问题

  • 结果合并

  • 全局唯一主键ID

  • 分布式事务(暂时不支持)

4.2.5  水平分库——问题解决

(1)自动路由算法

sql转发:在水平拆分后,数据被分散到多张表里。原来的一个sql需要拆解,进行转发路由。

例:

select * from tb1 where member_id in ('test1234', 'pavaretti17', 'abcd');
=>
select * from tb1 where member_id in ('test1234', 'pavaretti17', 'abcd');
select * from tb1 where member_id in ('abcd');

2IbIbmV.jpg!mobile

拆分表的数据访问——SQL转发

其中拆分和寻找的算法:怎么知道对应哪个表?即自动路由算法。常见的有:固定哈希算法和一致性哈希算法。

a)固定哈希算法

JjI7b22.png!mobile

b)一致性哈希算法

一致性哈希算法在1997年由麻省理工学院提出,是一种特殊的哈希算法,目的是解决分布式缓存的问题。

一致性哈希算法的优势:

  • 极好的应对了服务器宕机的场景。

  • 很好的支持后期服务器扩容。

  • 在引入虚拟节点后:能很好的平衡各节点的数据分布。

由于一致性哈希算法的优势,此算法几乎是所有分布式场景下使用的方案,包括mysql的分布式、redis的分布式等。

fU3q22Y.png!mobile

(2) 结果合并

fmUvQf3.png!mobile

升华: 引入fork-Join,提升操作速度(多线程并发重点场景,代码中也很常用哦)。

  • 任务拆分

  • 多路并行操作

  • 结果合并

yeUrAjz.png!mobile

(3)全局唯一主键

算法:基于数据库更新+内存分配。 在数据库中维护一 个ID,获取下一个ID时,会对数据库进行ID=ID+100 WHERE ID=XX,拿到100个ID后,在内存中进行分配。

  • 优势:简单高效。

  • 缺点:无法保证自增顺序。

例:

水平分库分表:一拆三场景。
主键分隔值:1000。
  • 表1新增一条数据,于是给表1分配1000个主键ID, 直到它用完。

  • 同理,表2、表3在新增数据时,也给它们分配1000个主键ID。直到它用完。

  • 当它们的1000个 主键ID 用完后,继续给它们分配1000个即可。

  • 重复下去,可保证各库表上的主键不重叠,唯一。

Ujm2Qje.png!mobile

这种产生全局唯一id的方式相当有效,保证基本的全局唯一特性和高性能的同时,可以对生成id的数据库分机架分机房部署达到容灾的目的。

4.2.6  分表分库总结

J73qaaZ.jpg!mobile

架构师角度:

  • 优先考虑缓存降低对数据库的读操作。

  • 再考虑读写分离,降低数据库写操作。

  • 最后开始数据拆分,切分模式:首先垂直(纵向)拆分、再次水平拆分。

  • 首先考虑按照业务垂直拆分。

  • 再考虑水平拆分:先分库(设置数据路由规则,把数据分配到不同的库中)。

  • 最后再考虑分表,单表拆分到数据1000万以内。

个人开发角度:

  • 优先使用分表分库框架(直接使用)。

  • 优先考虑缓存降低对数据库的读操作。

  • 自己垂直分表。

  • 自己水平分表。

之所以先垂直拆分才水平拆分,是因为垂直拆分后数据业务清晰而且单一,更加方便指定水平的标准。

4.3  分布式化

分布式化是大潮,是大规模服务器最后都要走的一步。

NzQVFzb.jpg!mobile

分布式数据库架构演变

4.3.1  读写分离

设计读写分离的数据库,有两大意义:

  • 主从只负责各自的写和读,极大程度的缓解X锁和S锁的竞争。

  • 从库可配置myisam引擎,提升查询性能以及节约系统开销。

说明:myisam查询效率高于默认的innodb效率。参考:myisam和innodb的区别。

VVvMV3.jpg!mobile

核心问题:

  • 数据的备份同步问题:参考4.4.3。

  • 读写比例支持动态设置:结合业务,如淘宝可设置为20:1。

4.3.2  容灾

主备倒换:提高可靠性 > 应对个别数据库宕机场景,尤其主库宕机。

ieaEzi.jpg!mobile

主备倒换

说明:DB2主库宕机后,自动将主库转为DB3。

核心问题:

  • 数据的备份同步问题:binlog 参考4.4.3。

  • 检测数据库的在线状态:心跳机制。

4.3.3  数据备份与同步

当只有单机或者一份数据时,一但数据库出问题,那么整体服务将不可用,而且更严重的是会造成数据损害丢失不可逆。

在读写分离与主备倒换的场景下,核心要解决的是多个数据库的数据同步与备份问题。

当前主流的是采用日志备份方式(redis也类似)。

实现原理:binlog日志备份。

j6j2uy.jpg!mobile

数据备份:bin-log同步

说明:

  • 主库负责写操作,在数据变更时,会写入binlog,同时通知各从库。

  • 从库收到通知后,IO线程会主动过来读取主库的binlog,并写入自己的log。

  • 写完从库log后,通知sql线程,sql线程读取自己的日志,写入从库。

4.3.4  动态扩容

动态扩容的意义在于:随着后期业务量增大,数据库个数可以通过增多的方式来应对,而相对的改造代价很小。

扩容前:

2Ezuqyz.png!mobile

扩容后:

vQj6ZzA.png!mobile

核心内容:

  • 在添加新库时

  • 注册机器与库

  • 路由算法调整:固定哈希算法-调整模数/一致性哈希算法天然支持扩容

  • 可选的权重调整

  • 修改权重,数据插入偏向于新库5。

  • 在各库数量平衡时,触发修改回原来平衡的权重,以保证后续的均衡分配。

五  架构成型

sql流向

下图介绍sql从流入TDD到流入数据库,期间TDDL各模块对Sql的处理。

VRFZ73b.png!mobile

架构图

BfmAV3.png!mobile

下图介绍了TDDL三层的位置以及作用。

AJ3U3aR.png!mobile

核心能力图

TDDL 核心能力,核心组建示意图,其中标出了各模块核心要解决功能,核心算法等。

Y3YrQ3i.png!mobile

参考

TDDL 官方文档
http://mw.alibaba-inc.com/products/tddl/_book/

TDD产品原理介绍

http://gitlab.alibaba-inc.com/middleware/tddl5-wiki/raw/master/docs/Tddl_Intro.ppt

TDDL(07-10年)初始版本介绍

https://wenku.baidu.com/view/9cb630ab7f1922791788e825.html

阿里云SQL调优指南

https://help.aliyun.com/document_detail/144293.html

一致性哈希算法原理

https://www.cnblogs.com/lpfuture/p/5796398.html

TDDL初期源码(码云)

https://gitee.com/justwe9891/TDDL

MyISAM与InnoDB 的区别(9个不同点) 

https://blog.csdn.net/qq_35642036/article/details/82820178

MySQL实操

RDS MySQL版基于阿里云分布式文件系统和SSD盘高性能存储,提供了容灾、备份、恢复、监控、迁移等全套解决方案。 本课程共36课时,讲解MySQL基于阿里云云服务器ECS的安装、配置, 通过学习与 掌握 MySQL的工作原 理、使用技巧以及优化,并具备 云端使用MySQL的能力

点击“阅读原文”学习 MySQL 吧~

7rAJ3qQ.jpg!mobile

关注 「阿里技术」

把握前沿技术脉搏

戳我,学MySQL。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK