数据库专题《四》——数据库优化

一、原理分析

1.1、MySQL架构原理

在此之前我们需要先了解一下MySQL的各个部分组件的工作结构,便于我们理解MySQL服务器。如下图:

 

图片 

MySQL工作架构大致可以分为三层,最上面为客户端,比如:连接处理、授权认证、安全等功能都在这一层处理。

MySQL的核心服务在中间这一层,包括查询解析、分析、优化、缓存、内置函数。所有的跨存储引擎的功能也在这一层实现:存储过程、触发器、视图等。

最下层为存储引擎负责MySQL中的数据存储和提取。和Linux下的文件系统类似,每种存储引擎都有其优势和劣势。中间的服务层通过API与存储引擎通信,这些API接口屏蔽了不同存储引擎间的差异。

1.2、MySQL查询原理

我们想让MySQL获得更高的查询性能,首先需要先了解MySQL怎样进行优化和执行查询的。先来看一下,当我们向MySQL发送了一个请求的时候,它都做了些什么呢?

 

图片

1.2.1、客户端/服务端通信协议

MySQL客户端/服务端通信协议不是同时发生的,在任一时刻,要么是服务器向客户端发送数据,要么是客户端向服务器发送数据,一旦一端开始发送消息,另一端要接收完整个消息才能响应它,所以我们无法也无须将一个消息切成小块独立发送,也没有办法进行流量控制。

客户端用一个单独的数据包将查询请求发送给服务器,所以当查询语句很长的时候,需要设置 max_allowed_packet参数。但是需要注意的是,如果查询实在是太大,服务端会拒绝接收更多数据并抛出异常。 

与之相反的是,服务器响应给用户的数据通常会很多,由多个数据包组成。但是当服务器响应客户端请求时,客户端必须完整的接收整个返回结果,而不能简单的只取前面几条结果,然后让服务器停止发送。因而在实际开发中,尽量保持查询简单且只返回必需的数据,减小通信间数据包的大小和数量是一个非常好的习惯,这也是查询中尽量避免使用 SELECT*以及加上 LIMIT限制的原因之一。

1.2.2、查询缓存

在解析一个查询语句前,如果查询缓存是打开的,那么MySQL会检查这个查询语句是否命中查询缓存中的数据。如果当前查询恰好命中查询缓存,在检查一次用户权限后直接返回缓存中的结果。这种情况下,查询不会被解析,也不会生成执行计划,更不会执行。

MySQL将缓存存放在一个引用表(不要理解成 table,可以认为是类似于 HashMap的数据结构),通过一个哈希值索引,这个哈希值通过查询本身、当前要查询的数据库、客户端协议版本号等一些可能影响结果的信息计算得来。所以两个查询在任何字符上的不同(例如:空格、注释),都会导致缓存不会命中。

如果查询中包含任何用户自定义函数、存储函数、用户变量、临时表、mysql库中的系统表,其查询结果 都不会被缓存。比如函数 NOW()或者 CURRENT_DATE()会因为不同的查询时间,返回不同的查询结果,再比如包含 CURRENT_USER或者 CONNECION_ID()的查询语句会因为不同的用户而返回不同的结果,将这样的查询结果缓存起来没有任何的意义。

既然是缓存,就会失效,那查询缓存何时失效呢?MySQL的查询缓存系统会跟踪查询中涉及的每个表,如果这些表(数据或结构)发生变化,那么和这张表相关的所有缓存数据都将失效。正因为如此,在任何的写操作时,MySQL必须将对应表的所有缓存都设置为失效。如果查询缓存非常大或者碎片很多,这个操作就可能带来很大的系统消耗,甚至导致系统僵死一会儿。而且查询缓存对系统的额外消耗也不仅仅在写操作,读操作也不例外:

  1. 任何的查询语句在开始之前都必须经过检查,即使这条SQL语句永远不会命中缓存

  2. 如果查询结果可以被缓存,那么执行完成后,会将结果存入缓存,也会带来额外的系统消耗

基于此,我们要知道并不是什么情况下查询缓存都会提高系统性能,缓存和失效都会带来额外消耗,只有当缓存带来的资源节约大于其本身消耗的资源时,才会给系统带来性能提升。但要如何评估打开缓存是否能够带来性能提升是一件非常困难的事情,也不在本文讨论的范畴内。如果系统确实存在一些性能问题,可以尝试打开查询缓存,并在数据库设计上做一些优化,比如:

  1. 用多个小表代替一个大表,注意不要过度设计

  2. 批量插入代替循环单条插入

  3. 合理控制缓存空间大小,一般来说其大小设置为几十兆比较合适

  4. 可以通过 SQL_CACHE和 SQL_NO_CACHE来控制某个查询语句是否需要进行缓存

 

最后的忠告是不要轻易打开查询缓存,特别是写密集型应用。如果你实在是忍不住,可以将 query_cache_type设置为 DEMAND,这时只有加入 SQL_CACHE的查询才会走缓存,其他查询则不会,这样可以非常自由地控制哪些查询需要被缓存。

当然查询缓存系统本身是非常复杂的,这里讨论的也只是很小的一部分,其他更深入的话题,比如:缓存是如何使用内存的?如何控制内存的碎片化?事务对查询缓存有何影响等等,读者可以自行阅读相关资料,这里权当抛砖引玉吧。

1.2.3、语法解析和预处理

MySQL通过关键字将SQL语句进行解析,并生成一颗对应的解析树。这个过程解析器主要通过语法规则来验证和解析。比如SQL中是否使用了错误的关键字或者关键字的顺序是否正确等等。预处理则会根据MySQL规则进一步检查解析树是否合法。比如检查要查询的数据表和数据列是否存在等等。 

1.2.4、查询优化

经过前面的步骤生成的语法树被认为是合法的了,并且由优化器将其转化成查询计划。多数情况下,一条查询可以有很多种执行方式,最后都返回相应的结果。优化器的作用就是找到这其中最好的执行计划。

MySQL使用基于成本的优化器,它尝试预测一个查询使用某种执行计划时的成本,并选择其中成本最小的一个。在MySQL可以通过查询当前会话的 last_query_cost的值来得到其计算当前查询的成本。

mysql> select * from t_message limit 10;...省略结果集

mysql> show status like 'last_query_cost';+-----------------+-------------+| Variable_name | Value |+-----------------+-------------+| Last_query_cost | 6391.799000 |+-----------------+-------------+

示例中的结果表示优化器认为大概需要做6391个数据页的随机查找才能完成上面的查询。这个结果是根据一些列的统计信息计算得来的,这些统计信息包括:每张表或者索引的页面个数、索引的基数、索引和数据行的长度、索引的分布情况等等。

有非常多的原因会导致MySQL选择错误的执行计划,比如统计信息不准确、不会考虑不受其控制的操作成本(用户自定义函数、存储过程)、MySQL认为的最优跟我们想的不一样(我们希望执行时间尽可能短,但MySQL值选择它认为成本小的,但成本小并不意味着执行时间短)等等。

MySQL的查询优化器是一个非常复杂的部件,它使用了非常多的优化策略来生成一个最优的执行计划:

  • 重新定义表的关联顺序(多张表关联查询时,并不一定按照SQL中指定的顺序进行,但有一些技巧可以指定关联顺序)
  • 优化 MIN()和 MAX()函数(找某列的最小值,如果该列有索引,只需要查找B+Tree索引最左端,反之则可以找到最大值,具体原理见下文)
  • 提前终止查询(比如:使用Limit时,查找到满足数量的结果集后会立即终止查询)
  • 优化排序(在老版本MySQL会使用两次传输排序,即先读取行指针和需要排序的字段在内存中对其排序,然后再根据排序结果去读取数据行,而新版本采用的是单次传输排序,也就是一次读取所有的数据行,然后根据给定的列排序。对于I/O密集型应用,效率会高很多) 

随着MySQL的不断发展,优化器使用的优化策略也在不断的进化,这里仅仅介绍几个非常常用且容易理解的优化策略,其他的优化策略,大家自行查阅吧。

1.2.5、查询执行引擎

在完成解析和优化阶段以后,MySQL会生成对应的执行计划,查询执行引擎根据执行计划给出的指令逐步执行得出结果。整个执行过程的大部分操作均是通过调用存储引擎实现的接口来完成,这些接口被称为 handler API。查询过程中的每一张表由一个 handler实例表示。实际上,MySQL在查询优化阶段就为每一张表创建了一个 handler实例,优化器可以根据这些实例的接口来获取表的相关信息,包括表的所有列名、索引统计信息等。存储引擎接口提供了非常丰富的功能,但其底层仅有几十个接口,这些接口像搭积木一样完成了一次查询的大部分操作。

1.2.6、返回结果给客户端

查询执行的最后一个阶段就是将结果返回给客户端。即使查询不到数据,MySQL仍然会返回这个查询的相关信息,比如改查询影响到的行数以及执行时间等等。

如果查询缓存被打开且这个查询可以被缓存,MySQL也会将结果存放到缓存中。

结果集返回客户端是一个增量且逐步返回的过程。有可能MySQL在生成第一条结果时,就开始向客户端逐步返回结果集了。这样服务端就无须存储太多结果而消耗过多内存,也可以让客户端第一时间获得返回结果。需要注意的是,结果集中的每一行都会以一个满足①中所描述的通信协议的数据包发送,再通过TCP协议进行传输,在传输过程中,可能对MySQL的数据包进行缓存然后批量发送。 

回头总结一下MySQL整个查询执行过程,总的来说分为6个步骤:

  1. 客户端向MySQL服务器发送一条查询请求

  2. 服务器首先检查查询缓存,如果命中缓存,则立刻返回存储在缓存中的结果。否则进入下一阶段

  3. 服务器进行SQL解析、预处理、再由优化器生成对应的执行计划

  4. MySQL根据执行计划,调用存储引擎的API来执行查询

  5. 将结果返回给客户端,同时缓存查询结果

 二、优化方案

2.1、优化建议 

看了以上原理,想必你现在已经很清楚MySQL的运行原理了,接下来我将从一下这些方面对其进行优化。

2.1.1、SQL优化

我们需要注意这几点:先选择需要优化的SQL,一般都从Explain和Profile出发,永远用小结果集驱动大的结果集,在索引中完成排序,使用最小的Columns,使用最有效的过滤条件,避免复杂的JOIN和子查询。具体优化如下:

分析SQL执行频率

show status

例如:分析读为主,还是写为主

定位效率低的SQL

慢查询日志定位
-log-slow-queries = xxx(指定文件名)

SHOW PROCESSLIST
查看当前正在进行的线程,包括线程状态、是否锁表

分析SQL执行计划

explain "your sql"

desc "your sql"

- 部分参数分析
select_type: 
SIMPLE 简单表,不使用表连接或子查询
PRIMARY 主查询,即外层的查询
UNION 
SUBQUER 子查询的第一个select

type: 
ALL 全表扫描
index 索引全扫描
range 索引范围扫描
ref 使用非唯一索引或唯一索引的前缀扫描
eq_ref 类似ref,使用的索引是唯一索引
const/system 单表中最多有一个匹配行
NULL 不用访问表或者索引,直接得到结果

 

show profile 分析SQL

select @@have_profiling 是否支持
select @@profiling 是否开启

执行 "your sql"
show profiles 
show profile block io for QUERY 17

2.1.2、索引优化

这里我从三个方面来分析,分别是类型,方法,创建:

图片

图片

 

 

示例如下:

索引的存储分类

B-TREE索引:常见,大部分都支持
HASH索引:只有memory引擎支持
R-TREE索引:空间索引是MyISAM的一个特殊索引类型,主要用于地理空间数据类型
full-text索引:全文索引,MyISAM的一个特殊索引类型,innodb从5.6开始支持

索引的创建与删除

添加索引
ALTER Table `table_name` ADD PRIMARY KEY(`column`)
ALTER Table `table_name` ADD UNIQUE(`column`)
ALTER Table `table_name` ADD INDEX(`column`)
ALTER Table `table_name` ADD FULLTEXT(`column`)

删除
ALTER Table `table_name` drop index index_name

 

MySQL能使用索引的情况 

匹配全值
匹配值范围查询
匹配最左前缀
仅仅对索引进行查询(覆盖查询)
匹配列前缀 (添加前缀索引)
部分精确+部分范围

不能使用索引的情况

以%开关的like查询
数据类型出现隐式转换
复合索引查询条件不包含最左部分
使用索引仍比全表扫描慢
用or分割开的条件
 

2.1.3、语句优化

定期优化表

optimize table table_name 合并表空间碎片,对MyISAM、BDB、INNODB有效

如果提示不支持,可以用 mysql --skip-new 或者 mysql --safe-mode 来重启,以便让其他引擎支持

常用优化

尽量避免全表扫描,对where及orderby的列建立索引
尽量避免where使用 != 或 <>
尽量避免where子句用 or 连接条件
乱用%导致全表扫描
尽量避免where子句对字段进行表达式操作
尽量避免where子句对字段进行函数操作
覆盖查询,返回需要的字段
优化嵌套查询,关联查询优于子查询
组合索引或复合索引,最左索引原则
用exist代替in
当索引列有大量重复数据时,SQL查询可能不会去利用索引

2.1.4、JOIN的优化

JOIN原理

在mysql中使用Nested Loop Join来实现join;
   A JOIN B:通过A表的结果集作为循环基础,一条一条的通过结果集中的数据作为过滤条件到下一个表中查询数据,然后合并结果

 

JOIN优化原则

1,尽可能减少Join 语句中的Nested Loop 的循环总次数,用小结果集驱动大结果集;
2,优先优化Nested Loop 的内层循环;
3,保证Join 语句中被驱动表上Join 条件字段已经被索引;
4,扩大join buffer的大小;

2.1.5、数据库对象优化

优化表数据类型

PROCEDURE ANALYSE (16,256) 排除多于16个,大于256字节的ENUM建议

"your sql" PROCEDURE ANALYSE ()
 

表拆分

垂直拆分
针对某些列常用、某些列不常用

水平拆分
表很大
表中的数据有独立性,能简单分类
需要在表存放多种介质
 

反范式 

增加冗余列、增加派生列、重新组表和分割表 

使用中间表 

数据查询量大
数据统计、分析场景
 

2.1.6、优化MySQL服务器

MySQL的引擎对比:

 

图片

表引擎的命令: 

show engines; 查看myql所支持的存储引擎
show variables like '%storage_engine'; 查看mysql默认的存储引擎
show create table table_name 查看具体表使用的存储引擎
 

InnoDB

1. 提供事务、回滚、系统崩溃修复能力、多版本并发控制事务
2. 支持自增列
3. 支持外键
4. 支持事务以及事务相关联功能
5. 支持mvcc的行级锁

MyISAM

1. 不支持事务、不支持行级锁,只支持并发插入的表锁,主要用于高负载的select
2. 支持三种不同的存储结构:静态、动态、压缩

2.1.7、MySQL并发参数调整

MySQL并发参数 

max_connections 最大连接数,默认151

back_log 短时间内处理大量连接,可适当增大

table_open_cache 控制所有SQL执行线程可打开表缓存的数量,受其他参数制约

thread_cache_size 控制缓存客户服务线程数量,加快数据库连接速度,根据threads_created/connections来衡量是否合适

innodb_lock_wait_timeout 控制事务等待行锁时间,默认50ms

2.1.8、优化内存提高MySQL后台服务

MyISAM内存优化

#修改相应服务器位置的配置文件 my.cnf

key_buffer_size
决定myisam索引块缓存区的大小,直接影响表的存取效率,建议1/4可用内存

read_buffer 读缓存

write_buffer 写缓存

InnoDB内存优化

innodb_buffer_pool_size 存储引擎表数据和索引数据的最大缓存区大小

innodb_old_blocks_pct LRU算法 决定old sublist的比例

innodb_old_blocks_time LRU算法 数据转移间隔时间

2.1.9、应用的优化

使用原因

数据的重要性
mysql 服务及自身性能瓶颈
保证大型系统稳定可靠运行

优化方法

使用连接池

减少对 mysql 的真实连接
a. 避免相同数据重复执行(查询缓存)
b. 使用 mysql 缓存(sql 缓存)

负载均衡
a. LVS 分布式
b. 读写分离(主主复制、主从复制保证数据一致性

 

三、调优实例

前两部分过于理论化了,现在换个思路,就以简单场景来描述数据库调优,相比大家更能接受。

1、选取最适用的字段属性

MySQL可以很好的支持大数据量的存取,但是一般说来,数据库中的表越小,在它上面执行的查询也就会越快。因此,在创建表的时候,为了获得更好的性能,我们可以将表中字段的宽度设得尽可能小。

例如,在定义邮政编码这个字段时,如果将其设置为CHAR(255),显然给数据库增加了不必要的空间,甚至使用VARCHAR这种类型也是多余的,因为CHAR(6)就可以很好的完成任务了。同样的,如果可以的话,我们应该使用MEDIUMINT而不是BIGIN来定义整型字段。

另外一个提高效率的方法是在可能的情况下,应该尽量把字段设置为NOTNULL,这样在将来执行查询的时候,数据库不用去比较NULL值。
对于某些文本字段,例如“省份”或者“性别”,我们可以将它们定义为ENUM类型。因为在MySQL中,ENUM类型被当作数值型数据来处理,而数值型数据被处理起来的速度要比文本类型快得多。这样,我们又可以提高数据库的性能。

2、使用连接(JOIN)来代替子查询(Sub-Queries)

MySQL从4.1开始支持SQL的子查询。这个技术可以使用SELECT语句来创建一个单列的查询结果,然后把这个结果作为过滤条件用在另一个查询中。例如,我们要将客户基本信息表中没有任何订单的客户删除掉,就可以利用子查询先从销售信息表中将所有发出订单的客户ID取出来,然后将结果传递给主查询,如下所示:

DELETE FROM customerinfo

WHERE CustomerID NOT IN (SELECT CustomerID FROM salesinfo)

使用子查询可以一次性的完成很多逻辑上需要多个步骤才能完成的SQL操作,同时也可以避免事务或者表锁死,并且写起来也很容易。但是,有些情况下,子查询可以被更有效率的连接(JOIN)..替代。例如,假设我们要将所有没有订单记录的用户取出来,可以用下面这个查询完成:

SELECT * FROM customerinfo

WHERE CustomerID NOT IN (SELECTC ustomerID FROM salesinfo)

如果使用连接(JOIN)..来完成这个查询工作,速度将会快很多。尤其是当salesinfo表中对CustomerID建有索引的话,性能将会更好,查询如下:

SELECT * FROM customerinfo

LEFT JOIN salesinfo ON customerinfo.CustomerID=salesinfo.CustomerID

WHERE salesinfo.CustomerID ISNULL

连接(JOIN)..之所以更有效率一些,是因为MySQL不需要在内存中创建临时表来完成这个逻辑上的需要两个步骤的查询工作。

3、使用联合(UNION)来代替手动创建的临时表

MySQL从4.0的版本开始支持union查询,它可以把需要使用临时表的两条或更多的select查询合并的一个查询中。在客户端的查询会话结束的时候,临时表会被自动删除,从而保证数据库整齐、高效。使用union来创建查询的时候,我们只需要用UNION作为关键字把多个select语句连接起来就可以了,要注意的是所有select语句中的字段数目要想同。下面的例子就演示了一个使用UNION的查询。

SELECT Name,Phone FROM client UNION

SELECT Name,BirthDate FROM author UNION

SELECT Name,Supplier FROM product

4、事务

尽管我们可以使用子查询(Sub-Queries)、连接(JOIN)和联合(UNION)来创建各种各样的查询,但不是所有的数据库操作都可以只用一条或少数几条SQL语句就可以完成的。更多的时候是需要用到一系列的语句来完成某种工作。但是在这种情况下,当这个语句块中的某一条语句运行出错的时候,整个语句块的操作就会变得不确定起来。设想一下,要把某个数据同时插入两个相关联的表中,可能会出现这样的情况:第一个表中成功更新后,数据库突然出现意外状况,造成第二个表中的操作没有完成,这样,就会造成数据的不完整,甚至会破坏数据库中的数据。要避免这种情况,就应该使用事务,它的作用是:要么语句块中每条语句都操作成功,要么都失败。换句话说,就是可以保持数据库中数据的一致性和完整性。事物以BEGIN关键字开始,COMMIT关键字结束。在这之间的一条SQL操作失败,那么,ROLLBACK命令就可以把数据库恢复到BEGIN开始之前的状态。

BEGIN; INSERT INTO salesinfo SET CustomerID=14; UPDATE inventory SET Quantity=11 WHERE item='book'; COMMIT;

事务的另一个重要作用是当多个用户同时使用相同的数据源时,它可以利用锁定数据库的方法来为用户提供一种安全的访问方式,这样可以保证用户的操作不被其它的用户所干扰。

5、锁定表

尽管事务是维护数据库完整性的一个非常好的方法,但却因为它的独占性,有时会影响数据库的性能,尤其是在很大的应用系统中。由于在事务执行的过程中,数据库将会被锁定,因此其它的用户请求只能暂时等待直到该事务结束。如果一个数据库系统只有少数几个用户来使用,事务造成的影响不会成为一个太大的问题;但假设有成千上万的用户同时访问一个数据库系统,例如访问一个电子商务网站,就会产生比较严重的响应延迟。

其实,有些情况下我们可以通过锁定表的方法来获得更好的性能。下面的例子就用锁定表的方法来完成前面一个例子中事务的功能。

LOCK TABLE inventory WRITE SELECT Quantity FROM inventory WHERE Item='book';

...

UPDATE inventory SET Quantity=11 WHERE Item='book'; UNLOCKTABLES

这里,我们用一个select语句取出初始数据,通过一些计算,用update语句将新值更新到表中。包含有WRITE关键字的LOCKTABLE语句可以保证在UNLOCKTABLES命令被执行之前,不会有其它的访问来对inventory进行插入、更新或者删除的操作。

6、使用外键

锁定表的方法可以维护数据的完整性,但是它却不能保证数据的关联性。这个时候我们就可以使用外键。

例如,外键可以保证每一条销售记录都指向某一个存在的客户。在这里,外键可以把customerinfo表中的CustomerID映射到salesinfo表中CustomerID,任何一条没有合法CustomerID的记录都不会被更新或插入到salesinfo中。

CREATE    TABLE    customerinfo( CustomerIDINT    NOT    NULL,PRIMARYKEY(CustomerID))TYPE=INNODB;

CREATE    TABLE    salesinfo( SalesIDNT    NOT    NULL,CustomerIDINT    NOT    NULL, PRIMARYKEY(CustomerID,SalesID), FOREIGNKEY(CustomerID)    REFERENCES    customerinfo(CustomerID)    ON    DELETE    CASCADE)TYPE=INNODB;

 

注意例子中的参数“ON DELETE CASCADE”。该参数保证当customerinfo表中的一条客户记录被删除的时候,salesinfo表中所有与该客户相关的记录也会被自动删除。如果要在MySQL中使用外键,一定要记住在创建表的时候将表的类型定义为事务安全表InnoDB类型。该类型不是MySQL表的默认类型。定义的方法是在CREATETABLE语句中加上TYPE=INNODB。如例中所示。

7、使用索引

索引是提高数据库性能的常用方法,它可以令数据库服务器以比没有索引快得多的速度检索特定的行,尤其是在查询语句当中包含有MAX(),MIN()和ORDERBY这些命令的时候,性能提高更为明显。

那该对哪些字段建立索引呢?

一般说来,索引应建立在那些将用于JOIN,WHERE判断和ORDERBY排序的字段上。尽量不要对数据库中某个含有大量重复的值的字段建立索引。对于一个ENUM类型的字段来说,出现大量重复值是很有可能的情况

例如customerinfo中的“province”..字段,在这样的字段上建立索引将不会有什么帮助;相反,还有可能降低数据库的性能。我们在创建表的时候可以同时创建合适的索引,也可以使用ALTERTABLE或CREATEINDEX在以后创建索引。此外,MySQL从版本3.23.23开始支持全文索引和搜索。全文索引在MySQL中是一个FULLTEXT类型索引,但仅能用于MyISAM类型的表。对于一个大的数据库,将数据装载到一个没有FULLTEXT索引的表中,然后再使用ALTERTABLE或CREATEINDEX创建索引,将是非常快的。但如果将数据装载到一个已经有FULLTEXT索引的表中,执行过程将会非常慢。

8、优化的查询语句

绝大多数情况下,使用索引可以提高查询的速度,但如果SQL语句使用不恰当的话,索引将无法发挥它应有的作用。

下面是应该注意的几个方面。

  • 首先,最好是在相同类型的字段间进行比较的操作。

    在MySQL3.23版之前,这甚至是一个必须的条件。例如不能将一个建有索引的INT字段和BIGINT字段进行比较;但是作为特殊的情况,在CHAR类型的字段和VARCHAR类型字段的字段大小相同的时候,可以将它们进行比较。

  • 其次,在建有索引的字段上尽量不要使用函数进行操作。

例如,在一个DATE类型的字段上使用YEAE()函数时,将会使索引不能发挥应有的作用。所以,下面的两个查询虽然返回的结果一样,但后者要比前者快得多。

  • 第三,在搜索字符型字段时,我们有时会使用LIKE关键字和通配符,这种做法虽然简单,但却也是以牺牲系统性能为代价的。

例如下面的查询将会比较表中的每一条记录。

SELECT * FROM  books WHERE name like "MySQL%";

但是如果换用下面的查询,返回的结果一样,但速度就要快上很多:

SELECT  * FROM books WHERE name>="MySQL"  and name "MySQM";

最后,应该注意避免在查询中让MySQL进行自动类型转换,因为转换过程也会使索引变得不起作用。

 

posted @ 2020-11-03 14:31  杨兮臣  阅读(127)  评论(0编辑  收藏  举报