本文编译自:袋鼠云科技|有趣的开源项目:https://github.com/DTStack/flinkxFlinkX是一个基于Flink的统一批量数据同步工具,不仅可以采集静态数据,如MySQL、HDFS等,还可以采集实时数据改变数据。比如MySQLbinlog、Kafka等都是全局的、异构的、批流式的一体化数据同步引擎。有兴趣的欢迎到github社区找我们玩~在MySQL中,同样的查询条件,如果你在SQL语句位置改变OR,那么查询结果也会不同。在更复杂的情况下,可能会导致索引选择不当的性能风险。为了避免出现执行效率大幅下降的问题,我们可以适当考虑使用Unionall来分离逻辑比较复杂的查询SQL。常见的OR使用场景,请阅读以下案例:案例一:不同列使用OR条件查询1.待优化场景SELECT....FROM`t1`aWHEREa.token='16149684'ANDa.store_id='242950'AND(a.registrationIdISNOTNULLANDa.registrationId<>'')ORa.uid=308475ANDa.registrationIdISNOTNULLANDa.registrationId<>''执行计划+-------------+------------------------+----------------+----------------+--------------------+--------------------+----------------+----------------+-------------------------------------------+|编号|选择类型|表|类型|钥匙|密钥长度|参考|行|额外|+------------+------------------------+-------------------+----------------+--------------------+-----------------+----------------+----------------+------------------------------------------+|1|简单|一个|范围|idx_registrationid|99||100445|使用索引条件;使用where|+------------+--------------------+----------------+----------------+-----------------+----------------+----------------+--------------+--------------------------------------------+共1行记录的返回,它需要5ms2。场景分析从查询条件可以看出token和uid的过滤性很好,但是因为使用了or,所以需要采用索引合并的方式来获得更好的性能。但是在实际执行过程中,MySQL优化器默认选择使用registrationId上的索引,导致SQL性能不佳。3.场景优化我们将SQL改写成unionall的形式。选择......从`t1`a.token='16054473'ANDa.store_id='138343'ANDb.is_refund=1AND(a.registrationIdISNOTNULLANDa.registrationId<>'')unionallSELECT......从`t1`处a.uid=181579ANDa.registrationId不是NULLANda.registrationId<>''+------------+-----------------------+----------------+---------------+----------------------------+--------------+--------------------+-----------------------------+----------------+-------------------------------------+|编号|选择类型|表|类型|可能的键|钥匙|密钥长度|参考|行|额外|+------------+--------------------+------------------+----------------+--------------------------+----------------+----------------+---------------------------+----------------+----------------------------------+|1个|初级|一个|参考|IDX_TOKEN、IDX_STORE_ID_TOKEN|IDX_令牌|63|常量|1|使用索引条件;使用哪里||1|初级|乙|eq_ref|初级|初级|4|youdian_life_sewsq.a.role_id|1|使用哪里||2|联盟|一个|常量|初级|初级|4|常量|1|||2|联盟|乙|常量|初级|初级|4|常量|0|未找到唯一行|||联合结果|<联合1,2>|所有||||||使用临时|+------------+--------------------+----------------+----------------+------------------------+----------------+------------------+---------------------------+----------------+--------------------------------+共返回5行记录,对比优化耗时5ms来自对比前后的执行计划,可以明显看出,将SQL拆分成两个子查询,然后使用union合并结果,稳定性和安全性更好,性能更高。案例二:对同一列1使用OR查询条件待优化场景select.....fromt1asmcileftjoint1asccv2_1onccv2_1.unique_no=mci=category_no1leftjoint1asccv2_2onccv2_2.unique_no=mci=category_no2leftjoint1asccv2_3onccv2_3.unique_no=mci=category_no3leftjoin(selectproduct_id,count(0)countfromt2pprodinnerjoint3pinfoonpininfo.promotion_id=pprod.promotion_idandpprod.is_enable=1andppinfo.is_enable=1和pinfo.belong_t0=1和pinfo.end_time>=now()而不是(pinfo.onshelv_time>'2019-06-3000:00:00'或pinfo.end_time>'2018-12-0500:00:00')groupbypprod.product_id)aspconpc.product_id=mci.product_idwheremci.is_enable=0andmci.comodifty_typein('1','5','6')and(pc.count=0orpc.countisnull)限制0,5;执行计划2.场景分析本例中的SQL查询中有一个子查询。子查询作为驱动表,生成auto_key。测试通过SQL拆分进行,验证主要是(pc.count=0,或者pc.count为null)会影响整个SQL的性能,需要对比重写。3.场景优化首先,我们可以分开思考(pc.count=0,或者pc.count为null)如何优化?先写一个类似的SQLSelectcolfromtestwherecol=100orcolisnull;+------+|col|+------+|100||NULL|+--------+2rowsinset(0.00sec)这时候我们看到的其实是同一列,只不过对应的是不同的值。在这种情况下,我们可以使用casewhen进行转换。SelectcolFromtestwherecasewhencolisnullthen100elsecol=100end;+------+|col|+------+|100||NULL|+--------+2rowsinset(0.00sec)然后返回原来的SQL改写。选择.....fromt1作为mcileft加入t1作为ccv2_1onccv2_1.unique_no=mci=category_no1leftjoint1asccv2_2onccv2_2.unique_no=mci=category_no2leftjoint1asccv2_3onccv2_3.unique_no=mci=category_no3leftjoin(selectproduct_id,count(0)countfromt2pprodinnerjoint3pinfoonpinfo.promotion_id=pprod.promotion_id和pprod.is_enable=1andppinfo.is_enable=1andpinfo.belong_t0=1andpinfo.end_time>=now()而不是(pinfo.onshelv_time>'2019-06-3000:00:00'或pinfo.end_time>'2018-12-0500:00:00')groupbypprod.product_id)aspconpc.product_id=mci.product_idwheremci.is_enable=0andmci.comodifty_typein('1','5','6')andcasewhenpc.countisnullthen0elsepc.countend=0limit0,5;可以看出优化后的SQL比原始SQL快了30秒,执行效率提升了约50倍。案例三:优化关联SQLOR条件1.待优化场景SELECTuser_msg.msg_idAS'msg_id',user_msg.contentAS'msg_content',…FROMuser_msgLEFTJOINuserONuser_msg.user_id=user.user_idLEFTJOINgroupONuser_msg.group_id=group.group_idWHEREuser_msg.gmt_modified>=date_sub('2018-03-2909:31:44',INTERVAL30SECOND)ORuser.gmt_modified>=date_sub('2018-03-2909:31:44',INTERVAL30SECOND)ORgroup.gmt_modified>=date_sub('2018-03-2909:31:44',INTERVAL30SECOND)2.场景分析我们仔细分析了上面的查询语句,发现虽然业务逻辑只需要查询半分钟内修改数据,但是在执行过程中,所有数据都必须关联操作,造成不必要的性能损失。3.场景优化我们拆分了原来的SQL。sql-01的第一部分如下:SELECTuser_msg.msg_idAS'msg_id',user_msg.contentAS'msg_content',…FROMuser_msgLEFTJOINuserONuser_msg.user_id=user。user_idLEFTJOINgroupONuser_msg.group_id=group.group_idWHEREuser_msg.gmt_modified>=date_sub('2018-03-2909:31:44',INTERVAL30SECOND)sql-01由user_msg表驱动,使用gmt_modified索引过滤最新数据。第二部分sql-02如下:SELECTuser_msg.msg_idAS'msg_id',user_msg.contentAS'msg_content',…FROMuser_msgLEFTJOINuserONuser_msg.user_id=user.user_idLEFTJOINgroupONuser_msg.group_id=group.group_idWHERE用户。gmt_modified>=date_sub('2018-03-2909:31:44',INTERVAL30SECOND)ql-02以user为驱动表,msguser_id的索引过滤行很好。第三部分sql-03如下:SELECTuser_msg.msg_idAS'msg_id',user_msg.contentAS'msg_content',…FROMuser_msgLEFTJOINuserONuser_msg.user_id=user.user_idLEFTJOINgroupONuser_msg.group_id=group.group_idWHERE团体。gmt_modified>=date_sub('2018-03-2909:31:44',INTERVAL30SECOND)sql-03以group为驱动表,使用gmt_modified索引过滤最新数据。总结MySQLOR条件优化的常见场景主要有:1、同一列可以用IN替换2、不同列、复杂情况下,可以用unionall来分隔3、关联SQLOR条件,需要结合实际场景,分析优化。
