如何编写高性能sql语句

一、什么是执行计划?

1)执行计划

执行计划是数据库根据SQL语句和相关表的统计信息作出的一个查询方案,这个方案是由查询优化器自动分析产生的,比如一条SQL语句如果用来从一个 10万条记录的表中查1条记录,那查询优化器会选择“索引查找”方式,如果该表进行了归档,当前只剩下5000条记录了,那查询优化器就会改变方案,采用 “全表扫描”方式。 

可见,执行计划并不是固定的,它是“个性化的”。产生一个正确的“执行计划”有两点很重要:    

a、SQL语句是否清晰地告诉查询优化器它想干什么?  

b、查询优化器得到的数据库统计信息是否是最新的、正确的?

2)定期归档

上文中提到了表归档,那什么是归档?其实就是做一个数据库的存档。

例如:数据库有一张表数据量很大,真正WEB项目只用到一个月内的数据,因此把一个月前的旧数据定期归档,该怎么做?

1 - 创建一个新表,表结构和索引与旧表一模一样

create table table_new like table_old;

2 - 新建存储过程,查询30天的数据并归档进新数据库,然后把30天前的旧数据从旧表里删除

delimiter $
create procedure sp()
begin
insert into tb_new select * from table_old where rectime < NOW()  -  INTERVAL 30 DAY;
delete from db_smc.table_old where rectime < NOW() - INTERVAL 30 DAY;
end

3 - 创建EVENT,每天晚上凌晨00:00定时执行上面的存储过程

create event if not exists event_temp 
on schedule every 1 day
on completion preserve
do call sp();

备注:

第一次执行存储过程的时候因为历史数据过大, 可能发生意外让该次执行没有成功。重新执行时会遇到报错ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction,应急解决方案如下:

1、执行show full processlist;查看所有MySQL线程

2、执行SELECT * FROM information_schema.INNODB_TRX\G; 查看是否有错误线程,线程id在show full processlist;的结果中状态为sleep

3、kill 进程id

二、 统一SQL语句的写法

对于以下两句SQL语句,程序员认为是相同的,数据库查询优化器认为是不同的。      

select * from dual
select * From dual

其实就是大小写不同,查询分析器就认为是两句不同的SQL语句,必须进行两次解析。生成2个执行计划。所以作为程序员,应该保证相同的查询语句在任何地方都一致,多一个空格都不行!

三、SQL语句采用绑定变量   

select * from orderheader where changetime > '2010-10-20 00:00:01'
select * from orderheader where changetime > '2010-09-22 00:00:01'

以上两句语句,查询优化器认为是不同的SQL语句,需要解析两次。如果采用绑定变量

select * from orderheader where changetime > @chgtime

@chgtime变量可以传入任何值,这样大量的类似查询可以重用该执行计划了,这可以大大降低数据库解析SQL语句的负担。一次解析,多次重用,是提高数据库效率的原则。  

四、绑定变量窥测  

事物都存在两面性,绑定变量对大多数OLTP处理是适用的,但是也有例外。比如在where条件中的字段是“倾斜字段”的时候。

“倾斜字段”指该列中的绝大多数的值都是相同的,比如一张人口调查表,其中“民族”这列,90%以上都是汉族。那么如果一个SQL语句要查询30岁的汉族人口有多少,那“民族”这列必然要被放在where条件中。这个时候如果采用绑定变量@nation会存在很大问题。   

试想如果@nation传入的第一个值是“汉族”,那整个执行计划必然会选择表扫描。然后,第二个值传入的是“布依族”,按理说“布依族”占的比例可能只有万分之一,应该采用索引查找。但是,由于重用了第一次解析的“汉族”的那个执行计划,那么第二次也将采用表扫描方式。这个问题就是著名的“绑定变量窥测”,建议对于“倾斜字段”不要采用绑定变量。 

五、mysql分区表

分区表是一种粗粒度,简易的索引策略,适用于大数据的过滤场景.最适合的场景是,没有合适的索引时,对其中几个分区表进行全表扫描.或者只有一个分区表和索引是热点,而且这个分区和索引能够全部存储在内存中.限制单表分区数不要超过150个,并且注意某些导致无法做分区过滤的细节,分区表对于单条记录的查询没有优势,需要注意这类查询的性能.

分区表分为RANGE,LIST,HASH,KEY四种类型,并且分区表的索引是可以局部针对分区表建立的

CREATE TABLE sales (
    id INT AUTO_INCREMENT,
    amount DOUBLE NOT NULL,
    order_day DATETIME NOT NULL,
    PRIMARY KEY(id, order_day)
) ENGINE=Innodb PARTITION BY RANGE(YEAR(order_day)) (
    PARTITION p_2010 VALUES LESS THAN (2010),
    PARTITION p_2011 VALUES LESS THAN (2011),
    PARTITION p_2012 VALUES LESS THAN (2012),
    PARTITION p_catchall VALUES LESS THAN MAXVALUE);

这段语句表示将表内数据按照order_dy的年份范围进行分区,2010年一个区,2011一个,2012一个,剩下的一个.

要注意如果这么做,则order_day必须包含在主键中,且会产生一个问题,就是当年份超过阈值,到了2013,2014时,需要手动创建这些分区

替代方法就是使用HASH

CREATE TABLE sales (
    id INT PRIMARY KEY AUTO_INCREMENT,
    amount DOUBLE NOT NULL,
    order_day DATETIME NOT NULL
) ENGINE=Innodb PARTITION BY HASH(id DIV 1000000);

这种分区表示每100W条数据建立一个分区,且没有阈值范围的影响.

分区表的查询语句如下(查询p_2010分区):

SELECT * FROM `sales` partition(p_2010)

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏PHP实战技术

MySQL性能优化的最佳20+条经验

今天,数据库的操作越来越成为整个应用的性能瓶颈了,这点对于Web应用尤其明显。关于数据库的性能,这并不只是DBA才需要担心的事,而这更是我们程序员需要去关注的事...

33512
来自专栏杨建荣的学习笔记

一条insert语句导致的性能问题分析(二)(r8笔记第43天)

今天对之前描述的问题一条insert语句导致的性能问题分析(一) 进行了进一步的补充。 有一条insert语句的主要性能瓶颈在于insert子句中的查询语句,查...

2905
来自专栏数据和云

崔华:Oracle 里的哈希连接原理解析

崔华,网名 dbsnake Oracle ACE Director,ACOUG 核心专家 编辑手记:感谢崔华授权我们独家转载其精品文章,也欢迎大家向“Oracl...

3005
来自专栏杨建荣的学习笔记

通过错误的sql来测试推理sql的解析过程(二) (r8笔记第7天)

之前总结过一篇 通过错误的sql来测试推理sql的解析过程 也算是以毒攻毒,当然也分析出来一些有意思的内容来,让原本看起来枯燥的内容有了更多的实践意义。 ...

2639
来自专栏Spark学习技巧

浪尖,请问如何确定hive分桶数?

顺便打个广告,更多优质文章和问题答疑及视频教程请点击原文链接,加入浪尖知识星球-Spark技术学院获取。

1345
来自专栏岑玉海

Hive Tunning 补充 关于bucket

  在前面的几篇文章当中一直有一个概念bucketing不清楚到底是怎么回事。   网友南京-李先森给了他收集的一些资料,如下:   Buckets 对指定列计...

2624
来自专栏用户2442861的专栏

MySQL性能优化的最佳20+条经验

今天,数据库的操作越来越成为整个应用的性能瓶颈了,这点对于Web应用尤其明显。关于数据库的性能,这并不只是DBA才需要担心的事,而这更是我们程序员需要去关注的...

502
来自专栏java一日一条

MySQL 性能优化的最佳 20+ 条经验

今天,数据库的操作越来越成为整个应用的性能瓶颈了,这点对于Web应用尤其明显。关于数据库的性能,这并不只是DBA才需要担心的事,而这更是我 们程序员需要去关注的...

342
来自专栏PHP技术

MySQL性能优化的21条经验

1. 为查询缓存优化你的查询 大多数的MySQL服务器都开启了查询缓存。这是提高性最有效的方法之一,而且这是被MySQL的数据库引擎处理的。当有很多相同的查询被...

3078
来自专栏高性能服务器开发

数据库进阶2 Mysql高并发优化

如果不能设计一个合理的数据库模型,不仅会增加客户端和服务器段程序的编程和维护的难度,而且将会影响系统实际运行的性能。所以,在一个系统开始实施之前,完备的数据库模...

641

扫码关注云+社区