MySQL 对于千万级的大表要怎么优化

知乎讨论:https://www.zhihu.com/question/19719997


48 个回答

默认排序

很多人第一反应是各种切分;我给的顺序是:
第一优化你的sql和索引;

第二加缓存,memcached,redis;

第三以上都做了后,还是慢,就做主从复制或主主复制,读写分离,可以在应用层做,效率高,也可以用三方工具,第三方工具推荐360的atlas,其它的要么效率不高,要么没人维护;

第四如果以上都做了还是慢,不要想着去做切分,mysql自带分区表,先试试这个,对你的应用是透明的,无需更改代码,但是sql语句是需要针对分区表做优化的,sql条件中要带上分区条件的列,从而使查询定位到少量的分区上,否则就会扫描全部分区,另外分区表还有一些坑,在这里就不多说了;

第五如果以上都做了,那就先做垂直拆分,其实就是根据你模块的耦合度,将一个大的系统分为多个小的系统,也就是分布式系统;

第六才是水平切分,针对数据量大的表,这一步最麻烦,最能考验技术水平,要选择一个合理的sharding key,为了有好的查询效率,表结构也要改动,做一定的冗余,应用也要改,sql中尽量带sharding key,将数据定位到限定的表上去查,而不是扫描全部的表;

mysql数据库一般都是按照这个步骤去演化的,成本也是由低到高;

有人也许要说第一步优化sql和索引这还用说吗?的确,大家都知道,但是很多情况下,这一步做的并不到位,甚至有的只做了根据sql去建索引,根本没对sql优化(中枪了没?),除了最简单的增删改查外,想实现一个查询,可以写出很多种查询语句,不同的语句,根据你选择的引擎、表中数据的分布情况、索引情况、数据库优化策略、查询中的锁策略等因素,最终查询的效率相差很大;优化要从整体去考虑,有时你优化一条语句后,其它查询反而效率被降低了,所以要取一个平衡点;即使精通mysql的话,除了纯技术面优化,还要根据业务面去优化sql语句,这样才能达到最优效果;你敢说你的sql和索引已经是最优了吗?

再说一下不同引擎的优化,myisam读的效果好,写的效率差,这和它数据存储格式,索引的指针和锁的策略有关的,它的数据是顺序存储的(innodb数据存储方式是聚簇索引),他的索引btree上的节点是一个指向数据物理位置的指针,所以查找起来很快,(innodb索引节点存的则是数据的主键,所以需要根据主键二次查找);myisam锁是表锁,只有读读之间是并发的,写写之间和读写之间(读和插入之间是可以并发的,去设置concurrent_insert参数,定期执行表优化操作,更新操作就没有办法了)是串行的,所以写起来慢,并且默认的写优先级比读优先级高,高到写操作来了后,可以马上插入到读操作前面去,如果批量写,会导致读请求饿死,所以要设置读写优先级或设置多少写操作后执行读操作的策略;myisam不要使用查询时间太长的sql,如果策略使用不当,也会导致写饿死,所以尽量去拆分查询效率低的sql,

innodb一般都是行锁,这个一般指的是sql用到索引的时候,行锁是加在索引上的,不是加在数据记录上的,如果sql没有用到索引,仍然会锁定表,mysql的读写之间是可以并发的,普通的select是不需要锁的,当查询的记录遇到锁时,用的是一致性的非锁定快照读,也就是根据数据库隔离级别策略,会去读被锁定行的快照,其它更新或加锁读语句用的是当前读,读取原始行;因为普通读与写不冲突,所以innodb不会出现读写饿死的情况,又因为在使用索引的时候用的是行锁,锁的粒度小,竞争相同锁的情况就少,就增加了并发处理,所以并发读写的效率还是很优秀的,问题在于索引查询后的根据主键的二次查找导致效率低;

ps:很奇怪,为什innodb的索引叶子节点存的是主键而不是像mysism一样存数据的物理地址指针吗?如果存的是物理地址指针不就不需要二次查找了吗,这也是我开始的疑惑,根据mysism和innodb数据存储方式的差异去想,你就会明白了,我就不费口舌了!

所以innodb为了避免二次查找可以使用索引覆盖技术,无法使用索引覆盖的,再延伸一下就是基于索引覆盖实现延迟关联;不知道什么是索引覆盖的,建议你无论如何都要弄清楚它是怎么回事!

尽你所能去优化你的sql吧!说它成本低,却又是一项费时费力的活,需要在技术与业务都熟悉的情况下,用心去优化才能做到最优,优化后的效果也是立竿见影的!
编辑于 2016-01-19
908 61 条评论
分享
收藏 感谢 收起

提问:如何设计或优化千万级别的大表?此外无其他信息,个人觉得这个话题有点范,就只好简单说下该如何做,对于一个存储设计,必须考虑业务特点,收集的信息如下:
1.数据的容量:1-3年内会大概多少条数据,每条数据大概多少字节;

2.数据项:是否有大字段,那些字段的值是否经常被更新;
3.数据查询SQL条件:哪些数据项的列名称经常出现在WHERE、GROUP BY、ORDER BY子句中等;
4.数据更新类SQL条件:有多少列经常出现UPDATE或DELETE 的WHERE子句中;
5.SQL量的统计比,如:SELECT:UPDATE+DELETE:INSERT=多少?

6.预计大表及相关联的SQL,每天总的执行量在何数量级?
7.表中的数据:更新为主的业务 还是 查询为主的业务
8.打算采用什么数据库物理服务器,以及数据库服务器架构?
9.并发如何?
10.存储引擎选择InnoDB还是MyISAM?

大致明白以上10个问题,至于如何设计此类的大表,应该什么都清楚了!

至于优化若是指创建好的表,不能变动表结构的话,那建议InnoDB引擎,多利用点内存,减轻磁盘IO负载,因为IO往往是数据库服务器的瓶颈

另外对优化索引结构去解决性能问题的话,建议优先考虑修改类SQL语句,使他们更快些,不得已只靠索引组织结构的方式,当然此话前提是,
索引已经创建的非常好,若是读为主,可以考虑打开query_cache,

以及调整一些参数值:sort_buffer_size,read_buffer_size,read_rnd_buffer_size,join_buffer_size

更多信息参见:
MySQL数据库服务器端核心参数详解和推荐配置
mysqlops.com/2011/10/26

编辑于 2011-12-19
112 3 条评论
分享
收藏 感谢
说3点
1. 设计合适的索引,基于主键的查找,上亿数据也是很快的;
2. 反范式化设计,以空间换时间,避免join,有些join操作可以在用代码实现,没必要用数据库来实现;
3. buffer,尽量让内存大于数据.
编辑于 2012-02-01
37 7 条评论
分享
收藏 感谢
先读写分离、再垂直拆分、再水平拆分!
发布于 2011-12-19
17 3 条评论
分享
收藏 感谢

分享:MySQL数据库设计总结

作者针对MySQL数据库设计总结了19条规则,其中对于大表优化,他在规则7中提到:

大表可以考虑水平拆分。大表影响查询效率,根据业务特性有很多拆分方式,像根据时间递增的数据,可以根据时间来分;以id划分的数据,可以根据id%数据库个数的方式来拆分。

19条规则摘要如下:

规则1:一般情况可以选择MyISAM存储引擎,如果需要事务支持必须使用InnoDB存储引擎。

规则2:命名规则。

规则3:数据库字段类型定义

  1. 经常需要计算和排序等消耗CPU的字段,应该尽量选择更为迅速的字段,如用TIMESTAMP(4个字节,最小值1970-01-01 00:00:00)代替Datetime(8个字节,最小值1001-01-01 00:00:00),通过整型替代浮点型和字符型
  2. 变长字段使用varchar,不要使用char
  3. 对于二进制多媒体数据,流水队列数据(如日志),超大文本数据不要放在数据库字段中

规则4:业务逻辑执行过程必须读到的表中必须要有初始的值。避免业务读出为负或无穷大的值导致程序失败

规则5:并不需要一定遵守范式理论,适度的冗余,让Query尽量减少Join

规则6:访问频率较低的大字段拆分出数据表。有些大字段占用空间多,访问频率较其他字段明显要少很多,这种情况进行拆分,频繁的查询中就不需要读取大字段,造成IO资源的浪费。

规则7:大表可以考虑水平拆分。大表影响查询效率,根据业务特性有很多拆分方式,像根据时间递增的数据,可以根据时间来分。以id划分的数据,可根据id%数据库个数的方式来拆分。

一.数据库索引

规则8:业务需要的相关索引是根据实际的设计所构造sql语句的where条件来确定的,业务不需要的不要建索引,不允许在联合索引(或主键)中存在多于的字段。特别是该字段根本不会在条件语句中出现。

规则9:唯一确定一条记录的一个字段或多个字段要建立主键或者唯一索引,不能唯一确定一条记录,为了提高查询效率建普通索引

规则10:业务使用的表,有些记录数很少,甚至只有一条记录,为了约束的需要,也要建立索引或者设置主键。

规则11:对于取值不能重复,经常作为查询条件的字段,应该建唯一索引(主键默认唯一索引),并且将查询条件中该字段的条件置于第一个位置。没有必要再建立与该字段有关的联合索引。

规则12:对于经常查询的字段,其值不唯一,也应该考虑建立普通索引,查询语句中该字段条件置于第一个位置,对联合索引处理的方法同样。

规则13:业务通过不唯一索引访问数据时,需要考虑通过该索引值返回的记录稠密度,原则上可能的稠密度最大不能高于0.2,如果稠密度太大,则不合适建立索引了。


规则14:需要联合索引(或联合主键)的数据库要注意索引的顺序。SQL语句中的匹配条件也要跟索引的顺序保持一致。

注意:索引的顺势不正确也可能导致严重的后果。

规则15:表中的多个字段查询作为查询条件,不含有其他索引,并且字段联合值不重复,可以在这多个字段上建唯一的联合索引,假设索引字段为 (a1,a2,...an),则查询条件(a1 op val1,a2 op val2,...am op valm)m<=n,可以用到索引,查询条件中字段的位置与索引中的字段位置是一致的。

规则16:联合索引的建立原则(以下均假设在数据库表的字段a,b,c上建立联合索引(a,b,c))

规则17:重要业务访问数据表时。但不能通过索引访问数据时,应该确保顺序访问的记录数目是有限的,原则上不得多于10.

二.Query语句与应用系统优化

规则18:合理构造Query语句

规则19:应用系统的优化

各规则详细解读,欢迎阅读 MySQL数据库设计总结

发布于 2017-04-01
28 1 条评论
分享
收藏 感谢 收起
不纸上谈兵,说一下我的思路以及我的解决,抛砖引玉了
我最近正在解决这个问题
我现在的公司有三张表,是5亿的数据,每天张表每天的增量是100w
每张表大概在10个columns左右
下面是我做的测试和对比
1.首先看engine,在大数据量情况下,在没有做分区的情况下
mysiam比innodb在只读的情况下,效率要高13%左右
2.在做了partition之后,你可以去读一下mysql的官方文档,其实对于partition,专门是对myisam做的优化,对于innodb,所有的数据是存在ibdata里面的,所以即使你可以看到schema变了,其实没有本质的变化
在分区出于同一个physical disk下面的情况下,提升大概只有1%
在分区在不同的physical disk下,我分到了三个不同的disks下,提升大概在3%,其实所谓的吞吐量,由很多因素决定的,比如你的explain parition时候可以看到,record在那一个分区,如果每个分区都有,其实本质上没有解决读的问题,这样只会提升写的效率。
另外一个问题在于,分区,你怎么分,如果一张表,有三个column都是经常被用于做查询条件的,其实是一件很悲惨的事情,因为你没有办法对所有的sql做针对性的分区,如果你只是如mysql官方文档上说的,只对时间做一个分区,而且你也只用时间查询的话,恭喜你
3.表主要用来读还是写,其实这个问题是不充分的,应该这样问,你在写入的时候,同时并发的查询多么?我的问题还比较简单,因为mongodb的shredding支持不能,在crush之后,还是回到mysql,所以在通常情况下,9am-9pm,写入的情况很多,这个时候我会做一个view,view是基于最近被插入或者经常被查询的,通过做view来分离读取,就是说写是在table上的,读在进行逻辑判断前是在view上操作的
4做一些archive table,比如先对这些大表做很多已有的统计分析,然后通过已有的分析+增量来解决
5如果你用mysiam,还有一个问题你要注意,如果你的.configure的时候,加了一个max index length参数的时候,当你的record数大于制定长度的时候,这个index会被disable
6
7
8
太多了,洗澡去先- -
当然,我还是纸上谈兵了,lz把上下文给足,我可以帮你看一下
发布于 2011-12-19
49 13 条评论
分享
收藏 感谢
@哈哈
答主答得非常好了,但是不太赞同答主给出的顺序。
一个合格的技术人,应该能做出适用于未来的产品。引入分布式会带来很多麻烦,但它会让你走得更远。这不正是技术人的价值所在吗?
看到有人回复说有钱就上Oracle了。Facebook,阿里没钱吗?钱不是最重要的考虑。再说他们的技术实力没有甲骨文强吗?万万不要迷信IOE。
  • 首先,任何优化,都需要你了解你的业务,了解你的数据。
    • QPS要到多少?- 带宽及存储够的情况下,单机几千QPS妥妥的。
    • 读写比例如何?- 读多写少和写多读少,优化方法是有很大差别的。设置于只读场景,果断压缩。
    • 数据是否快速增长?- 基本就是QPS的要求。
    • 数据及服务的SLA要到多少?- 数据需不需要强一致?HA做到什么程度?
    • 诸如此类。
不同的场景有不同的侧重,解决方案是不同的。而对于一些典型的场景可能会有成熟的解决方案。
题主已注明“千万级”,因此以下假设题主为最常见的场景: 大量数据,QPS要求高,读多写少,数据快速增长,SLA要求高
  • 其次,说优化的方法。

主要从三个维度说:Why, How, When。
0. sql vs nosql
有些跑题,但也是很重要的一方面。
Why: nosql天生分布,而且大多针对某种类型的数据、某种使用场景做过优化。
比如大批量的监控数据,用mysql存费时费力,可以选择mongo,甚至时间序列数据库,存取会有量级提升。
How: 找对应解决方案。
When: 有足够诱惑 - 针对使用场景,有成熟解决方案,效率获得大量提升。
1. 优化shema、sql语句+索引
Why: 再好的MySQL架构也扛不住一个频繁的垃圾查询。不合理的schema设计也会导致数据存取慢。索引的作用不必多说,但如innodb下,错的索引带来的可能不只是查询变慢而已。
How: 设计阶段就需要预计QPS及数据规模,参考业务场景对数据的要求,合理设计表结构(参考mysql在线DDL问题),甚至违反设计范式做到适当冗余。生产环境分析慢日志,优化语句。索引的设计需要知道索引是怎么用的,比如innodb的加锁机制。
When: 这个不仅仅是第一个要考虑的,而应该是需要持续去优化的。特别是要参考业务。但实际环境中如果是这个的问题,那一般比较幸运了,因为一般已经优化过很多了。实际中遇到的一般是更深的问题。
2. 缓存
缓存没有那么简单。
缓存对于应用不是完全透明的,除非你用Django这种成熟框架,而且缓存粒度很大,但实际。。。像python,最少也得加几个装饰器。
如何保证缓存里面的数据是始终正确的?写数据前失效缓存还是写数据后?
缓存挂了或者过冷,流量压到后端mysql了怎么办?
缓存也不是万能的。写多读少,命中率会很低。
How: memcache用做缓存,redis用于需要持久化的场景。(redis能不能完全取代memcache?呵呵。。)
还可以使用mysql自带的query cache,对应用基本完全透明。但会受限于本机。而且只缓存查询结果,mc和redis可以缓存一些加工后的数据。
而且数据量大、QPS大的情况下,也需要考虑分片及HA的问题。如果有一个数据过热,把一个节点压垮了怎么办?
When: 基本上大多数读多写少的场景都能用,写多的情况下可能需要考虑考虑。
3. 复制及读写分离
Why: 这个其实是大多数场景下都必须的。因为复制可以实现备份、高可用、负载均衡。就算嫌麻烦不做负载均衡,那备份下总是要的吧?既然已经备份了,何不加个LVS+HAProxy做下HA?顺便稍微修改下应用,读写分离也就成了。
How: 节点少的情况下,主备。前面加Keepalived+HAProxy等组件,失效自动切换。读写分离可能需要修改下应用。
节点多的情况下,一是考虑多级备份,减轻主的压力。其次可以引入第三方组件,接管主节点的备份工作。
主主不是很推荐。一是需要考虑数据冲突的情况,比如错开id,同时操作数据后冲突解决。其次如果强一致会导致延迟增加,如果有节点挂了,需要等到超时才返回。
When: 主备几乎大多数场景。甚至不论数据大小。高可用对应用透明,为啥不用?主主麻烦,建议先用切分。
4. 切分
包括垂直切分和水平切分,实现方式上又包括分库、分表。
虽然有些难度,但还是推荐常用的。
Why: 垂直切分保证业务的独立性,防止不同业务争抢资源,毕竟业务是有优先级的。
水平切分主要用于突破单机瓶颈。除了主主外,只有切分能真正做到将负载分配下去。
切分后也可对不同片数据进行不同优化。如按时间切分,超过一定时间数据不允许修改,就可以引入压缩了,数据传输及读取减少很多。
How: 根据业务垂直切分。业务内部分库、分表。一般都需要修改应用。除分表外,其余实现不是很复杂。有第三方组件可用,但通用高效又灵活的方式,还是自己写client。
When: 垂直切分一般都要做,只不过业务粒度大小而已。
分库有是经常用的,就算当前压力小,也尽量分出几个逻辑库出来。等规模上去了,很方便就迁移扩展。
水平拆分有一定难度,但如果将来一定会到这个规模,又可能用到,建议越早做越好。因为对应用的改动较大,而且迁移成本高。

综上,数据库设计要面向现代化,面向世界,面向未来。。。
编辑于 2016-04-06
14 2 条评论
分享
收藏 感谢 收起
都千万数据了,居然没有具体细节,提问者肯定是新手。
发布于 2014-08-29
7 2 条评论
分享
收藏 感谢

1. 千万只是小case而已,千万对程序员来说好大啊, 人家一天几千万的,那DBA不是不要活了?
现在5T,10T 的单个数据库都很多。程序猿能兼职干了吗??
2. 你需要一个专业的DBA,专业的人干专业的事,DBA手里有自己的武器

3.程序员来操DBA的心,不是傻,就是楞,在不就是逗比。
我想强调的是“专业性”,数据库架构、管理、优化,在IT的始祖,美国是一个专门的行业和要求很严格的岗位,在我们这里是什么情况,一个编代码能力还不到3-5年的初中级程序猿总要试图,找到解决海量数据问题的捷径,还总一种企图找到“魔术师式的秘诀” 的心态,很显然,并不存在,一个合格的DBA,是经历各种问题处理、碰壁、深思的成长过程,经历早已覆盖了这些初中级程序猿能碰到的你们认为比较难的问题,对他们来说,这些都是很基本的问题, IT本身并没有捷径。 数据库的运行本质和一切程序代码都是一个原理,DBA不过对于数据库运行的各个环节更加清晰,更有量化,碰到问题的广度和深度都比写代码的程序猿多一些。相反要DBA去调试java代码可能同样吃力、难受,还做不出效果。
编辑于 2016-11-16
9 5 条评论
分享
收藏 感谢
你总得说说场景吧,不提场景就谈优化的,都是耍流氓。
发布于 2016-05-13
1 3 条评论
分享
收藏 感谢

没看错吧,千万级也叫大表?这个规模的,只要没把索引用错,几乎不用考虑优化。

具体遇到什么问题了?可以列出来,大家一起诊断一下。

关系数据库本来是一套严密的理论,现在越来越少的人愿意去花时间捉摸集合运算,很多时候是没有用对集合运算导致性能急剧下降。这其实不属于优化范围
编辑于 2016-06-05
10 3 条评论
分享
收藏 感谢
如果数据很冷或者都是简单主键查询,不优化也罢。
发布于 2014-08-09
1 2 条评论
分享
收藏 感谢

单表 7亿笔记录, 80G 大小, 无分区, 每秒可以支持400次增删查改.
还有一个 2.8亿记录, 大约40Gb 同一个库 的两个大表.

出现问题了,多半是你设计出问题了, 跟数据库关系不大.
编辑于 2015-08-31
7 7 条评论
分享
收藏 感谢
mysql只做简单的事情,千万级的表,不论如何优化,同样的SQL都没有十万级的表访问快。
如果设计千万级的大表,要问自己几个问题:
1.数据是否存在明显的冷热(考虑旧数据归档)
2.是否可以按照时间,区域拆分表
3.如果字段过多是否可以字段的关联性进行拆分
说白了就是:归档,垂直拆分,水平拆分
周松, http://ChinaDBA.com
先读写分离、再垂直拆分、再水平拆分!
发布于 2014-11-25
7 添加评论
分享
收藏 感谢
推荐一本书,《MySQL高性能优化》
发布于 2016-01-19
4 2 条评论
分享
收藏 感谢
提到优化,先要确定出现的问题,是存储引擎选择问题,还是sql语句使用问题(如:索引)亦或者是单一存储服务器对于千万级别的数据力不从心。
解决方法:
1、根据不同业务选用不同存储引擎,虽然一般情况下都优先选择InnoDB。
2、分析sql语句的影响结果集。查慢查询日志,定位慢查询的sql语句,查看是否有锁定的sql操作。
3、分库,分表,分区(慎用分区,往往OLTP操作不适用分区,分区反倒会拖慢原有查询)
4、利用缓存或NoSql代替现有一些热点查询操作,减轻Mysql压力。
分库:
可以基于业务逻辑拆分,不同业务分布在不同服务器中,减少单一服务器压力
分表:
垂直分表和水平分表两种方式
垂直分表:将表中的一些频繁更新和非频繁更新的字段分开存放
水平分表,三种分表方式:
  1. 均等分表,哈希方式分表,优点是负载平均分布,缺点是当容量持续增加时扩荣不方便,需要重新分表,主键就很不好处理。
  2. 递增分表,比如每一千万数据开一个新表,优点是自适应强,缺点是数据负载不均衡,需要代码层额外处理。
  3. 时间分表,根据不同的创建时间分表,适用于OLAP应用。
发布于 2015-11-22
4 添加评论
分享
收藏 感谢

读:对MySQL最好的优化,就是不读MySQL。
不要笑。
是去读cache。
在cache为王的时代,如果所有请求都落到后端DB,什么架构都扛不住。
去读redis和mc吧。

写:别一个劲什么东西都直接扔给DB,扔到队列里,控制好速度去消费,就写不死。

备份:都千万级别了,如果被注入或者出现人为失误丢数据了,没有备份,估计就完蛋了。

亲身经历过,某大型app,单表366G,被注入,恢复了一天。如果那天没有备份,估计就得辞职白白了。
发布于 2016-06-08
4 1 条评论
分享
收藏 感谢
千万也算大表ರ_ರ
发布于 2016-01-18
3 添加评论
分享
收藏 感谢

千万级,MySQL实际上确实不是什么压力,InnoDB的存储引擎,使用的是B+树存储结构,千万级的数据量,基本也就是三到四层的搜索,如果有合适的索引,性能基本也不是问题。

但经常出现的情况是,业务上面的增长,导致数据量还会继续增长,为了应对这方面的问题而必须要做扩展了此时可能首先需要考虑的就是分表策略了。

当然分表,可能还有其它几个原因,比如表变大了,千万级的数据库,为了减少运维成本,降低风险,就想到了通过分表来解决问题,这都是比较合适的。

分表,还有另一个方面的意思,就是在数据量更大的情况下,为了分担业务压力,将数据表分到不同的实例中去,这样有两方面的好处:1. 降低业务风险,如果一套数据库集群出问题了,那至少还有其它的可以服务,这样被影响的业务可能只是一部分。2. 降低运维成本,如果数据库想要做迁移,或者正常维护等操作了,那涉及到的数据量小,下线时间短,操作快,从而对业务影响也就小了。这种方式,我们称之为“分实例”。

分表的话,还是要根据具体的业务逻辑等方面来做,这方面有更精彩的回答,我这里贴一下:

========================================

分库分表是MySQL永远的话题,一般情况下认为MySQL是个简单的数据库,在数据量大到一定程度之后处理查询的效率降低,如果需要继续保持高性能运转的话,必须分库或者分表了。关于数据量达到多少大是个极限这个事儿,本文先不讨论,研究源码的同学已经证实MySQL或者Innodb内部的锁粒度太大的问题大大限制了MySQL提供QPS的能力或者处理大规模数据的能力。在这点上,一般的使用者只好坐等官方不断推出的优化版本了。

在一般运维的角度来看,我们什么情况下需要考虑分库分表?

首先说明,这里所说的分库分表是指把数据库数据的物理拆分到多个实例或者多台机器上去,而不是类似分区表的原地切分。

原则零:能不分就不分。

是的,MySQL 是关系数据库,数据库表之间的关系从一定的角度上映射了业务逻辑。任何分库分表的行为都会在某种程度上提升业务逻辑的复杂度,数据库除了承载数据的存储和访问外,协助业务更好的实现需求和逻辑也是其重要工作之一。分库分表会带来数据的合并,查询或者更新条件的分离,事务的分离等等多种后果,业务实现的复杂程度往往会翻倍或者指数级上升。所以,在分库分表之前,不要为分而分,去做其他力所能及的事情吧,例如升级硬件,升级,升级网络,升级数据库版本,读写分离,负载均衡等等。所有分库分表的前提是,这些你已经尽力了。

原则一:数据量太大,正常的运维影响正常业务访问。

这里说的运维,例如:

(1)对数据库的备份。如果单表或者单个实例太大,在做备份的时候需要大量的磁盘IO或者网络IO资源。例如1T的数据,网络传输占用50MB的时候,需要20000秒才能传输完毕,在此整个过程中的维护风险都是高于平时的。我们在Qunar的做法是给所有的数据库机器添加第二块网卡,用来做备份,或者SST,Group Communication等等各种内部的数据传输。1T的数据的备份,也会占用大量的磁盘IO,如果是SSD还好,当然这里忽略某些厂商的产品在集中IO的时候会出一些BUG的问题。如果是普通的物理磁盘,则在不限流的情况下去执行xtrabackup,该实例基本不可用。

(2)对数据表的修改。如果某个表过大,对此表做DDL的时候,MySQL会锁住全表,这个时间可能很长,在这段时间业务不能访问此表,影响甚大。解决的办法有类似腾讯游戏DBA自己改造的可以在线秒改表,不过他们目前也只是能添加字段而已,对别的DDL还是无效;或者使用pt-online-schema-change,当然在使用过程中,它需要建立触发器和影子表,同时也需要很长很长的时间,在此操作过程中的所有时间,都可以看做是风险时间。把数据表切分,总量减小,有助于改善这种风险。

(3)整个表热点,数据访问和更新频繁,经常有锁等待,你又没有能力去修改源码,降低锁的粒度,那么只会把其中的数据物理拆开,用空间换时间,变相降低访问压力。

原则二:表设计不合理,需要对某些字段垂直拆分

这里举一个例子,如果你有一个用户表,在最初设计的时候可能是这样:

table :users

id bigint 用户的ID

name varchar 用户的名字

last_login_time datetime 最近登录时间

personal_info text 私人信息

xxxxx 其他信息字段。

一般的users表会有很多字段,我就不列举了。如上所示,在一个简单的应用中,这种设计是很常见的。但是:

设想情况一:你的业务中彩了,用户数从100w飙升到10个亿。你为了统计活跃用户,在每个人登录的时候都会记录一下他的最近登录时间。并且的用户活跃得很,不断的去更新这个login_time,搞的你的这个表不断的被update,压力非常大。那么,在这个时候,只要考虑对它进行拆分,站在业务的角度,最好的办法是先把last_login_time拆分出去,我们叫它 user_time。这样做,业务的代码只有在用到这个字段的时候修改一下就行了。如果你不这么做,直接把users表水平切分了,那么,所有访问users表的地方,都要修改。或许你会说,我有proxy,能够动态merge数据。到目前为止我还从没看到谁家的proxy不影响性能的。

设想情况二:personal_info这个字段本来没啥用,你就是让用户注册的时候填一些个人爱好而已,基本不查询。一开始的时候有它没它无所谓。但是到后来发现两个问题,一,这个字段占用了大量的空间,因为是text嘛,有很多人喜欢长篇大论地介绍自己。更糟糕的是二,不知道哪天哪个产品经理心血来潮,说允许个人信息公开吧,以方便让大家更好的相互了解。那么在所有人猎奇窥私心理的影响下,对此字段的访问大幅度增加。数据库压力瞬间抗不住了,这个时候,只好考虑对这个表的垂直拆分了。

原则三:某些数据表出现了无穷增长

例子很好举,各种的评论,消息,日志记录。这个增长不是跟人口成比例的,而是不可控的,例如微博的feed的广播,我发一条消息,会扩散给很多很多人。虽然主体可能只存一份,但不排除一些索引或者路由有这种存储需求。这个时候,增加存储,提升机器配置已经苍白无力了,水平切分是最佳实践。拆分的标准很多,按用户的,按时间的,按用途的,不在一一举例。

原则四:安全性和可用性的考虑

这个很容易理解,鸡蛋不要放在一个篮子里,我不希望我的数据库出问题,但我希望在出问题的时候不要影响到100%的用户,这个影响的比例越少越好,那么,水平切分可以解决这个问题,把用户,库存,订单等等本来同统一的资源切分掉,每个小的数据库实例承担一小部分业务,这样整体的可用性就会提升。这对Qunar这样的业务还是比较合适的,人与人之间,某些库存与库存之间,关联不太大,可以做一些这样的切分。

原则五:业务耦合性考虑

这个跟上面有点类似,主要是站在业务的层面上,我们的火车票业务和烤羊腿业务是完全无关的业务,虽然每个业务的数据量可能不太大,放在一个MySQL实例中完全没问题,但是很可能烤羊腿业务的DBA 或者开发人员水平很差,动不动给你出一些幺蛾子,直接把数据库搞挂。这个时候,火车票业务的人员虽然技术很优秀,工作也很努力,照样被老板打屁股。解决的办法很简单:惹不起,躲得起。

《三国演义》第一回:“话说天下大势,分久必合,合久必分。”其实在实践中,有时候可能你原本要分,后来又发现分了还得合,分分合合,完全是现实的需求,随需而变才是王道,而DBA的价值也能在此体现。或分或合的情况太多,不能穷举,欢迎继续交流这个话题,如果以上有错误之后,也请批评指正。

给生活加点料。

================================

文章摘自微信公众号formysql。

如何分表的方案,其实这个不能一概而论,与业务逻辑有关系,与数据性质有关系,比如订单类型的,那就非常容易了,通过时间这个特性,可以通过一个路由表,把数据分散到多个实例上面,或者多个表上面,扩展性非常强,但是如果是用户关系等类似的表,他的唯一可以做HASH的值就是用户ID,做HASH时,涉及到不均匀、可扩展能力,迁移麻烦等问题,所以还是不太容易的,所以只能是具体问题具体分析了。

上面说的是分表的优化方案,当然还有其它方案,那就是要尽可能的写好SQL语句,不要留坑,MySQL就是适合那种快进快出的语句,尽可能的别把业务逻辑放到MySQL中去处理,要保持MySQL的高效运行才是最正确的选择。

发布于 2017-03-14
2 添加评论
分享
收藏 感谢 收起

优化本身需要有一个非常明确具体的目标,以及应用类型环境。

如果非要抛开这两个因素,也有看上去可以解决一切问题但不具备实际操作范例的答案,那就是:
1. 优化SQL
2. 优化结构
3. 优化存储


你可能感兴趣的:(mysql)