17. 分库分表
当数据的体量达到一定级别之后,代码优化,已经达不到真实的性能要求了。下一步就可以考虑分库分表了。
我见过很多人不管什么问题,上来就分库分表是不对的。微信公众号也有很多篇文章的标题也是比较搞笑的《老大让我优化数据库,我上来就分库分表,他过来就是一jio》
下面可以介绍一下分库分表下的两种拆分以及何时拆分
17.1 水平拆分
水平拆分,主要拆的一个数据量级的问题。如果一个表中的数据超过500万行,那么就可以考虑进行拆分了。水平拆分的方式类似于医院男女科一样。
来了100个人报名。50个男,50个女。50个男肯定选择男科报名,50个女选择女科报名。有可能例子不恰当大概的意思差不多。
如果表中有原数据,可以采用把ID取模处理。偶数去A表,基数去B表。这个例子应该比较经典吧。
水平拆分的优点:
- 表关联基本能够在数据库端全部完成。不会存在某些超大型数据量和高负载的表遇到瓶颈的问题;
- 应用程序端整体架构改动相对较少; 事务处理相对简单;
- 只要切分规则能够定义好,基本上较难遇到扩展性限制;
水平切分的缺点:
- 切分规则相对更为复杂,很难抽象出一个能够满足整个数据库的切分规则
- 后期数据的维护难度有所增加,人为手工定位数据更困难;
- 应用系统各模块耦合度较高,可能会对后面数据的迁移拆分造成一定的困难。
17.2 垂直拆分
就是根据不同的业务进行拆分的,拆分成不同的数据库,比如会员数据库、订单数据库、支付数据库、消息数据库等,垂直拆分在大型电商项目中使用比较常见。
优点:拆分后业务清晰,拆分规则明确,系统之间整合或扩展更加容易。
缺点:部分业务表无法join,跨数据库查询比较繁琐(必须通过接口形式通讯(http+json))、会产生分布式事务的问题,提高了系统的复杂度。举栗子:不可能出现,在订单服务中,订单服务直接连接会员服务的数据库这种情况。
17.3 拆分解决方案
我这里用的是mycat中间件进行拆分。mycat支持10种分片策略
- 1、求模算法
- 2、分片枚举
- 3、范围约定
- 4、日期指定
- 5、固定分片hash算法
- 6、通配取模
- 7、ASCII码求模通配
- 8、编程指定
- 9、字符串拆分hash解析
详细的就不介绍了,我会选择一篇分库分表详细的介绍一下。大概的就是这些了。
18. MySQL刷脏页机制
内存上的数据和磁盘上的数据页的内容一致时,称为 “干净页”。
内存上的数据和磁盘上的数据页的内容不一致时,称为 “脏页”。
MySQL刷脏页的这个机制,会遇到查询卡顿的情况。为什么这么说呢,我们举一个场景吧。我老家里是开超市的,刷脏页的这个机制就好比我们家的账本,如果在超市营业期间,有人来赊账,我们就可以直接把赊账信息填写在那种临时记事本上,等晚上下班了再把数据转移到超市大赊账本上。
那么如果这段期间,临时记事本用光了,就必须停下手里的工作把临时记事本上的数据全部转移到大赊账本之后,再进行下面的操作,在转移的过程中几乎是属于卡顿情况的。
刷脏页是怎么刷的
首页你要告诉MySQL当前的计算机能刷多少的IO能力,这样innodb才能使出吃奶的力气进行刷脏页上的数据,这样也算是性能最大化吧。这个值不能过高也不能过低,过高的导致查询性能过低,如果过低就导致,刷脏页的数据跟不上添加的数据。最终影响系统的使用性能。
刷脏页比例如何设置
涉及的参数是 innodb_io_capacity
,innodb_max_dirty_pages_pct
第二个参数是控制刷脏页的比例,默认值为75,也就是75%。
假设脏页比例为M,我们范围是从0-100开始计算的,innodb每次写入的时候都有一个序号,这个序号跟checkpoint之间的差值我们设为N。N会算出一个范围0-100之间的数据。然后再根据F2(N)算法继续计算,这个算法比较复杂,我们只需要能说出N越大,算出来的值就越大就好了。
然后用F1(M)和 F2(N)取一个最大值假设为R,之后引擎就可以按照 innodb_io_capacity 定义的能力乘以 R% 来控制刷脏页的速度。
脏页比例是通过 Innodb_buffer_pool_pages_dirty/Innodb_buffer_pool_pages_total
得到的
扩展一个参数
innodb_flush_neighbors。MySQL在刷脏页时,会有一个连坐机制,当前面那个参数为1时,就会启用连坐机制,如果为0时,就不会启用连坐机制。
这个连坐机制是什么呢?如果刷一个脏页为AA,AA的旁边的数据页也是脏页,那么刷到AA的同时会把旁边的BB也一起刷掉。
具体的计算比较复杂,建议一笔带过,大概的流程能说出来就可以了。具体的技术文章在我公众号中。
19. 删除数据,表空间大小不变
这个问题应该是被问烂了。这个问题如果学过C语言的时候应该会更容易理解一些。
MySQL中删除数据是采用删除标记的方式。并不是直接删除对应的数据,所以给你的感觉数据的确没有了,但是数据页中仍然存在那块数据内存。
这里扩展一下空间复用的问题。
如果删除的那个数据是在300-700之间,并且插入的那个值的ID也是 300-700之间时,才会去复用这个空间,如果不是这个范围的就不会复用此空间。只有同时删除一整页数据的时候,下一次才会百分之百的复用,这样的几率还是比较小的。
如果不是百分之百的复用那么就会存在一种空洞的现象!我们复现一下,一条1 - 5的记录中,1,2,5被复用了,3,4没有被复用,这种情况就是空洞。
插入也会造成空洞,空洞的主要影响就是数据不紧凑,从而造成查询性能变慢。
解决方案
- 重建表
- 重新刷新表索引
20. 200G数据,100G内存会不会OOM
答案肯定是不会OOM的
首先我们介绍一下,当我们查询200G的数据的流程问题。
- 获取一行,写到 net_buffer 中。这块内存的大小是由参数 net_buffer_length 定义的,默认是 16k。
- 重复获取行,直到 net_buffer 写满,调用网络接口发出去。
- 如果发送成功,就清空 net_buffer,然后继续取下一行,并写入 net_buffer。
- 如果发送函数返回 EAGAIN 或 WSAEWOULDBLOCK,就表示本地网络栈(socket send buffer)写满了,进入等待。直到网络栈重新可写,再继续发送。
从上述流程中我们可以得到,一个查询在发送过程中,占用的 MySQL 内部的内存最大就是 net_buffer_length 这么大,并不会达到 200G;socket send buffer 也不可能达到 200G(默认定义 /proc/sys/net/core/wmem_default),如果 socket send buffer 被写满,就会暂停读数据的流程。
综上所述:MySQL查询是 边读边发 的!