mysql分解关联

分解关联查询有时候是高性能查询的一种方案。在数据库中获取一张表的结果集,然后在应用程序做关联。例如表A(ID,name,age) B(ID,AID,COL1,COL2)

方案一

select * from a join b on a.id=b.aid where a.name='zhangsan';

方案二 把这个sql可拆分成

select * from a where a.name='zhangsan'; 
-- 得到3条数据,分别是id=1,2,3    
select * from b where b.aid in(1,2,3); 
-- 得到b表中的所有符合条件的数据
-- 最后应用层对查询出来的记录进行关联。

可能有人会觉得困惑,为什么不直接关联查询呢,简单粗暴,还不用多次连接数据库。事实上这种做法并没有错,但是关联查询使用内嵌循环的方式,如果数据量不是特别大的时候性能可能会比分解关联好,但是如果数据量非常大查询起来可能会非常慢,而且MySQL innodb不支持hash索引。如果把关联查询分解成单表查询,其实好处也是挺多的。

缓存的利用,单表的查询容易被应用程序缓存。
减少锁的竞争
在应用层做关联可以更容易对业务拆分,项目更容易拓展
MySQL innodb不支持hash索引,这相当于在应用层做hash关联

mysql重构查询方式

前几天有个同事需要开发一个功能,需要的数据来自不同的数据库。同事说,在不同数据库读取数据性能很差。我倒是不太认同这样的一个说法。
我认为在前期对业务进行做垂直分层对后期的拓展、负载、性能、故障转移等很多方面是很有好处且很有必要的(前期对项目规模的预测很重要,如果是很小的项目就没必要这样做),另外关于跨数据库查询性能差这种说法我认为不一定,这个要视情况而定,因为MySQL在设计上从连接到断开都是很轻量级的,在返回一个小的查询结果集方面非常高效,而且在现在的网络中,带宽和网络延迟都比以前快很多。跨数据库查询就类似(查询形式上的类似,并非性能上的类型)于在单个数据库中把一个复杂的查询拆分成几个小查询。

一般情况下,如果能一次查询所有的结果集,并且响应的时间在可接受的范围内那么不建议多次查询,但是有时候连表查询可能性能很差,这时候可以考虑把一个复杂的查询拆分成多个小的查询,通过多次测试找到合适的方案(事实上,跨数据库设计已对服务器做负载,就单查询方面来说,查询的性能可能会比单个数据库更高,而且在流量的高峰期会更加明显)。

MySQL 容量规划

在读密集型的应用中,主从的拓扑结构是一个比较好的解决方案。但是主从并不意味着一味地添加从服务器来扩充系统容量,因为当写操作成为系统的瓶颈时,添加的服务器越多,对主服务器的压力就会越大,并且有可能性能会降低。

例如:假设服务器的工作负载为10%的写和90%的读

前提:
并且读写都是相同的工作量,每台服务器(相同配置)
每秒只能进行1000查询操作
可以把所有的读操作都分配给从服务器

基于以上情况,如果当前的服务器能够支持1000次/s的查询,那么系统需要添加多少台服务器才能提升1倍性能呢。也就是200写和1800次读。因为写就站了20%,那么每台服务器的读也就是剩下800次,在这1800次读操作中,需要至少3台服务器。如果性能在提升一倍呢。也就是400次写和3600次读,因为每台服务器的读剩下600次,那么至少需要6台。往后需要性能提升的时候在财务允许的情况下且不改变架构的情况下可以不断添加从服务器。直到性能提升到最开始的10倍也就是1000次写操作和9000次读操作。这时候无法进行读操作,因为所有的资源已经被写操作占用了。这时候写操作就是整个架构的瓶颈,如果添加从服务器只会降低服务器性能,对于整个项目来说无疑是雪上加霜。这时候只能改变系统架构,例如对系统进行垂直拆分等

mysql 主从拓扑结构

MySQL的主从拓扑结构在少量写大量读的情况下表现非常优越。可以把读分摊到多个备库上,直到写成为整个拓扑结构的瓶颈(在容量规划会介绍为什么写会成为瓶颈)。这种拓扑结构在其他拓扑结构中,是比较简单的一种,但很灵活,能满足多种要求。

把一台备库当做待用的主库,除了复制没有其他的数据传输。
把一台备库放在远程数据中心,用作灾难恢复
延迟一个或多个备库,以备灾难恢复
使用一个备库作为开发测试等

MySQL复制-主从拓扑结构

mongo 优化器

mongodb优化方式很多跟MySQL类似如index、hint……。但是mongodb的查询优化器与MySQL稍有不同。例如一个索引能够精确匹配一个查询,那么查询优化器就会使用设个索引,例如在A列上有一个索引idx_A,那么优化器会选择idx_A方案。否则可能会选择其他索引,那么其他索引中选哪呢。mongodb会在这些索引中找到最先返回100个结果的索引。这个查询计划会被缓存下来,直到数据发送较大的变动,查询优化器根据其他可选的查询计划重新评估然后选出最好的计划。

mysql tinyint溢出

大部分开发语言基础部分基本类似,都有基础的数据类型,分别用不同的关键字表示不同范围的值。这些问题虽然都是一些很基础的问题,但是如果因为没有处理好这些问题,往往在修改BUG的时候是非常难被发现的。

场景还原:在数据库一张部门表中,同事甲把部门ID设置成int,部门ID是预约表的引用(但是没有添加外键约束) 。在预约表中部门ID的数据类型是tinyint,然而在项目实体模型中,同事乙预约表中的部门ID是int类型。前期由于分店不多(一个分店算一个部门),并没有发现什么问题。随着公司在全国的开拓业务,分店越来越多。部门表终于超过127条记录。因为部门ID是int类型,所以在加入数据的时候并没有什么异常。直到在一次APP预约中,App的同事说调用的接口数据不对。于是开始了BUG排查之旅。初看同事丙写的预约逻辑都没什么问题。在客户预约的信息中,数据库预约表中部门ID是127(正确值是131)。一步一步DEBUG,但是还是没找出问题。于是在调试的过程中获取插入的sql语句。获取到sql语句后,在sqlyog直接执行sql。发现预约表中存储部门ID依然是127(我录入的是131)。看到执行结果,我是一脸懵逼。于是在接下来的几次录入数据中发现预约表中部门ID依然是127。于是各种排查,最终看了一下数据库表的结构,发现在预约表的部门ID的数据类型为tinyint类型,这就解释了为什么当时入了部门ID的时候都是127。最终把数据库预约表中部门ID数据类型由tinyint修改成int后一切业务正常(一般情况下2个字节应该也可以满足要求了,smallint带符号的范围是-32768到32767。无符号的范围是0到65535)。

mysql hint

在MySQL优化中,如果对优化器的执行计划不满意是可以通过hit来控制最终的计划。但是如果没有十足的把握,不建议使用hit,因为MySQL会有统计引擎(不同引擎统计方式可能不同)。对于hit的用法,我建议阅读官方的文档。

在实践中,会有出现MySQL给出的优化策略不是最优的策略。曾经遇到一种情况,在多表查询中,因为业务的关系,需要取出大量的数据,但是MySQL给出的优化策略是走表中的索引。在多次测量中,MySQL给出的策略优化效果并不理想。后来直接把索引给忽略了。相对来说,性能有所提升。我认为是MySQL内部的统计跟实际情况的数据有偏差较大,导致计算随机IO和顺序IO的时间偏差较大。很多情况下MySQL给出优化策略应该是最优的,所以慎用hit。

mysql in使用场景

现在网上对于in的讨论、比较有很多,我认为最严谨的说法就是慎用。但是慎用不代表不要用,in的存在也有存在的理由,因为in有自己的应用场景。要使用in需要了解in在MySQL优化器中如果和转化的。例如有一张表A中有C1、C2、C3、C4、c5。where C1 IN (‘c1value1’,’c1value2’) AND C2 IN(‘c2value1’,’c2value2’);在这个情况下,MySQL优化器会转化成2*2=4种组合。对于4种组合这种情况来说是相对理想的,但是有一点组合越大,性能越差。

很多情况下可以通过其他查询来替代in查询。如果没有索引或者单列索引对于值的比较,用between and或者>或者<比较合适,因为在MySQL对值进行比较的时候只比较一次。而in的话会被比较多次。如果in子查询性能很多时候也不是很理想。MySQL优化器改写的sql执行效率很多情况下都是很糟糕的。关于子查询再后续的文章中在做讨论。那么什么情况下适合使用in呢。我认为覆盖索引非最后一列字段且该字段的查询条件是多个值的可以考虑使用in,因为索引有最左原则。以表A为例子,如果查询表A中c2,c3列,其中c3=’c3value’ ,c4值c4value1、c4value2、c4value3、
c5=’c5value5’,另外index idx_c3_c4_c5(索引的顺序依次是C3,C4,C5)。要完成这个查询查询的sql有很多种,一般情况下,我认为性能较好的sql应该是select c2,c3 from a where c3=’c3value’ and c4 in (c4value1、c4value2、c4value3) and c5=’c5value5’。因为c4位于索引的第二列,如果使用between and 、>、<这些范围查询查询,一定程度上,能够提升性能(相对于全表扫描来说,或者index_c3),但这不是最好的选择,因为无法利用index_c3_c4_c5中的c5列,这里使用in,优化器会转化成三种集合,可以充分利用index_c3_c4_c5中的所有列。如果遇到查询c1,c2列查询条件是c4=c4value1 and c5=c5value,如果c3的值个数有限,这里以性别为例,c3列存储都是男或者女,那么sql可以写成select c1,c2 from A where c3 in(‘男’,’女’) and c4=c4value1 and c5=c5value,但是这种技巧不能滥用。

然而sql是不是最好的选择还要看MySQL的执行计划。因为MySQL内部有一个统计引擎,在数据分布比较极端的情况下,查询出来的数据中随机I/O比全盘扫描锁消耗的时间还要长,那么以上的就不一定是最好的。

mysql decimal

mysql中decimal类型用于存储精确的小数,decimal允许最多65个数字。在处理decimal的时候,会消耗额外的空间和计算开销(cpu不支持直接对decimal进行计算)。所以根据实际情确定是否使用decimal。事实上,并不是涉及到钱的都一定使用decimal,这要看精确范围。如果是银行这种涉及到高精度的计算,我认为用decimal比较合适。如果是对精确范围较小的话,可以用其他数据类型。比如一些在线商店之类的平台,一件商品的价格不是很大,那么完全可以用int来记录(无符号int范围0~4294967295),单位为分,在显示的时候根据存储单位和显示单位进行转化。例如在数据库中保存了价格为100,单位是分,那么转化后在界面上显示1元。如果int的存储范围还不够,那么可以设置成bigint。bigint为64字节,可以存储大部分情况下的值。

TCP/IP慢启动

在TCP/IP数据交互节点,如果是在同个局域网那么数据一开始可以发送多个数据包给接收方。但是如果发送方发送的数据要经过较多的路由和速率较慢的链路,可能导致TCP吞吐量降低。所以在一开始的时候先限制发送端的发送速度,随着时间的推移根据接收方的接收情况在调整发送速率。这就是我们说的慢启动(slow start)用于防止因特网的突然过载和拥塞。

慢启动为发送方的TCP增加另外一个窗口即拥塞窗口,初始值为一个报文段。每收到接收方的一个ack,拥塞窗口就会增加一个报文段,以此类推。拥塞窗口有一个上限,即拥塞窗口与通告窗口中的最小值作为发送上限。

slowstart
图片中ack的大小比例画的不对(ack只有头部)这里简述慢启动的开始阶段到理想稳定状态。