一条SQL引起的系统不可用

19 篇文章 1 订阅

一.前言

最近在运维系统,系统对客端突然报了403错误,从后台看发现了大量的慢SQL,导致查询超时,仔细分析我从来没见过那么厚颜无耻的SQL,一条SQL语句关联了一个大表(6000数据)查询了10次。我也很少见过一个SQL语句写了500多行。将一个很大的任务放在一个SQL里计算。以前能跑得起来是因为数据量少,现在表的数据量增加到6000万。性能急剧下降。

二、慢SQL分析(没见过如此厚颜无耻的SQL)

select
  count(1) as cnt,
  user_tag_user_life_period as label_value,
  'user_tag_user_life_period' as label_english
from
  data_tag.tag_user_attribute_all
where
  one_id global in (
    select
      one_id
    from
      data_dwd.big_table final
    where
      (
        (
          case
            when reg_time_taxfree is not null then 1
            else 0
          end
        ) + (
          case
            when reg_time_hotel is not null then 1
            else 0
          end
        ) + (
          case
            when reg_time_travel is not null then 1
            else 0
          end
        ) + (
          case
            when reg_time_invest is not null then 1
            else 0
          end
        )
      ) >= 2
  )
group by
  user_tag_user_life_period
having
  user_tag_user_life_period is not null
  and user_tag_user_life_period <> ''
order by
  cnt desc
limit
  1
union all
select
  count(1) as cnt,
  user_tag_level_name_taxfree as label_value,
  'user_tag_level_name_taxfree' as label_english
from
  data_tag.tag_user_attribute_all
where
  one_id global in (
    select
      one_id
    from
      data_dwd.big_table final
    where
      (
        (
          case
            when reg_time_taxfree is not null then 1
            else 0
          end
        ) + (
          case
            when reg_time_hotel is not null then 1
            else 0
          end
        ) + (
          case
            when reg_time_travel is not null then 1
            else 0
          end
        ) + (
          case
            when reg_time_invest is not null then 1
            else 0
          end
        )
      ) >= 2
  )
group by
  user_tag_level_name_taxfree
having
  user_tag_level_name_taxfree is not null
  and user_tag_level_name_taxfree <> ''
order by
  cnt desc
limit
  1
union all
select
  count(1) as cnt,
  user_tag_level_name_travel as label_value,
  'user_tag_level_name_travel' as label_english
from
  data_tag.tag_user_attribute_all
where
  one_id global in (
    select
      one_id
    from
      data_dwd.big_table final
    where
      (
        (
          case
            when reg_time_taxfree is not null then 1
            else 0
          end
        ) + (
          case
            when reg_time_hotel is not null then 1
            else 0
          end
        ) + (
          case
            when reg_time_travel is not null then 1
            else 0
          end
        ) + (
          case
            when reg_time_invest is not null then 1
            else 0
          end
        )
      ) >= 2
  )
group by
  user_tag_level_name_travel
having
  user_tag_level_name_travel is not null
  and user_tag_level_name_travel <> ''
order by
  cnt desc
limit
  1
union all
select
  count(1) as cnt,
  user_tag_level_name_hotel as label_value,
  'user_tag_level_name_hotel' as label_english
from
  data_tag.tag_user_attribute_all
where
  one_id global in (
    select
      one_id
    from
      data_dwd.big_table final
    where
      (
        (
          case
            when reg_time_taxfree is not null then 1
            else 0
          end
        ) + (
          case
            when reg_time_hotel is not null then 1
            else 0
          end
        ) + (
          case
            when reg_time_travel is not null then 1
            else 0
          end
        ) + (
          case
            when reg_time_invest is not null then 1
            else 0
          end
        )
      ) >= 2
  )
group by
  user_tag_level_name_hotel
having
  user_tag_level_name_hotel is not null
  and user_tag_level_name_hotel <> ''
order by
  cnt desc
limit
  1
union all
select
  count(1) as cnt,
  user_tag_taxfree_and_travel_sex as label_value,
  'user_tag_taxfree_and_travel_sex' as label_english
from
  data_tag.tag_user_attribute_all
where
  one_id global in (
    select
      one_id
    from
      data_dwd.big_table final
    where
      (
        (
          case
            when reg_time_taxfree is not null then 1
            else 0
          end
        ) + (
          case
            when reg_time_hotel is not null then 1
            else 0
          end
        ) + (
          case
            when reg_time_travel is not null then 1
            else 0
          end
        ) + (
          case
            when reg_time_invest is not null then 1
            else 0
          end
        )
      ) >= 2
  )
group by
  user_tag_taxfree_and_travel_sex
having
  user_tag_taxfree_and_travel_sex is not null
  and user_tag_taxfree_and_travel_sex <> ''
order by
  cnt desc
limit
  1
union all
select
  count(1) as cnt,
  user_tag_taxfree_and_travel_user as label_value,
  'user_tag_taxfree_and_travel_user' as label_english
from
  data_tag.tag_user_attribute_all
where
  one_id global in (
    select
      one_id
    from
      data_dwd.big_table final
    where
      (
        (
          case
            when reg_time_taxfree is not null then 1
            else 0
          end
        ) + (
          case
            when reg_time_hotel is not null then 1
            else 0
          end
        ) + (
          case
            when reg_time_travel is not null then 1
            else 0
          end
        ) + (
          case
            when reg_time_invest is not null then 1
            else 0
          end
        )
      ) >= 2
  )
group by
  user_tag_taxfree_and_travel_user
having
  user_tag_taxfree_and_travel_user is not null
  and user_tag_taxfree_and_travel_user <> ''
order by
  cnt desc
limit
  1
union all
select
  count(1) as cnt,
  user_tag_taxfree_and_hotel_sex as label_value,
  'user_tag_taxfree_and_hotel_sex' as label_english
from
  data_tag.tag_user_attribute_all
where
  one_id global in (
    select
      one_id
    from
      data_dwd.big_table final
    where
      (
        (
          case
            when reg_time_taxfree is not null then 1
            else 0
          end
        ) + (
          case
            when reg_time_hotel is not null then 1
            else 0
          end
        ) + (
          case
            when reg_time_travel is not null then 1
            else 0
          end
        ) + (
          case
            when reg_time_invest is not null then 1
            else 0
          end
        )
      ) >= 2
  )
group by
  user_tag_taxfree_and_hotel_sex
having
  user_tag_taxfree_and_hotel_sex is not null
  and user_tag_taxfree_and_hotel_sex <> ''
order by
  cnt desc
limit
  1
union all
select
  count(1) as cnt,
  user_tag_taxfree_and_hotel_user as label_value,
  'user_tag_taxfree_and_hotel_user' as label_english
from
  data_tag.tag_user_attribute_all
where
  one_id global in (
    select
      one_id
    from
      data_dwd.big_table final
    where
      (
        (
          case
            when reg_time_taxfree is not null then 1
            else 0
          end
        ) + (
          case
            when reg_time_hotel is not null then 1
            else 0
          end
        ) + (
          case
            when reg_time_travel is not null then 1
            else 0
          end
        ) + (
          case
            when reg_time_invest is not null then 1
            else 0
          end
        )
      ) >= 2
  )
group by
  user_tag_taxfree_and_hotel_user
having
  user_tag_taxfree_and_hotel_user is not null
  and user_tag_taxfree_and_hotel_user <> ''
order by
  cnt desc
limit
  1
union all
select
  count(1) as cnt,
  user_tag_last_year_amount as label_value,
  'user_tag_last_year_amount' as label_english
from
  data_tag.tag_user_attribute_all
where
  one_id global in (
    select
      one_id
    from
      data_dwd.big_table final
    where
      (
        (
          case
            when reg_time_taxfree is not null then 1
            else 0
          end
        ) + (
          case
            when reg_time_hotel is not null then 1
            else 0
          end
        ) + (
          case
            when reg_time_travel is not null then 1
            else 0
          end
        ) + (
          case
            when reg_time_invest is not null then 1
            else 0
          end
        )
      ) >= 2
  )
group by
  user_tag_last_year_amount
having
  user_tag_last_year_amount is not null
  and user_tag_last_year_amount <> ''
order by
  cnt desc
limit
  1
union all
select
  count(1) as cnt,
  user_tag_last_year_frequency as label_value,
  'user_tag_last_year_frequency' as label_english
from
  data_tag.tag_user_attribute_all
where
  one_id global in (
    select
      one_id
    from
      data_dwd.big_table final
    where
      (
        (
          case
            when reg_time_taxfree is not null then 1
            else 0
          end
        ) + (
          case
            when reg_time_hotel is not null then 1
            else 0
          end
        ) + (
          case
            when reg_time_travel is not null then 1
            else 0
          end
        ) + (
          case
            when reg_time_invest is not null then 1
            else 0
          end
        )
      ) >= 2
  )
group by
  user_tag_last_year_frequency
having
  user_tag_last_year_frequency is not null
  and user_tag_last_year_frequency <> ''
order by
  cnt desc
limit 1;

这个SQL引发系统403,

一个重要设计缺陷是查询频繁。

第二个是这个查询放在对客端的微服务中开启了线程,这种设计严重影响对客端的性能和流畅度,这种业务应放在后台管理系统计算,而不是对客端。

第三、就是这个业务没有进行分解,是个大业务SQL 。

第四、这是一个无耻不考虑后果的SQL。没有考虑到单表数据量的暴增。

三、如何解决慢SQL和避免慢SQL

解决慢SQL(慢查询)和避免慢SQL是数据库优化中的关键任务。以下是一些建议和方法,可以帮助你解决和避免慢SQL:

1. 优化查询语句

  • 使用索引:确保查询中使用的字段都已经建立了索引,这可以大大提高查询速度。
  • **避免SELECT ***:只选择需要的字段,而不是选择所有字段。
  • 使用连接(JOIN)代替子查询:当可能时,使用JOIN操作代替子查询。
  • 优化WHERE子句:避免在WHERE子句中使用函数或计算,这会导致索引失效。

2. 优化数据库设计

  • 正规化:通过正规化来减少数据冗余。
  • 反正规化:在某些情况下,为了查询性能,可以故意引入一些冗余。
  • 分区:将大表分区,可以提高查询性能。

3. 优化数据库配置

  • 调整缓存大小:根据工作负载调整数据库的缓存大小。
  • 调整I/O性能:确保数据库服务器有足够的I/O性能。
  • 监控和调整并发连接数:根据实际需要调整最大并发连接数。

4. 使用分析工具

  • 慢查询日志:启用数据库的慢查询日志功能,找出执行时间长的查询。
  • EXPLAIN计划:使用EXPLAIN语句查看查询的执行计划,找出性能瓶颈。

5. 硬件和存储优化

  • 使用更快的存储:例如,使用SSD替代HDD。
  • 增加内存:为数据库服务器增加更多的内存。
  • 优化I/O配置:确保I/O子系统(如RAID配置)是最优的。

6. 避免常见错误

  • 避免在循环中执行查询:这会导致大量的数据库连接和查询。
  • 避免使用LIKE操作符进行前缀模糊匹配:这会导致全表扫描。

7. 定期维护

  • 更新统计信息:定期更新数据库的统计信息,以便优化器做出更好的决策。
  • 重建索引:定期重建或优化索引,保持其性能。

8. 考虑使用缓存

  • 查询缓存:某些数据库支持查询缓存,可以考虑启用。
  • 外部缓存:如Redis或Memcached,用于缓存热点数据。

9. 考虑分布式解决方案

  • 读写分离:将读操作和写操作分离到不同的服务器上。
  • 分片:将数据分布到多个数据库服务器上。

10. 持续监控和学习

  • 监控数据库性能:使用监控工具持续监控数据库性能。
  • 持续学习:数据库技术和最佳实践在不断变化,保持学习是关键。

 四、常见SQL优化方法

常见的SQL优化方法包括以下几个方面:

  1. 选择特定字段:尽量避免使用SELECT *,而是选择你真正需要的具体字段。这样可以减少不必要的数据传输和处理,从而提高查询效率。

  2. 使用索引:确保查询中使用的字段都已经建立了索引。索引可以大大提高查询速度,因为数据库可以快速定位到数据而不需要全表扫描。

  3. 优化WHERE子句

    • 避免在WHERE子句中使用函数或计算,因为这会导致索引失效。
    • 尽量避免使用OR来连接条件,因为当OR两边的字段不是索引字段时,查询可能不走索引。
    • 尽量避免在索引列上使用MySQL的内置函数。
  4. 优化JOIN操作:优先使用INNER JOIN,如果是LEFT JOIN,确保左边表的结果集尽量小。

  5. 使用LIMIT:当只需要一条或少数几条记录时,使用LIMIT来限制返回的结果集大小。

  6. 优化LIKE查询:尽量避免使用前缀模糊查询(如LIKE '%li%'),因为它会导致全表扫描。如果可能,尽量使用后缀模糊查询(如LIKE 'li%')。

  7. 避免使用子查询:当可能时,使用JOIN操作代替子查询。

  8. 优化排序操作:如果排序字段没有用到索引,尽量减少排序操作。

  9. 考虑表的设计:正规化和反正规化可以影响查询性能。确保你的表设计是合理的,并且考虑了查询性能。

  10. 使用分析工具:利用数据库的慢查询日志功能和EXPLAIN计划来找出性能瓶颈。

  11. 硬件和存储优化:确保数据库服务器有足够的硬件资源,如内存和I/O性能。使用更快的存储,如SSD,也可以提高性能。

  12. 避免常见错误:例如,避免在循环中执行查询,这会导致大量的数据库连接和查询。

  13. 定期维护:更新统计信息,重建或优化索引,以保持数据库性能。

  14. 考虑使用缓存:例如,使用查询缓存或外部缓存(如Redis或Memcached)来缓存热点数据。

  15. 持续监控和学习:使用监控工具持续监控数据库性能,并随着技术和最佳实践的发展保持学习。

结合这些策略和方法,你可以有效地优化SQL查询,提高数据库性能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

奋力向前123

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值