数据库

位置:IT落伍者 >> 数据库 >> 浏览文章

实例讲解MYSQL数据库的查询优化技术


发布日期:2019年02月22日
 
实例讲解MYSQL数据库的查询优化技术

数据库系统是管理信息系统的核心基于数据库的联机事务处理(OLTP)以及联机分析处理(OLAP)是银行企业政府等部门最为重要的计算机应用之一从大多数系统的应用实例来看查询操作在各种数据库操作中所占据的比重最大而查询操作所基于的SELECT语句在SQL语句中又是代价最大的语句举例来说如果数据的量积累到一定的程度比如一个银行的账户数据库表信息积累到上百万甚至上千万条记录全表扫描一次往往需要数十分钟甚至数小时如果采用比全表扫描更好的查询策略往往可以使查询时间降为几分钟由此可见查询优化技术的重要性

笔者在应用项目的实施中发现许多程序员在利用一些前端数据库开发工具(如PowerBuilderDelphi等)开发数据库应用程序时只注重用户界面的华丽并不重视查询语句的效率问题导致所开发出来的应用系统效率低下资源浪费严重因此如何设计高效合理的查询语句就显得非常重要本文以应用实例为基础结合数据库理论介绍查询优化技术在现实系统中的运用

分析问题

许多程序员认为查询优化是DBMS(数据库管理系统)的任务与程序员所编写的SQL语句关系不大这是错误的一个好的查询计划往往可以使程序性能提高数十倍查询计划是用户所提交的SQL语句的集合查询规划是经过优化处理之后所产生的语句集合DBMS处理查询计划的过程是这样的在做完查询语句的词法语法检查之后将语句提交给DBMS的查询优化器优化器做完代数优化和存取路径的优化之后由预编译模块对语句进行处理并生成查询规划然后在合适的时间提交给系统处理执行最后将执行结果返回给用户在实际的数据库产品(如OracleSybase等)的高版本中都是采用基于代价的优化方法这种优化能根据从系统字典表所得到的信息来估计不同的查询规划的代价然后选择一个较优的规划虽然现在的数据库产品在查询优化方面已经做得越来越好但由用户提交的SQL语句是系统优化的基础很难设想一个原本糟糕的查询计划经过系统的优化之后会变得高效因此用户所写语句的优劣至关重要系统所做查询优化我们暂不讨论下面重点说明改善用户查询计划的解决方案

解决问题

下面以关系数据库系统Informix为例介绍改善用户查询计划的方法

.合理使用索引

索引是数据库中重要的数据结构它的根本目的就是为了提高查询效率现在大多数的数据库产品都采用IBM最先提出的ISAM索引结构索引的使用要恰到好处其使用原则如下

●在经常进行连接但是没有指定为外键的列上建立索引而不经常连接的字段则由优化器自动生成索引

●在频繁进行排序或分组(即进行group by或order by操作)的列上建立索引

●在条件表达式中经常用到的不同值较多的列上建立检索在不同值少的列上不要建立索引比如在雇员表的性别列上只有两个不同值因此就无必要建立索引如果建立索引不但不会提高查询效率反而会严重降低更新速度

●如果待排序的列有多个可以在这些列上建立复合索引(compound index)

●使用系统工具如Informix数据库有一个tbcheck工具可以在可疑的索引上进行检查在一些数据库服务器上索引可能失效或者因为频繁操作而使得读取效率降低如果一个使用索引的查询不明不白地慢下来可以试着用tbcheck工具检查索引的完整性必要时进行修复另外当数据库表更新大量数据后删除并重建索引可以提高查询速度

.避免或简化排序

应当简化或避免对大型表进行重复的排序当能够利用索引自动以适当的次序产生输出时优化器就避免了排序的步骤以下是一些影响因素

●索引中不包括一个或几个待排序的列

●group by或order by子句中列的次序与索引的次序不一样

●排序的列来自不同的表

为了避免不必要的排序就要正确地增建索引合理地合并数据库表(尽管有时可能影响表的规范化但相对于效率的提高是值得的)如果排序不可避免那么应当试图简化它如缩小排序的列的范围等

.消除对大型表行数据的顺序存取

在嵌套查询中对表的顺序存取对查询效率可能产生致命的影响比如采用顺序存取策略一个嵌套层的查询如果每层都查询那么这个查询就要查询亿行数据避免这种情况的主要方法就是对连接的列进行索引例如两个表学生表(学号姓名年龄……)和选课表(学号课程号成绩)如果两个表要做连接就要在学号这个连接字段上建立索引

还可以使用并集来避免顺序存取尽管在所有的检查列上都有索引但某些形式的where子句强迫优化器使用顺序存取下面的查询将强迫对orders表执行顺序操作

SELECT * FROM orders WHERE (customer_num= AND order_num>) OR order_num=

虽然在customer_num和order_num上建有索引但是在上面的语句中优化器还是使用顺序存取路径扫描整个表因为这个语句要检索的是分离的行的集合所以应该改为如下语句

SELECT * FROM orders WHERE customer_num= AND order_num>

UNION

SELECT * FROM orders WHERE order_num=

这样就能利用索引路径处理查询

.避免相关子查询

一个列的标签同时在主查询和where子句中的查询中出现那么很可能当主查询中的列值改变之后子查询必须重新查询一次查询嵌套层次越多效率越低因此应当尽量避免子查询如果子查询不可避免那么要在子查询中过滤掉尽可能多的行

.避免困难的正规表达式

MATCHES和LIKE关键字支持通配符匹配技术上叫正规表达式但这种匹配特别耗费时间例如SELECT * FROM customer WHERE zipcode LIKE _ _ _

即使在zipcode字段上建立了索引在这种情况下也还是采用顺序扫描的方式如果把语句改为SELECT * FROM customer WHERE zipcode >在执行查询时就会利用索引来查询显然会大大提高速度

另外还要避免非开始的子串例如语句SELECT * FROM customer WHERE zipcode[]>在where子句中采用了非开始子串因而这个语句也不会使用索引

.使用临时表加速查询

把表的一个子集进行排序并创建临时表有时能加速查询它有助于避免多重排序操作而且在其他方面还能简化优化器的工作例如

SELECT custnamercvblesbalance……other columns

FROM custrcvbles

WHERE custcustomer_id = rcvlbescustomer_id

AND rcvbllsbalance>

AND custpostcode>

ORDER BY custname

如果这个查询要被执行多次而不止一次可以把所有未付款的客户找出来放在一个临时文件中并按客户的名字进行排序

SELECT custnamercvblesbalance……other columns

FROM custrcvbles

WHERE custcustomer_id = rcvlbescustomer_id

AND rcvbllsbalance>

ORDER BY custname

INTO TEMP cust_with_balance

然后以下面的方式在临时表中查询

SELECT * FROM cust_with_balance

WHERE postcode>

临时表中的行要比主表中的行少而且物理顺序就是所要求的顺序减少了磁盘I/O所以查询工作量可以得到大幅减少

注意临时表创建后不会反映主表的修改在主表中数据频繁修改的情况下注意不要丢失数据

.用排序来取代非顺序存取

非顺序磁盘存取是最慢的操作表现在磁盘存取臂的来回移动SQL语句隐藏了这一情况使得我们在写应用程序时很容易写出要求存取大量非顺序页的查询

有些时候用数据库的排序能力来替代非顺序的存取能改进查询

实例分析

下面我们举一个制造公司的例子来说明如何进行查询优化制造公司数据库中包括个表模式如下所示

.part表

零件号零件描述其他列

(part_num)(part_desc)(other column)

Seageat G disk……

Novel M network card……

……

.vendor表

厂商号厂商名其他列

(vendor _num)(vendor_name) (other column)

Seageat Corp……

IBM Corp……

……

.parven表

零件号厂商号零件数量

(part_num)(vendor_num)(part_amount)





……

下面的查询将在这些表上定期运行并产生关于所有零件数量的报表

SELECT part_descvendor_namepart_amount

FROM partvendorpar               

上一篇:MYSQL中怎样设列的默认值为Now()的介绍

下一篇:数据从sqlserver导入mysql数据库的体验