mysql 多个and无效_案例解读MySQL搞不定的,不妨使用PostgreSQL试试

阅读使人充实,讨论使人敏捷,写作使人精确。

>>> 前    言

我们都是MySQL的重度用户,可能有人会问MySQL没有非得要替换的痛点,为什么要讨论PostgreSQL? 是的,没有任何一个非得要的理由,因为任何一种场景使用现有的技术组合,都能找到较好的解决方案。有的只有在特定的场景下,综合公司内部技术条件、系统稳健性、运营成本等因素考虑,看哪个更合适,自行取舍即可。 任何一个数据库产品,都有他存在的合理性,作为技术人员,我们不应该抗拒、甚至排斥某一项技术。

文章案例仅作参考,如果MySQL的用户,期待可以从中借鉴规避一些问题;如果是PostgreSQL用户,期待可以有更多的人共同学习、应用。

ba637d203e78da9cec75b0b1cf3b4394.png 业务应用场景介绍

这里接手的是运维中台服务系统,由于公司属于互联网行业关系型数据库基本是MySQL的天下。因此,公司早期搭建运维中台进行数据库选型的时候,除了MySQL,没有其他更优的方案选择。

同时,也因为有太多原因支持选择MySQL,比如轻量易用、公司内外形成的技术生态,运维人员熟悉,开发人员了解。

互联网大部分场景的特点通常是使用简单SQL即可,因此我们也有充分的理由相信,MySQL在互联网行业确实用得不错。

但互联网行业发展到今天,业务场景也不总是简单SQL实现即可,就像传统行业发展到今天,也不是那么传统了。好像现实环境中总有不少意外。

例如受人员变动、架构调整、业务功能需求丰富等等因素影响,加上运维、开发同学的水平参差不齐,即使有SQL审核平台,我们对开发规范很难做到有保障性的管控约束。于是,原本轻量级的MySQL系统,也有可能随着业务的发展,从简单SQL场景逐渐混入了复杂的SQL逻辑。

以该运维中台为例,服务从最初的轻量级场景发展至今已逐渐具备了以下几个特点:

l 功能复杂

因为是中台,所以打通了运维、开发、业务运营多方面数据流动渠道,兼备CMDB资源管理;数据库、服务器、应用版本部署;模块日常扩容容;监控预警等等功能。

l 读写频繁,IO较重

平台面向所有的现网、测试环境的设备,上游通过spark、flink等环境将数据多重聚合清洗之后,定时任务批量加载、或实时采集设备资源信息入库,界面上提供各方渠道使用。

这里截取一张QPS实时信息:

988fe331d340d867946242b26936fa2d.png

l 聚合统计较多

由于运维和业务应用的日常运营都涉及多种多样的实时监控、统计,查询场景存在比较多的avg、max等聚合统计操作。

l 数据关系复杂

日常运营需要关联多个维度获取相关数据,比如按业务模块维度,关联该模块下所有ip的资源使用情况,实时查看应用相关的监控状态。涉及到模块之间调用数据表、ip信息表、告警监控表等多个表之间的关联查询。

ba637d203e78da9cec75b0b1cf3b4394.png   意料之中的那些循序渐进的问题 刚一开始,采用MySQL集中式单体架构,系统运行比较平稳,但随着数据量的增长,以及平台功能的扩展,N年后的今天,各种问题开始层出不穷,网络带宽瓶颈、IO瓶颈、CPU瓶颈、内存瓶颈,频繁的连接请求失败、事务阻塞超时等等情况无一幸免。 直观的表现是我们在界面点击某个按钮,时不时加载十几秒才终于刷出来了数据,感觉用得很不爽。 土豪式的解决方法是演变成一主多从的读写分离、或分布式横向扩展架构,将大查询放到从库,或堆设备横向扩展计算能力。其实,如果没有彻底的拆解、重构应用逻辑,问题会周而复此,然后运维及业务同学会为之奋斗不息,永无尽头。 此外,并非所有场景都能通过堆设备或者分布式就能解决,比如该服务频繁的主从延迟并不能通过一主多从的读写分离来解决;比如主外键约束、没有合适分布键的多表关联,在目前分布式环境简直就是痛上加痛。

64b65fc6cee13dfeb77a7d4d23e989de.png

性能问题如此多,抓个现场SQL一看有点像Oracle的用法,当然Oracle的SQL用法远比例中的要复杂得多。其实这里的SQL不算太复杂,但在MySQL这样使用,已经有点反人类的感觉:
SELECT `core_alarm`.`ip`, `core_alarm`.`business_id`, `core_business`.`owners`, `core_ipextend`.`owner`, `core_alarm`.`value`, `core_alarm`.`alarm_count`, `core_alarm`.`begin_time`, `core_alarm`.`type`, `core_alarm`.`description`, `core_ipextend`.`svr_device_class_name`, `core_alarm`.`context`, `core_alarm`.`finish_time` FROM `core_alarm` INNER JOIN `core_ipextend` ON ( `core_alarm`.`ip` = `core_ipextend`.`ip` ) INNER JOIN `core_business` ON ( `core_alarm`.`business_id` = `core_business`.`busid` ) WHERE (`core_alarm`.`begin_time` > '2020-12-02 00:00:00' AND (`core_alarm`.`ip`) IN (SELECT U0.`ip` FROM `core_alarm` U0 INNER JOIN `core_business` U2 ON ( U0.`business_id` = U2.`busid` ) WHERE (U0.`begin_time` > '2020-12-10 00:00:00' AND U0.`type` = 3 AND NOT (U2.`name` LIKE '%测试%' AND U2.`name` IS NOT NULL) AND NOT (U2.`name` LIKE '%离线%' AND U2.`name` IS NOT NULL) AND NOT (U2.`name` LIKE '%开发机%' AND U2.`name` IS NOT NULL) AND NOT (U2.`name` LIKE '%母机%' AND U2.`name` IS NOT NULL) AND NOT (U2.`name` LIKE '%其它%' AND U2.`name` IS NOT NULL))) AND `core_alarm`.`type` = 3 AND NOT (`core_business`.`name` LIKE '%测试%' AND `core_business`.`name` IS NOT NULL) AND NOT (`core_business`.`name` LIKE '%离线%' AND `core_business`.`name` IS NOT NULL) AND NOT (`core_business`.`name` LIKE '%开发机%' AND `core_business`.`name` IS NOT NULL)AND NOT (`core_business`.`name` LIKE '%母机%' AND `core_business`.`name` IS NOT NULL) AND NOT (`core_business`.`name` LIKE '%其它%' AND `core_business`.`name` IS NOT NULL)) ORDER BY `core_alarm`.`begin_time` ASC;
其实从业务层面考虑,该SQL实现的需求也并不过分。上述语句具体的功能为: 查看各个业务(表core_business)相关设备(core_ipextend)的告警信息(core_alarm),然后排除掉“开发”、“测试”、“离线”等现网环境的信息(name not like条件部分),最后将结果按告警时间最新的排障前面。 这个功能需求,涉及到了多表关联、子查询、文本检索、模糊查询、结果集排序。 从一开始数据量较小的时候是没有什么问题的,但随着业务发展,各个业务模块主机壮大,设备量的增长以及告警需求的变多,问题就会变得越来越明显。 对MySQL了解的同学应该清楚,当前优化器及特性决定了MySQL确实更适合根据主键或索引id精确扫描返回少量数据、轻量逻辑读、SQL简单的OLTP场景。 整体来说,MySQL属于偏轻量级的DB产品,要保证系统的健康平稳,需要良好的开发使用规范加持。类似text等流量较大、IO较重的,尤其是复杂SQL场景,MySQL会表现得不尽人意。 像上面这种SQL,如果从开始选型就决定要使用MySQL,那么应用的逻辑实现就不应该这样设计,你让MySQL臣妾如何做得到。 再者,哪怕是MySQL-8.0丰富了不少优化器的能力,如果规范约束不到,恐怕这种场景拿来上线,恐怕也会不尽人意。本案例中MySQL用的是5.7.17版本。 上面这个语句,执行耗时 18.44秒
mysql>SELECT `core_alarm`.`ip`, `core_alarm`.`business_id`, `core_business`.`owners`, `core_ipextend`.`owner`, `core_alarm`.`value`, `core_alarm`.`alarm_count`, `core_alarm`.`begin_time`, `core_alarm`.`type`, `core_alarm`.`description`, `core_ipextend`.`svr_device_class_name`, `core_alarm`.`context`, `core_alarm`.`finish_time` FROM `core_alarm` INNER JOIN `core_ipextend` ON ( `core_alarm`.`ip` = `core_ipextend`.`ip` ) INNER JOIN `core_business` ON ( `core_alarm`.`business_id` = `core_business`.`busid` ) WHERE (`core_alarm`.`begin_time` > '2020-12-02 00:00:00' AND (`core_alarm`.`ip`) IN (SELECT U0.`ip` FROM `core_alarm` U0 INNER JOIN `core_business` U2 ON ( U0.`business_id` = U2.`busid` ) WHERE (U0.`begin_time` > '2020-12-10 00:00:00' AND U0.`type` = 3 AND NOT (U2.`name` LIKE  '%测试%' AND U2.`name` IS NOT NULL) AND NOT (U2.`name` LIKE  '%离线%' AND U2.`name` IS NOT NULL) AND NOT (U2.`name` LIKE  '%开发机%' AND U2.`name` IS NOT NULL) AND NOT (U2.`name` LIKE  '%母机%' AND U2.`name` IS NOT NULL) AND NOT (U2.`name` LIKE  '%其它%' AND U2.`name` IS NOT NULL))) AND `core_alarm`.`type` = 3 AND NOT (`core_business`.`name` LIKE  '%测试%' AND `core_business`.`name` IS NOT NULL) AND NOT (`core_business`.`name` LIKE  '%离线%' AND `core_business`.`name` IS NOT NULL) AND NOT (`core_business`.`name` LIKE  '%开发机%' AND `core_business`.`name` IS NOT NULL)AND NOT (`core_business`.`name` LIKE  '%母机%' AND `core_business`.`name` IS NOT NULL) AND NOT (`core_business`.`name` LIKE  '%其它%' AND `core_business`.`name` IS NOT NULL)) ORDER BY `core_alarm`.`begin_time` ASC;+---------------+-------------+-------------------------------------+-------------+-------+-------------+----------------------------+------+-------------+-----------------------+-------------+----------------------------+| ip            | business_id | owners                              | owner       | value | alarm_count | begin_time                 | type | description | svr_device_class_name | context     | finish_time                |+---------------+-------------&
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值