mysql提高查询速度

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了mysql提高查询速度相关的知识,希望对你有一定的参考价值。

怎么在大量数据中提高运行速度

1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。
2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:
select id from t where num is null
可以在num上设置默认值0,确保表中num列没有null值,然后这样查询:
select id from t where num=0
3.应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描。
4.应尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描,如:
select id from t where num=10 or num=20
可以这样查询:
select id from t where num=10
union all
select id from t where num=20
5.in 和 not in 也要慎用,否则会导致全表扫描,如:
select id from t where num in(1,2,3)
对于连续的数值,能用 between 就不要用 in 了:
select id from t where num between 1 and 3
6.下面的查询也将导致全表扫描:
select id from t where name like \'%abc%\'
若要提高效率,可以考虑全文检索。
7.如果在 where 子句中使用参数,也会导致全表扫描。因为SQL只有在运行时才会解析局部变量,但优化程序不能将访问计划的选择推迟到运行时;它必须在编译时进行选择。然而,如果在编译时建立访问计划,变量的值还是未知的,因而无法作为索引选择的输入项。如下面语句将进行全表扫描:
select id from t where num=@num
可以改为强制查询使用索引:
select id from t with(index(索引名)) where num=@num
8.应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。如:
select id from t where num/2=100
应改为:
select id from t where num=100*2
9.应尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。如:
select id from t where substring(name,1,3)=\'abc\'--name以abc开头的id
select id from t where datediff(day,createdate,\'2005-11-30\')=0--‘2005-11-30’生成的id
应改为:
select id from t where name like \'abc%\'
select id from t where createdate>=\'2005-11-30\' and createdate<\'2005-12-1\'
10.不要在 where 子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。
11.在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使用,并且应尽可能的让字段顺序与索引顺序相一致。
12.不要写一些没有意义的查询,如需要生成一个空表结构:
select col1,col2 into #t from t where 1=0
这类代码不会返回任何结果集,但是会消耗系统资源的,应改成这样:
create table #t(...)
13.很多时候用 exists 代替 in 是一个好的选择:
select num from a where num in(select num from b)
用下面的语句替换:
select num from a where exists(select 1 from b where num=a.num)
14.并不是所有索引对查询都有效,SQL是根据表中数据来进行查询优化的,当索引列有大量数据重复时,SQL查询可能不会去利用索引,如一表中有字段sex,male、female几乎各一半,那么即使在sex上建了索引也对查询效率起不了作用。
15.索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insert 及 update 的效率,因为 insert 或 update 时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定。一个表的索引数最好不要超过6个,若太多则应考虑一些不常使用到的列上建的索引是否有必要。
16.应尽可能的避免更新 clustered 索引数据列,因为 clustered 索引数据列的顺序就是表记录的物理存储顺序,一旦该列值改变将导致整个表记录的顺序的调整,会耗费相当大的资源。若应用系统需要频繁更新 clustered 索引数据列,那么需要考虑是否应将该索引建为 clustered 索引。
17.尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。这是因为引擎在处理查询和连接时会逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。
18.尽可能的使用 varchar/nvarchar 代替 char/nchar ,因为首先变长字段存储空间小,可以节省存储空间,其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。
19.任何地方都不要使用 select * from t ,用具体的字段列表代替“*”,不要返回用不到的任何字段。
20.尽量使用表变量来代替临时表。如果表变量包含大量数据,请注意索引非常有限(只有主键索引)。
21.避免频繁创建和删除临时表,以减少系统表资源的消耗。
22.临时表并不是不可使用,适当地使用它们可以使某些例程更有效,例如,当需要重复引用大型表或常用表中的某个数据集时。但是,对于一次性事件,最好使用导出表。
23.在新建临时表时,如果一次性插入数据量很大,那么可以使用 select into 代替 create table,避免造成大量 log ,以提高速度;如果数据量不大,为了缓和系统表的资源,应先create table,然后insert。
24.如果使用到了临时表,在存储过程的最后务必将所有的临时表显式删除,先 truncate table ,然后 drop table ,这样可以避免系统表的较长时间锁定。
25.尽量避免使用游标,因为游标的效率较差,如果游标操作的数据超过1万行,那么就应该考虑改写。
26.使用基于游标的方法或临时表方法之前,应先寻找基于集的解决方案来解决问题,基于集的方法通常更有效。
27.与临时表一样,游标并不是不可使用。对小型数据集使用 FAST_FORWARD 游标通常要优于其他逐行处理方法,尤其是在必须引用几个表才能获得所需的数据时。在结果集中包括“合计”的例程通常要比使用游标执行的速度快。如果开发时间允许,基于游标的方法和基于集的方法都可以尝试一下,看哪一种方法的效果更好。
28.在所有的存储过程和触发器的开始处设置 SET NOCOUNT ON ,在结束时设置 SET NOCOUNT OFF 。无需在执行存储过程和触发器的每个语句后向客户端发送 DONE_IN_PROC 消息。
29.尽量避免大事务操作,提高系统并发能力。
30.尽量避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理。
参考技术A 在已有的 mysql 服务器之上使用 Apache Spark (无需将数据导出到 Spark 或者 Hadoop 平台上),这样至少可以提升 10 倍的查询性能。使用多个 MySQL 服务器(复制或者 Percona XtraDB Cluster)可以让我们在某些查询上得到额外的性能提升。你也可以使用 Spark 的缓存功能来缓存整个 MySQL 查询结果表。

思路很简单:Spark 可以通过 JDBC 读取 MySQL 上的数据,也可以执行 SQL 查询,因此我们可以直接连接到 MySQL 并执行查询。那么为什么速度会快呢?对一些需要运行很长时间的查询(如报表或者BI),由于 Spark 是一个大规模并行系统,因此查询会非常的快。MySQL 只能为每一个查询分配一个 CPU 核来处理,而 Spark 可以使用所有集群节点的所有核。在下面的例子中,我们会在 Spark 中执行 MySQL 查询,这个查询速度比直接在 MySQL 上执行速度要快 5 到 10 倍。

另外,Spark 可以增加“集群”级别的并行机制,在使用 MySQL 复制或者 Percona XtraDB Cluster 的情况下,Spark 可以把查询变成一组更小的查询(有点像使用了分区表时可以在每个分区都执行一个查询),然后在多个 Percona XtraDB Cluster 节点的多个从服务器上并行的执行这些小查询。最后它会使用map/reduce 方式将每个节点返回的结果聚合在一起形成完整的结果。

这篇文章跟我之前文章“Airlines On-Time Performance” 所使用的数据库是相同的。瓦迪姆创建了一些脚本可以方便的下载这些数据并上传到 MySQL 数据库。

MySQL查询优化——提高速度和效率

【中文标题】MySQL查询优化——提高速度和效率【英文标题】:MySQL query optimization - increase speed and efficiency 【发布时间】:2012-06-30 04:58:18 【问题描述】:

我想在数据库中获取一些电子邮件,并且每封电子邮件都有一个状态。所有可能的状态都存储在一个表中,它们都具有权限(例如显示、编辑、删除等)。这些电子邮件不是通过网站获得权限的用户,而是用户添加的电子邮件列表。

这是表格结构:

电子邮件表格

如果不存在 `email__email` 则创建表( `email_id` int(11) NOT NULL AUTO_INCREMENT, `created` 时间戳 NULL DEFAULT NULL, `updated` 时间戳 NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `user_fk` int(11) 非空, `status_fk` tinyint(2) 非空, `language` enum('fr','en') 整理 utf8_unicode_ci DEFAULT NULL, `email` varchar(255) 整理 utf8_unicode_ci DEFAULT NULL, `firstName` varchar(255) 整理 utf8_unicode_ci DEFAULT NULL, `lastName` varchar(100) 整理 utf8_unicode_ci DEFAULT NULL, `companyName` varchar(255) 整理 utf8_unicode_ci DEFAULT NULL, `gender` enum('f','m') 整理 utf8_unicode_ci DEFAULT NULL, 主键(`email_id`), 唯一键 `user_email` (`user_fk`,`email`), KEY `user_fk` (`user_fk`), KEY `created` (`created`), KEY `status_fk` (`status_fk`) ) 引擎=InnoDB 默认字符集=utf8 排序=utf8_unicode_ci AUTO_INCREMENT=3031492 ;

状态表

如果不存在 `email__status` 则创建表( `status_id` int(11) NOT NULL AUTO_INCREMENT, `name_fr` varchar(50) 整理 utf8_unicode_ci DEFAULT NULL, `name_en` varchar(50) 整理 utf8_unicode_ci DEFAULT NULL, `description_fr` varchar(150) 整理 utf8_unicode_ci DEFAULT NULL, `description_en` varchar(150) 整理 utf8_unicode_ci DEFAULT NULL, `permShow` tinyint(1) NOT NULL DEFAULT '0', `permSend` tinyint(1) NOT NULL DEFAULT '0', `permEdit` tinyint(1) NOT NULL DEFAULT '0', `permDelete` tinyint(1) NOT NULL DEFAULT '0', `permImport` tinyint(1) NOT NULL DEFAULT '0', 主键(`status_id`) ) 引擎=InnoDB 默认字符集=utf8 排序=utf8_unicode_ci AUTO_INCREMENT=7 ;

这是带有 EXPLAIN 的慢查询:

SELECT EE.*, ES.name_fr AS statusName, ES.description_fr AS statusDescription, ES.permShow, ES.permSend, ES.permEdit, ES.permDelete, ES.permImport , (SELECT GROUP_CONCAT(CONVERT(CONCAT(GC.name, '~', GC.group_id), CHAR(255)) SEPARATOR ',') FROM `group` GC INNER JOIN group_email GEC ON GEC.group_fk = GC.group_id WHERE GEC.email_fk = EE.email_id AND GC.deleted = 0) AS 组 FROM `email__email` EE INNER JOIN email__status ES ON EE.status_fk = ES.status_id 其中 1 = 1 和 EE.user_fk = 54 AND ES.permShow = 1 ORDER BY EE.email_id DESC 限制 15 EXTRA ID KEY KEY_LEN POSSIBLE_KEYS REF ROWS SELECT_TYPE TABLE TYPE 使用临时的;使用文件排序 1 user_email 4 user_email,user_fk,status_fk const 180681 PRIMARY EE ref 使用哪里;使用连接缓冲区 1 [空字符串] [空字符串] PRIMARY [空字符串] 6 PRIMARY ES ALL 使用索引 2 email_fk 4 group_email,group_fk,email_fk mailing_dev.EE.email_id 1 DEPENDENT SUBQUERY GEC ref 使用 where 2 PRIMARY 4 PRIMARY mailing_dev.GEC.group_fk 1 DEPENDENT SUBQUERY GC eq_ref

这是一个带有 EXPLAIN 的快速查询:

选择 EE。* , (SELECT GROUP_CONCAT(CONVERT(CONCAT(GC.name, '~', GC.group_id), CHAR(255)) SEPARATOR ',') FROM `group` GC INNER JOIN group_email GEC ON GEC.group_fk = GC.group_id WHERE GEC.email_fk = EE.email_id AND GC.deleted = 0) AS 组 FROM `email__email` EE 其中 1 = 1 和 EE.user_fk = 54 AND EXISTS(从 email__status 中选择 permShow,其中 status_id = EE.status_fk AND permShow = 1) ORDER BY EE.email_id DESC 限制 15 EXTRA ID KEY KEY_LEN POSSIBLE_KEYS REF ROWS SELECT_TYPE TABLE TYPE 使用 where 1 PRIMARY 4 user_email,user_fk [empty string] 270 PRIMARY EE index 使用 where 3 PRIMARY 4 PRIMARY mailing_dev.EE.status_fk 1 DEPENDENT SUBQUERY email__status eq_ref 使用索引 2 email_fk 4 group_email,group_fk,email_fk mailing_dev.EE.email_id 1 DEPENDENT SUBQUERY GEC ref 使用 where 2 PRIMARY 4 PRIMARY mailing_dev.GEC.group_fk 1 DEPENDENT SUBQUERY GC eq_ref

这两个查询之间存在很大差异,但第二个查询没有给我两个需要获取的重要列。我可以像 join 一样做子查询来获取它们,但我仍然不希望每个子查询都有很多......有什么想法可以改进吗?

谢谢

【问题讨论】:

【参考方案1】:

email__email.status_fk 是一个 tinyint,但 email__status.status_id 是一个 int(11)。

这可能会破坏您的 INNER JOIN。更改一种或另一种数据类型,然后重试。

【讨论】:

同时更改为 tinyint(2),然后更改为 int(11) 但没有更改...我运行优化查询来检查基数但仍然没有任何更改。 EXPLAIN 保留在 Using 临时;在 status_fk 上使用文件排序。不过,我会保留您对数据一致性的建议

以上是关于mysql提高查询速度的主要内容,如果未能解决你的问题,请参考以下文章

如何提高这个 MySQL 查询的速度?

MYSQL查询优化(需要提高速度)

怎么提高数据库查询效率

MySql,我不确定我的索引是不是正确,或者我是不是可以提高查询速度?

如何提高这个mysql查询的速度

如何让mysql速度更快的响应?如何提高读取和查询速度