一、存储引擎
1、MYSQL体系结构
连接层、服务层、引擎层、存储层;
2、存储引擎简介
存储引擎就是存储数据、建立索引、更新/查询数据等技术的实现方式。存储引擎是基于表的,而不是库的,所以存储引擎也可被称为表类型。
1)在创建表时指定存储引擎
sql
CREATE TABLE 表名(
字段1名 字段1类型 [COMMENT 字段1注释],
......
字段n名 字段n类型 [COMMENT 字段n注释]
)ENGINE=INNODB [COMMENT 表注释]
2)查看数据库引擎
sql
--查看数据库的引擎
mysql> show engines;
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
| Engine | Support | Comment | Transactions | XA | Savepoints |
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
| ndbcluster | NO | Clustered, fault-tolerant tables | NULL | NULL | NULL |
| CSV | YES | CSV storage engine | NO | NO | NO |
| ARCHIVE | YES | Archive storage engine | NO | NO | NO |
| BLACKHOLE | YES | /dev/null storage engine (anything you write to it disappears) | NO | NO | NO |
| ndbinfo | NO | MySQL Cluster system information storage engine | NULL | NULL | NULL |
| MRG_MYISAM | YES | Collection of identical MyISAM tables | NO | NO | NO |
| FEDERATED | NO | Federated MySQL storage engine | NULL | NULL | NULL |
| MyISAM | YES | MyISAM storage engine | NO | NO | NO |
| PERFORMANCE_SCHEMA | YES | Performance Schema | NO | NO | NO |
| InnoDB | DEFAULT | Supports transactions, row-level locking, and foreign keys | YES | YES | YES |
| MEMORY | YES | Hash based, stored in memory, useful for temporary tables | NO | NO | NO |
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
11 rows in set (0.04 sec)
sql
--查看数据库中某个表使用的什么存储引擎
show create table xxx;
3、存储引擎特点
(一)INNODB
1)介绍:
InnoDB是一种兼顾高可靠性和高性能的通用存储引擎,在Mysql 5.5之后,InnoDB是mysql默认的存储引擎
2)特点:
DML操作遵循ACID模型,支持事务;
行级锁,支持并发访问性能;
支持外键FOREIGN KEY约束,保证数据完整性和正确性;
3)文件:
xxx.ibd :xxx是文件名,InnoDB引擎的每张表都对应这样一个表空间文件,存储该表的表结构(frm、sdi)、数据和索引
参数:innodb_file_per_table
使用命令可查看idb文件中的文件结构
ibd2sdi xxx.ibd
sql
-- 查看变量,每个表一个文件,8.0版本之后默认是ON
mysql> show variables like 'innodb_file_per_table';
+-----------------------+-------+
| Variable_name | Value |
+-----------------------+-------+
| innodb_file_per_table | ON |
+-----------------------+-------+
1 row in set (0.01 sec)
4)逻辑存储结构
表空间:TableSpace
段:Segment
区:Extent 默认大小 1M 包含64个页
页:Page 默认大小 16K
行:Row
(二)MyISAM
1)介绍:
MyISAM是MYSQL早期默认存储引擎。
2)特点:
不支持事务,不支持外键;
支持表锁,不支持行锁;
访问速度快;
3) 文件:
xxx.sdi:存储表结构信息
xxx.MYD:存储数据
xxx.MYI:存储索引
(三)Memory
1)介绍:
Memory存储引擎的表数据是存储在内存中的,受到断电问题、或硬件问题的影响,只能将这些表作为临时表或者缓存使用。
2)特点:
内存存放
Hash索引(默认)
3)文件:
xxx.sdi:存放表结构信息
4、存储引擎选择
根据应用系统特点选择存储引擎,对于复杂应用,可以根据实际情况,选择多种存储引擎的组合。
InnoDB:mysql默认存储引擎,支持事务和外键。如果应用对事务的完整性有较高要求,在并发条件下要求数据一致性,数据操作除了插入和查询之外,还有很多更新和删除操作,那么innoDB存储引擎是比较合适的选择。
MyISAM:如果数据以插入和查询为主,对数据的更新和删除较少,并且对事务的完整性和并发性要求不高,选择这个存储引擎比较合适。
Memory:将所有数据保存到内存中,访问速度快,通常用于临时表及缓存。memory对表的大小有限制,太大的表无法缓存到内存中,而且无法保证数据的安全性。
二、索引
1、索引概述
介绍:
索引是帮助mysql高效获取数据 的一种数据结构(有序的)。在数据之外,数据库还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查找算法,这种数据结构就是索引。
优缺点:
优势 | 劣势 |
---|---|
提高数据检索效率,降低数据库的IO成本 | 索引也是要暂用存储空间的 |
通过索引列对数据进行排序,降低数据排序的成本,降低CPU的消耗 | 索引大大提高了查询效率,同时降低了更新表的速度,如对表进行INSERT、UPDATE、DELETE时,效率降低。 |
2、索引结构
索引结构 | 描述 |
---|---|
B+Tree索引 | 最常见的索引类型,大部分存储引擎都支持B+树索引 |
Hash索引 | 底层数据结构使用hash表实现的,只有精确匹配索引列的查询才有效,不支持范围查询 |
R-Tree(空间索引) | 空间索引是MyISAM引擎的一种特殊索引类型,主要用于地理空间数据类型,用的较少 |
Full-text(全文索引) | 是一种通过建立倒排索引,快速匹配文档的方式。类似于Lucene、Solr、ES |
索引支持情况
索引 | InnoDB | MyISAM | Memory |
---|---|---|---|
B+Tree索引你 | 支持 | 支持 | 支持 |
Hash索引 | 不支持 | 不支持 | 支持 |
R-Tree索引 | 不支持 | 支持 | 不支持 |
Full-text全文索引 | 5.6版本之后支持 | 支持 | 不支持 |
我们平时说的索引结构,如果不做特殊说明都是指B+Tree索引。
1)二叉树
左小右大的二叉树
缺点:
1)顺序插入时,会形成一个链表 ,查询性能大大降低。
2)大数据量情况下,层级较深,检索速度慢。
2)红黑树
使用红黑树,解决二叉树的平衡问题(第一个问题),但是问题二依然存在
大数据量情况下,层级较深,检索速度慢。
3)B树(多路平衡查找树)
以一颗最大度数(max-degree)为5(5阶)的B-Tree为例(每个节点最多存储4个key,5个指针)
树的度数:指的是一个节点的最大子节点个数。
4)B+树
相对于B-Tree特点:
1)所有数据都出现在叶子节点,非叶子节点只起到索引作用
2)叶子节点是个单向链表
以一个最大度数为4(4阶)的B+树为例
Mysql索引数据结构对经典的B+Tree进行了优化,在原B+Tree的基础上,增加了一个指向相邻叶子节点的链表指针,就形成了一个带有顺序指针的B+Tree,提高了区间访问的性能。
5)Hash
哈希索引就是采用一定的hash算法,将键值换算成新的hash值,映射到对应的槽位上,然后存储在哈希表中。
如果两个或者多个键值,映射到一个相同的槽位上,他们就产生了哈希冲突(也成为哈希碰撞),可以通过链表来解决。
hash索引特点
1)只能用于对等比较(=,in),不支持范围查询
2)无法利用索引完成排序操作
3)查询效率高,通常只需要一次检索就可以了,效率通常要高于B+Tree索引
存储引擎支持
在mysql中,支持hash索引的是memory引擎,而InnoDB中具有自适应hash功能,hash索引是存储引擎根据B+Tree索引在指定条件下自动构建的。
6)为什么InnoDB采用B+Tree索引结构
1)相对于二叉树层级更少,搜索效率更高。
2)对于B-树,无论是叶子结点还是非叶子节点,都会保存数据,这样导致一页存储的键值减少,指针跟着减少,要同样保存大量数据,只能增加数的高度,导致性能下降。
3)对于Hash索引,B+Tree支持范围查找及排序操作。
3、索引分类
分类 | 含义 | 特点 | 关键字 |
---|---|---|---|
主键索引 | 针对于表中主键创建的索引 | 默认自动创建,只能有一个 | primary |
唯一索引 | 避免表中某个列的值数据重复 | 可以有多个 | unique |
普通索引 | 快速定位特定数据 | 可以有多个 | |
全文索引 | 全文索引查找的是文本中的关键词,而不是比较索引中的值 | 可以有多个 | fulltext |
在InnoDB存储引擎中,根据索引的存储形式又可以分为以下两种
分类 | 含义 | 特点 |
---|---|---|
聚集索引(Clustered Index) | 将数据存储于索引结构放到一块,索引结构的叶子节点保存了行数据 | 必须有,而且只有一个 |
二级索引(Secondary Index) | 将数据与索引分开存储,索引结构的叶子节点关联的是对应的主键 | 可以存在多个 |
聚集索引选取规则
1)如果存在主键,主键索引就是聚集索引
2)如果没有主键,将使用第一个唯一索引(UNIQUE)作为聚集索引
3)如果表没有主键或没有合适的唯一索引,则InnoDB会自动生成一个rowid作为隐藏的聚集索引
思考:
InnoDB主键索引的B+Tree高度为多高?
假设:
一行数据大小为1k,一页中可以存储16行这样的数据,InnoDB的指针暂用6个字节的空间,主键即使是BigInt,暂用字节数为8
高度为2:
n*8+(n+1)*6 =16*1024 算出n约为1170
1171*16=18736
高度为3:
1171*1171*16=2193,9856 ≈ 2200万
4、索引语法
sql
--创建索引
CREATE [UNIQUE|FULLTEXT] INDEX index_name ON table_name(index_col_name......);
--查看索引
SHOW INDEX FROM table_name;
--删除索引
DROP INDEX index_name ON table_name
5、sql性能分析
(一)SQL执行频率
Msql客户端连接成功后,通过命令show 【session|global】status 可以提供服务器状态信息,通过如下指令可以查看当前数据库的INSERT、UPDATE、DELETE、SELECT访问频次。如果没有select查询或者很少的查询操作,就没必要做优化。
show global status like 'Com_______' 7个下划线
sql
--查看各种语句使用频率
mysql> show global status like 'Com_______'
-> ;
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Com_binlog | 0 |
| Com_commit | 4 |
| Com_delete | 0 |
| Com_import | 0 |
| Com_insert | 8 |
| Com_repair | 0 |
| Com_revoke | 2 |
| Com_select | 263 |
| Com_signal | 0 |
| Com_update | 10 |
| Com_xa_end | 0 |
+---------------+-------+
11 rows in set (0.01 sec)
(二)慢查询日志
1)查看慢查询日志是否开启
show ariables like 'slow_query_log'
sql
--查看慢查询日志开关是否开启,默认是关闭
mysql> show variables like 'slow_query_log';
+----------------+-------+
| Variable_name | Value |
+----------------+-------+
| slow_query_log | OFF |
+----------------+-------+
1 row in set (0.02 sec)
慢查询日志记录了所有执行时间超过指定参数(long_query_time,单位:秒,默认10秒)的所有sql语句日志。
2)开启慢查询
mysql的慢查询日志默认没有开启,需要在MYSQL配置文件(/etc/my.cnf)中配置如下信息:
sql
--开启mysql慢查询日志开关
slow_query_log=1
--设置慢日志的时间为2秒,sql语句执行时间超过两秒,就会视为慢查询,记录慢查询日志
long_query_time=2
3)查看慢查询
配置完毕后,通过以下指令,重启mysql服务器进行测试,查看慢日志记录文件中记录的信息/var/lib/mysql/localhost-slow.log
(三)profile详情
show profiles命令在做sql优化时,能够帮我们了解每一条sql时间都耗费到哪里去了。通过have_profiling参数,能够看到当前mysql是否支持profile操作:
1)是否支持profile操作
select @@have_profiling;
默认profiling是关闭的,
2) 查看profiling是否开启
select @@profiling;
3)开启profile
可以在session|global级别开启profiling:
set profiling=1;
sql
--当前mysql是否支持profile操作
mysql> select @@have_profiling;
+------------------+
| @@have_profiling |
+------------------+
| YES |
+------------------+
1 row in set, 1 warning (0.00 sec)
--查看profiling开关是否开启,默认是关闭状态。 1表示开启,0表示关闭。
mysql> select @@profiling;
+-------------+
| @@profiling |
+-------------+
| 0 |
+-------------+
1 row in set, 1 warning (0.00 sec)
mysql> show profiles;
Empty set, 1 warning (0.00 sec)
--打开profile详情
mysql> set profiling=1;
Query OK, 0 rows affected, 1 warning (0.00 sec)
mysql> select @@profiling;
+-------------+
| @@profiling |
+-------------+
| 1 |
+-------------+
1 row in set, 1 warning (0.01 sec)
4)查看每一条sql语句耗时情况
开启profiling开关之后,通过如下指令,查看指令的执行耗时。
show profiles;
5)查看每个极端耗时情况
--查看指定query_id的查询语句每个阶段的耗时情况
show profile query for query_id;
6)查看每个阶段CPU使用情况
--查看指定query_id的sql语句cpu使用情况;
show profile cpu query for query_id;
7)下面是使用示例
sql
--查看每一条sql耗时基本情况
show profiles;
--查看指定query_id的查询语句每个阶段的耗时情况
show profile query for query_id;
--查看指定query_id的SQL语句CPU的使用情况
show profile cpu for query query_id;
--执行一些sql语句,供后面查询用
mysql> select * from account;
+----+--------+-------+
| id | name | money |
+----+--------+-------+
| 1 | 张三 | 2000 |
| 2 | 李四 | 2000 |
+----+--------+-------+
2 rows in set (0.00 sec)
mysql> select * from t01;
Empty set (0.00 sec)
mysql> select * from user;
+----+--------+----------+
| id | name | password |
+----+--------+----------+
| 1 | 张三 | 123 |
| 2 | 李四 | 123 |
| 3 | 王五 | 123 |
| 4 | 王五 | 123 |
| 5 | NULL | 123456 |
| 6 | doumi | 123456 |
+----+--------+----------+
6 rows in set (0.00 sec)
mysql> select * from user where id=3
-> ;
+----+--------+----------+
| id | name | password |
+----+--------+----------+
| 3 | 王五 | 123 |
+----+--------+----------+
1 row in set (0.00 sec)
mysql> select * from user where name='doumi';
+----+-------+----------+
| id | name | password |
+----+-------+----------+
| 6 | doumi | 123456 |
+----+-------+----------+
1 row in set (0.00 sec)
--查看每一条SQL的耗时情况
查看每条sql语句执行耗时情况
show profiles;
sql
mysql> show profiles;
+----------+------------+---------------------------------------+
| Query_ID | Duration | Query |
+----------+------------+---------------------------------------+
| 1 | 0.01071200 | show tables |
| 2 | 0.00215000 | select * from account |
| 3 | 0.00290200 | select * from t01 |
| 4 | 0.00397000 | select * from user |
| 5 | 0.00320600 | select * from user where id=3 |
| 6 | 0.00151600 | select * from user where name='doumi' |
+----------+------------+---------------------------------------+
6 rows in set, 1 warning (0.00 sec)
show profile for query 6; -- 6是上面show profiles命令结果中的query_id
sql
--查看query_id 为6的那条sql语句每个阶段的耗时情况
mysql> show profile for query 6;
+--------------------------------+----------+
| Status | Duration |
+--------------------------------+----------+
| starting | 0.000087 |
| Executing hook on transaction | 0.000009 |
| starting | 0.000008 |
| checking permissions | 0.000007 |
| Opening tables | 0.000045 |
| init | 0.000008 |
| System lock | 0.000009 |
| optimizing | 0.000943 |
| statistics | 0.000060 |
| preparing | 0.000111 |
| executing | 0.000102 |
| end | 0.000007 |
| query end | 0.000005 |
| waiting for handler commit | 0.000012 |
| closing tables | 0.000011 |
| freeing items | 0.000034 |
| cleaning up | 0.000058 |
+--------------------------------+----------+
17 rows in set, 1 warning (0.01 sec)
show profile cpu for query 6; -- 6是上面show profiles命令结果中的query_id
sql
--查看query_id为6的那条sql语句CPU消耗情况
mysql> show profile cpu for query 6;
+--------------------------------+----------+----------+------------+
| Status | Duration | CPU_user | CPU_system |
+--------------------------------+----------+----------+------------+
| starting | 0.000087 | 0.000073 | 0.000013 |
| Executing hook on transaction | 0.000009 | 0.000003 | 0.000007 |
| starting | 0.000008 | 0.000006 | 0.000002 |
| checking permissions | 0.000007 | 0.000004 | 0.000003 |
| Opening tables | 0.000045 | 0.000043 | 0.000002 |
| init | 0.000008 | 0.000003 | 0.000005 |
| System lock | 0.000009 | 0.000007 | 0.000002 |
| optimizing | 0.000943 | 0.000023 | 0.000175 |
| statistics | 0.000060 | 0.000043 | 0.000016 |
| preparing | 0.000111 | 0.000043 | 0.000068 |
| executing | 0.000102 | 0.000096 | 0.000006 |
| end | 0.000007 | 0.000003 | 0.000004 |
| query end | 0.000005 | 0.000003 | 0.000003 |
| waiting for handler commit | 0.000012 | 0.000010 | 0.000002 |
| closing tables | 0.000011 | 0.000009 | 0.000002 |
| freeing items | 0.000034 | 0.000010 | 0.000024 |
| cleaning up | 0.000058 | 0.000022 | 0.000035 |
+--------------------------------+----------+----------+------------+
17 rows in set, 1 warning (0.01 sec)
(四)explain执行计划
EXPLAIN或者DESC命令获取mysql如何执行select语句的信息,包括在select执行过程中,表如何连接及连接的顺序。
语法:
直接在select 语句前面加上explain/desc
EXPLAIN SELECT 字段名 FROM 表名 WHERE 条件;
sql
mysql> explain select * from user where id=2;
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| 1 | SIMPLE | user | NULL | const | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | NULL |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.01 sec)
mysql> desc select * from user where id=2;
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| 1 | SIMPLE | user | NULL | const | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | NULL |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
执行计划中每个字段含义
**id:**select查询的序列号,表示查询中执行select子句或者操作表的顺序(id相同,执行顺序从上到下,id不同,值越大越先执行)
**select_type:**表示查询类型,常见的有SIMPLE(简单表,即不使用表连接或子查询)、PRIMARY(主查询,即外层的查询)、UNION(UNION中的第二个或者后面的语句)、SUBQUERY(select或者where之后包含了子查询)
**type:**表示连接类型,性能由好到差的连接类型是:NULL,system、const、eq_ref、ref、range、index、all
**possible_keys:**显示可能应用在这张表的上索引,一个或多个。
**key:**实际用到的索引,如果没用到展示位null
key_lens:表示索引中使用的字节数,该值为索引字段最大可能长度,并非实际使用长度,在不损失精确性的前提下,长度越短越好。
**rows:**mysql认为必须要执行查询的行数,在InnoDB引擎的表中,是一个估计值,可能并不总是准确的。
**filtered:**表示返回结果的行数占需读取行数的百分比,filtered的值越大越好。
sql
mysql> select s.*,c.* from student_course sc,student s,course c where sc.student_id =s.id and sc.course_id = c.id;
+----+-----------+------------+----+--------+
| id | name | no | id | name |
+----+-----------+------------+----+--------+
| 1 | 红小豆 | 2000100101 | 1 | JAVA |
| 1 | 红小豆 | 2000100101 | 2 | PHP |
| 1 | 红小豆 | 2000100101 | 3 | MYSQL |
| 2 | 张天爱 | 2000100102 | 3 | MYSQL |
| 2 | 张天爱 | 2000100102 | 3 | MYSQL |
| 3 | 鹿晗 | 2000100103 | 4 | HADOOP |
+----+-----------+------------+----+--------+
6 rows in set (0.00 sec)
mysql>
mysql>
mysql> desc select s.*,c.* from student_course sc,student s,course c where sc.student_id =s.id and sc.course_id = c.id;;
+----+-------------+-------+------------+--------+---------------+---------+---------+-----------------------+------+----------+--------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+--------+---------------+---------+---------+-----------------------+------+----------+--------------------------------------------+
| 1 | SIMPLE | s | NULL | ALL | PRIMARY | NULL | NULL | NULL | 4 | 100.00 | NULL |
| 1 | SIMPLE | sc | NULL | ALL | NULL | NULL | NULL | NULL | 6 | 16.67 | Using where; Using join buffer (hash join) |
| 1 | SIMPLE | c | NULL | eq_ref | PRIMARY | PRIMARY | 4 | lyltest1.sc.course_id | 1 | 100.00 | NULL |
+----+-------------+-------+------------+--------+---------------+---------+---------+-----------------------+------+----------+--------------------------------------------+
3 rows in set, 1 warning (0.00 sec)
mysql> explain
-> select s.* from student s where
-> s.id in(select sc.student_id from student_course sc where sc.course_id=
-> (select c.id from course c where c.name='MYSQL'));
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-----------------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-----------------------------------------------------------+
| 1 | PRIMARY | s | NULL | ALL | PRIMARY | NULL | NULL | NULL | 4 | 100.00 | NULL |
| 1 | PRIMARY | sc | NULL | ALL | NULL | NULL | NULL | NULL | 6 | 16.67 | Using where; FirstMatch(s); Using join buffer (hash join) |
| 3 | SUBQUERY | c | NULL | ALL | NULL | NULL | NULL | NULL | 4 | 25.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-----------------------------------------------------------+
3 rows in set, 1 warning (0.01 sec)
需要重点关注的字段
6、索引使用
索引失效情况二:
or连接的条件:
用or分割开的条件,如果or前的条件中的列有索引,or后面的条件中的列没索引,那么涉及的索引都不会被引用到。