本文介绍的是针对MySQL 数据库的SQL优化,接下来让我们一起先了解下MySQL DBMS(MySQL Database Management System)。
1、结构图
2、MySQL 数据库引擎简介
2.1 ISAM(Indexed Sequential Access Method)
ISAM 是一个定义明确且历经时间考验的数据表格管理方法, 它在设计之时就考虑到数据库被查询的次数要远大于更新的次数。
特点:
ISAM 执行读取操作的速度很快, 而且不占用大量的内存和存储资源
缺陷:
不支持事务处理
不能够容错,硬盘崩溃了, 那么数据文件就无法恢复了2.2 MyISAM
MyISAM 是 MySQL 的 ISAM 扩展格式( MySQL5.5 之前版本的缺省数据库引擎) 数据库引擎。 除了提供 ISAM 里所没有的索引和字段管理的大量功能, MyISAM 还使用一种表格锁定的机制, 来优化多个并发的读写操作, 其代价是你需要经常运行 OPTIMIZE TABLE 命令,来恢复被更新机制所浪费的空间。
特点:
MYISAM 强调了快速读取操作, 这可能就是为什么 MySQL 受到了 WEB 开发如此青睐的主要原因: 在 WEB 开发中你所进行的大量数据操作都是读取操作。
缺陷:
不支持事务处理
不能在表损坏后恢复数据
注意:
如果使用该数据库引擎, 会生成三个文件:
.frm:表结构信息
.MYD:数据文件
.MYI:表的索引信息
2.3 InnoDB
InnoDB 数据库引擎都是造就 MySQL 灵活性的技术的直接产品,MySQL( 5.5 以上版本) 常用版本默认引擎。
特点:
对事务处理和外键的支持
数据多版本读取( InnoDB+MyISAM+ISAM)
缺陷:
读取操作比MyISAM慢
注意:
在 MySQL5.7 版本中, InnoDB 存储引擎管理的数据文件为两个: 分别是 frm表结构信息和idb索引文件。2.4 Memory 存储引擎
Memory 存储引擎是一个将数据存储在内存中的存储引擎。仅仅存放了一个表结构相关信息的.frm 文件在磁盘上面。 所以一旦 MySQL Crash 或者主机 Crash 之后, Memory 的表就只剩下一个结构了。 Memory 表支持索引, 并且同时支持 Hash 和 B-Tree 两种格式的索引。 由于是存放在内存中, 所以 Memory 都是按照定长的空间来存储数据的, 而且不支持 BLOB 和 TEXT类型的字段。 Memory 存储引擎实现页级锁定。2.5 BLACKHOLE 存储引擎
BLACKHOLE 黑洞存储引擎。就像我们 linux系统下面的”/dev/null”设备一样, 不管我们写入任何信息, 都是有去无回。
总结:
innodb 与 myisam 区别
- InnoDB 支持事务, MyISAM 不支持, 对于 InnoDB 每一条 SQL 语言都默认封装成事务,自动提交, 这样会影响速度, 所以最好把多条 SQL 语言放在 begin transaction 和 commit 之间, 组成一个事务;
- InnoDB 支持外键, 而 MyISAM 不支持。 对一个包含外键的 InnoDB 表转为 MYISAM 会失败;
- InnoDB 是聚集索引, 数据文件是和索引绑在一起的, 必须要有主键, 通过主键索引效率很高。 但是辅助索引需要两次查询, 先查询到主键, 然后再通过主键查询到数据。 因此,主键不应该过大, 因为主键太大, 其他索引也都会很大。 而 MyISAM 是非聚集索引, 数据文件是分离的, 索引保存的是数据文件的指针。 主键索引和辅助索引是独立的。
- InnoDB 不保存表的具体行数, 执行 select count(*) from table 时需要全表扫描。 而MyISAM 用一个变量保存了整个表的行数, 执行上述语句时只需要读出该变量即可, 速度很快;
- Innodb 不支持全文索引, 而 MyISAM 支持全文索引, 查询效率上 MyISAM 要高;
如何选择innodb 与 myisam
- 是否要支持事务, 如果要请选择 innodb, 如果不需要可以考虑 MyISAM
- 如果表中绝大多数都只是读查询, 可以考虑 MyISAM, 如果既有读写也挺频繁, 请使用 InnoDB。
- 系统崩溃后, MyISAM 恢复起来更困难, 能否接受;
- MySQL5.5 版本开始 Innodb 已经成为 Mysql 的默认引擎(之前是 MyISAM), 说明其优势是有目共睹的。
3、存储引擎管理
- 3.1 查看数据库支持的存储引擎
show engines - 3.2 查看数据库当前使用的存储引擎
show variables like ‘%storage_engine%’
也可以在 MySQL 配置文件中查看。 windows —— my.ini。 linux —— my.cnf - 3.3 查看数据库表所用的存储引擎
show create table table_name - 3.4 创建表指定存储引擎
create table table_name (column_name column_type) engine = engine_name - 3.5 修改表的存储引擎
alter table table_name engine=engine_name - 3.6 修改默认的存储引擎
在 MySQL 配置文件中修改下述内容:
default-storage-engine=INNODB
MySQL 配置文件:
windows 系 统 – MySQL 安 装目 录/my.ini (5.7 版 本 my.ini 文 件在 数 据目 录 中。
C:/programdata/MySQL Server 5.7/mysql/)
linux 系统 —— /etc/my.cnf
4、MySQL索引
索引在MySQL中也叫作 ” 键 “,是存储引擎用于快速找到记录的一种数据结构。
优点:
索引的目的在于提高查询效率
缺点:
索引需要占物理空间,意味着索引不是创建越多越好
需要耗费时间维护索引
4.1 哪些字段适合创建索引:
where + 判断条件字段
order by + 排序字段
select + 查询频率较高的字段
外键字段:可以加快连接的速度;4.2 哪些字段不适合创建索引:
查询中很少使用的字段
修改频率较高的字段
5、MySQL管理索引
5.1 普通索引
这是最基本的索引, 也是比较常用的索引。
- 5.1.1 创建索引
CREATE INDEX index_name ON table_name (column(length))
ALTER TABLE table_name ADD INDEX index_name (column(length))
CREATE TABLE table_name (id int not null auto_increment,title varchar(30) ,PRIMARY KEY(id) , INDEX index_name (title(5)))
- 5.1.2 查看索引
SHOW INDEX FROM table_name
SHOW KEYS FROM table_name #keys关键字只适用于MySQL数据库
- 5.1.3 删除索引
DROP INDEX index_name ON table_name
ALTER TABLE table_name DROP INDEX index_name
ALTER TABLE table_name DROP PRIMARY KEY
5.2 唯一索引
唯一索引列的值必须唯一, 但允许有空值 。
- 5.2.1 创建索引
CREATE UNIQUE INDEX index_name ON table_name (column(length))
ALTER TABLE table_name ADD UNIQUE index_name (column(length))
CREATE TABLE table_name (id int not null auto_increment,title varchar(30) ,PRIMARY KEY(id) , UNIQUE index_name (title(length)))
5.3 全文索引( FULLTEXT)
MySQL 从 3.23.23 版开始支持全文索引和全文检索, FULLTEXT 索引仅可用于 MyISAM表。
- 5.3.1 创建索引
CREATE FULLTEXT INDEX index_name ON table_name(column(length))
ALTER TABLE table_name ADD FULLTEXT index_name( column)
CREATE TABLE table_name (id int not null auto_increment,title varchar(30) ,PRIMARY KEY(id) , FULLTEXT index_name (title))
6、MySQL索引优化
- 6.1 索引不包含有 NULL 值的列
只要列中包含有 NULL 值都将不会被包含在索引中, 组合索引中只要有一列含有 NULL值, 那么这一列对于此组合索引就是无效的。 所以我们在数据库设计时不要让字段的默认值为 NULL。 - 6.2 使用短索引
例如, 如果有一个CHAR(255)的列,如果在前10个或20个字符内, 多数值是唯一的, 那么就不要对整个列进行索引。 短索引不仅可以提高查询速度而且可以节省磁盘空间和 I/O 操作。 - 6.3 索引列排序
MySQL 查询只使用一个索引, 因此如果where子句中已经使用了索引的话, 那么 order by中的列是不会使用索引的。 - 6.4 like 语句操作
like “%aaa%”不会使用索引, 而 like “aaa%”可以使用索引。 - 6.5 不要在列上进行运算
例如: select * from users where YEAR(birthdate)<2019, 将在每个行上进行运算, 这将导致索引失效而进行全表扫 描 , 因此可以改成 : select * from users where birthdate<’2019-01-01′
7、MySQL 中的 SQL 的常见优化策略
- 7.1 避免全表扫描
对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。 - 7.2 避免判断 null 值
应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引
而进行全表扫描,如:
select id from t where num is null
可以在 num 上设置默认值 0,确保表中 num 列没有 null 值,然后这样查询:
select id from t where num=0 - 7.3 避免不等值判断
应尽量避免在 where 子句中使用!=或<>操作符,否则引擎将放弃使用索引而进行全表扫描。 - 7.4 避免使用 or 逻辑
应尽量避免在 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 - 7.5 慎用 in 和 not in
in 和 not in 也要慎用,否则会导致全表扫描,如:
select id from t1 where num in(select id from t2 where id > 10)
此时外层查询会全表扫描,不使用索引。可以修改为:
select id from t1,(select id from t1 where id > 10)t2 where t1.id = t2.id
此时索引被使用,可以明显提升查询效率。 - 7.6 注意模糊查询
下面的查询也将导致全表扫描:
select id from t where name like ‘%abc%’
模糊查询如果是必要条件时,可以使用 select id from t where name like ‘abc%’来实现模糊查询,此时索引将被使用。如果头匹配是必要逻辑,建议使用全文搜索引擎 ( Elastic search等)。 - 7.7 避免查询条件中字段计算
应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。如:
select id from t where num/2=100
应改为:
select id from t where num=100*2 - 7.8 避免查询条件中对字段进行函数操作
应尽量避免在 where 子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。如:
select id from t where substring(name,1,3)=’abc’ #name 以abc开头的 id
应改为:
select id from t where name like ‘abc%’ - 7.9 WHERE 子句” = “左边注意点
不要在 where 子句中的 ” = ” 左边进行函数、算术运算或其他表达式运算,否则系统
将可能无法正确使用索引。 - 7.10 组合索引使用
在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使用,并且应尽可能的让字段顺序与索引顺序相一致。 - 7.11 exists
很多时候用 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) - 7.12 索引也可能失效
并不是所有索引对查询都有效,SQL 是根据表中数据来进行查询优化的,当索引列有大量数据重复时,SQL 查询可能不会去利用索引,如一表中有字段 sex、male、female几乎各一半,那么即使在 sex 上建了索引也对查询效率起不了作用。 - 7.13 表格字段类型选择
尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型, 这会降低查询和连接的性能,并会增加存储开销。 这是因为引擎在处理查询和连接时会逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。尽可能的使用 varchar 代替 char,因为首先可变长度字段存储空间小,可以节省存储空间,其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。 - 7.14 查询语法中的字段
任何地方都不要使用 select * from t ,用具体的字段列表代替 ” * “,不要返回用不到的任何字段。