文章目录
- 前言
- MySQL架构
- 存储引擎
-
- 1.语法
- 2.存储引擎特点
-
- innoDB
-
- MyISAM
- Memory
- 存储引擎对比
- 3.存储引擎选择
- 索引
-
- 1.索引结构
-
- B+Tree
- Hash
- 为什么innoDB存储引擎选择B+Tree索引结构?
- 2.索引的分类
- 3.索引语法
-
- 索引优缺点
- SQL性能分析
-
- 慢查询日志
- profile详情
- explain执行计划
-
- 索引的使用
- 索引的设计原则
- SQL优化
-
- 插入优化
- 主键优化
-
- order by优化
-
- limit优化
- count优化
-
- todo未完待续
- 总结
- Q&A
前言
作为一个程序猿,平时经常使用数据库,虽然说可以熟练使用数据库,但是对数据库的一些内部原理调优了解不太全面。本文以MySQL为例子,学习数据库的简单原理和一些调优。
在此特别感谢黑马MySQL学习视频。
MySQL架构
存储引擎
1.语法
#查看存储引擎
show engines;
#创建sql指定存储引擎
create table xxx (...) engine=innodb;
2.存储引擎特点
innoDB
innoDB是一种兼顾高可靠性和高性能的通用存储引擎,在MySQL5.5之后,innoDB是默认存储引擎。
特点:
- DML操作遵循ACID模型,支持事务;
- 行级锁,提高并发访问性能;
- 支持外键FOREIGN KEY约束,保证数据的完整性和正确性。
文件:
xxx.ibd:xxx代表的是表名,innoDB引擎的每张表都会对应这样一个表空间文件,存储该表的表结构(frm、sdi)、数据和索引。
参数:innodb_file_per_table;
逻辑存储结构:
MyISAM
MyISAM是mysql早期的默认存储引擎。
特点:
- 不支持事务,不支持外键;
- 支持表锁,不支持行锁;
- 访问速度快;
文件:
xxx.sdi:存储表结构信息;
xxx.MYD:存储数据;
xxx.MYI:存储索引;
Memory
Memory引擎的表数据是存储在内存中的,由于受到硬件问题或者断电问题的影响,只能将这些表作为临时表或缓存使用。
特点:
- 内存存放;
- hash索引;
- 速度快;
文件:
xxx.sdi:存储表结构信息;
存储引擎对比
3.存储引擎选择
在选择存储引擎的时候,应该根据应用系统的特点选择合适的存储引擎。对于复杂的应用系统,还可以根据实际情况选择多种存储引擎进行组合。
- innoDB:是MySQL的默认存储引擎,支持事务、外键和行级锁。如果应用对事物的完整性有较高的要求,在并发条件下要求数据一致性,数据操作除了插入和查询之外,还包含很多更新、删除操作,那么innoDB存储引擎是比较合适的选择;
- MyISAM:如果应用是以读操作为主,只有很少的更新和删除,并且对事物的完整性、并发性要求不是很高,可以选择它;
- MEMORY:将所有的数据保存在内存中,访问速度快,通常用于临时表及缓存。MEMORY的缺点就是对于表的大小有限制,太大的表无法缓存在内存中。
索引
1.索引结构
B+Tree
MySQL索引数据结构对经典的B+Tree进行了优化。在原B+Tree的基础上,增加一个指向相邻叶子节点的链表指针,就形成了带有顺序指针的B+Tree,提高区间访问的性能。
Hash
哈希索引就是采用一定的hash算法,将键值换算成新的hash值,映射到对应的槽位上,然后存储在hash表中。
如果两个(或多个)键值,映射到一个相同的槽位上,他们就产生了hash冲突(也称hash碰撞),可以通过链表来解决。
特点:
- hash索引只能用于对等比较(=,in)不支持范围查询(between,>,<,>=,<=);
- 无法利用索引完成排序操作;
- 查询效率高,通常只需要一次检索就可以了,效率通常高于B+Tree索引;
为什么innoDB存储引擎选择B+Tree索引结构?
- 相对于二叉树,层级少,搜索效率高;
- 对于B-Tree,无论是叶子结点还是非叶子节点,都会保存数据,这样导致一页中存储的键值减少,指针跟着减少,要同样保存大量数据,只能增加树的高度,导致性能降低;
- 对于hash索引,B+Tree支持范围匹配及排序操作;
2.索引的分类
分类 |
含义 |
特点 |
关键字 |
主键索引 |
针对于表中主键创建的索引 |
默认自动创建,只能有一个 |
PRIMARY |
唯一索引 |
避免同一个表中某数据列中的值重复 |
可以有多个 |
UNIQUE |
常规索引 |
快速定位特定数据 |
可以有多个 |
|
全文索引 |
全文索引查找的是文本中的关键词,而不是比较索引中的值 |
可以有多个 |
FULLTEXT |
在innoDB存储引擎中,根据索引的存储形式,又可以分为以下两种:
分类 |
含义 |
特点 |
聚集索引(clustered index) |
将数据存储与索引放到一块,索引结构的叶子节点保存了行数据 |
必须有,而且自有一个 |
二级索引(secondary index) |
将数据与索引分开存储,索引结构的叶子节点关联的是对应的主键 |
可以存多个 |
聚集索引选取规则:
- 如果存在主键,主键索引就是聚集索引;
- 如果不存在主键,将使用第一个唯一索引(unique)作为聚集索引;
- 如果表没有主键,或没有合适的唯一索引,则innoDB会自动生成一个row作为隐藏的聚集索引;
回表查询:
3.索引语法
创建索引:
create [unique|fulltext] index index_name on table_name
查看索引:
show index from table_name
删除索引:
drop index index_name on table_name
查看数据库增删改查情况
show global status like 'Com_________'
索引优缺点
优点 |
缺点 |
提高数据检索的效率,降低数据库的IO成本 |
索引列也要占用空间 |
通过索引列对数据进行排序,降低数据排序的成本,降低CPU的消耗 |
索引大大提高查询效率,同时也降低增删改的效率 |
SQL性能分析
慢查询日志
慢查询日志记录了所有执行时间超过指定参数(long_query_time,单位:秒,默认10秒)的所有SQL语句的日志。MySQL的慢查日志默认是没有开启的,需要在MySQL的配置文件(/etc/my.cnf)中配置如下信息:
slow_query_log=1
long_query_time=2
配置完毕后,通过以下指令重启MySQL服务器进行测试,查看慢日志文件中的记录的信息 /var/lib/mysql/localhost-slow.log。
查看慢查询是否打开。
show variables like 'slow_query_log';
profile详情
show profiles 能够在做SQL优化时帮助我们了解时间都消耗到哪里去了。通过have_profiling参数,能够看到当前MySQL是否支持profiles操作。
select @@have_profiling;
默认profiling是关闭的,可以通过set语句在session/global级别开启profiling:
set profiling = 1;
explain执行计划
explain或者desc命令获取MySQL如何执行select语句的信息,包括在select语句执行过程中表如何连接和连接顺序。语法:
#直接在select语句之前加上关键字explain或者desc
explain SELECT cloumns_list FROM table_name WHERE query_condition
explain执行计划各字段含义
- id
select查询的序列号,表示查询中执行select子句或者操作表的顺序(id相同,执行从上到下,id不同,值越大,越先执行)。
- select_type
select的类型,常见的取值有SIMPLE(简单表,即不使用表连接或者子查询)、PRIMARY(主查询,即外层的查询)、UNION(UNION中的第二个或者后面的查询语句)、SUBQUERY(select/where之后包含了子查询)等。
- type
表示连接类型,性能由好到差的连接类型为,null、system、const、eq_ref、ref、range、index、all。
- possible_key
显示可能应用在这个查询中的索引,一个或者多个。
- key
实际使用的索引,如果null,则没有使用索引。
- key_len
表示索引中使用的字段数,该值为索引字段最大可能长度,并非实际使用长度,在不损失精度的前提下,长度越短约好。
- rows
MySQL认为必须要执行查询的行数,在innoDB引擎的表中,是一个估计值,可能并不总是准确的。
- filtered
表示返回结果的行数占需读取行数的百分比,filtered的值越大约好。
索引的使用
- 最左前缀法则
如果索引了多列(联合索引),要遵循最左前缀法则,最左前缀法则指查询从索引的最左列开始,并且不跳过索引的列。如果跳跃某列,索引将部分失效(后面的字段索引失效)。
- 范围查询
联合索引中,出现范围查询(>,<),范围查询右侧的索引失效。
- 索引列运算
不要在索引列上进行运算操作,索引将失效。
- 字符串不加引号
字符串类型字段使用时,不加引号,索引将失效。
- 模糊查询
如果仅仅是尾部模糊匹配,索引将不会失效,如果是头部模糊匹配,索引失效。
- or连接的条件
用or分隔开的条件,如果or前的条件中的列有索引,而后面的列中没有索引,那么涉及的索引都不会被用到。
- 数据分不影响
如果MySQL评价使用索引比全表更慢,则不使用索引。
- 覆盖索引
尽量使用覆盖索引(查询使用了索引,并且需要返回的列,在该索引中已经全部能够找到),减少select *。
using index condition:查询使用了索引,但是需要回表查询数据;
using where;using index:查询使用了索引,但是需要的数据都在索引列中能够找到,所以不需要回表查询数据。
- 前缀索引
当字段类型为字符串(varchar、text等),有时候需要索引很长的字符串,这会让索引变得很大,查询时,浪费大量磁盘IO,影响查询效率。此时可以只将字符串的一部分前缀建立索引,这样可以大大节约索引空间,从而提高索引效率。
语法:
create index idx_xxx on table_name(column(n));
前缀长度
可以根据索引的选择性来决定,而选择性是指不重复的索引值(基数)和数据表记录总数的比值,索引的选择性越高则查询效率越高。
- 单列索引和联合索引
单列索引:即一个索引只包含单个列。
联合索引:即一个索引包含了多个列。
在业务场景中,如果存在多个查询条件,考虑对于查询字段建立索引时,建议建立联合索引,而非单列索引。
索引的设计原则
- 针对于大数据量且查询比较频繁的表建立索引;
- 针对于常作为查询条件(where)、排序(order by)、分组(group by)操作的字段建立索引;
- 尽量选择区分度高的列作为索引,尽量建立唯一索引,区分度越高,使用索引的效率越高;
- 如果是字符串类型的字段,字段的长度较长,可以针对于字段的特点,建立前缀索引;
- 尽量使用联合索引,减少单列索引,查询时联合索引很多时候可以覆盖索引,节约存储空间,避免回表,提高效率;
- 要控制索引的数量,索引并不是多多益善,索引越多,维护索引结构的代价也越大,会影响增删改的效率;
- 如果索引不能存储空值,请在创建表时使用not null约束它。当优化器知道每列是否包含null时,他可以更好地确定哪个索引最有效地用于查询。
SQL优化
插入优化
insert into table_name values(1, 'a'),(2, 'b'),(3, 'c');
start transaction;
insert into table_name values (1, 'a'),(2, 'b),(3,'c');
insert into table_name values (4, 'a'),(5, 'b),(6,'c');
commit;
主键优化
数据组织方式
在innoDB存储引擎中,表数据都是根据主键顺序组织存放的,这种存储方式的表成为索引组织表(iot)
主键设计原则
- 满足业务需求的情况下,尽量降低主键的长度;
- 插入数据时,尽量按照顺序插入,选择使用AOTO_INCREMENT自增主键;
- 尽量不要使用UUID做主键或者其他自然主键,如身份证号等;
- 业务操作时,避免对主键的修改;
order by优化
Using filesort:通过表的索引或全表扫描,读取满足条件的数据行,然后再排序缓冲区中完成排序操作,所有不是通过索引直接返回排序结果的排序都叫filesort排序;
Using index:通过有序索引顺序扫描直接返回有序数据,这种情况即是using index,不需要额外排序,操作效率高;
order by优化方法
- 根据排序字段建立适合的索引,多字段排序时,也要遵循最左前缀原则;
- 尽量使用覆盖索引
- 多字段排序,一个升序,一个降序,此时需要注意联合索引在创建时的规则(asc/desc);
- 如果不可避免的出现filesort,大数据量排序时,可以适当增大排序缓冲区大小sort_buffer_size(默认256k) ;
limit优化
count优化
MyISAM引擎把一个表的总行数存在了磁盘上,因此执行count()的时候会直接返回这个数,效率高;
InnoDB引擎就麻烦了,它执行count()的时候,需要把数据一行一行的从引擎里面读出来,然后累计计数;
count的几种用法
todo未完待续
总结
MySQL学习
Q&A