select distinct '字段'
from '表'
join '表' on '条件'
where '条件'
group by '字段'
having '条件'
order by '字段'
limit '条件'
复制代码
from '表'
on '条件'
'join类型' join '表'
where '条件'
group by '字段'
having '条件'
select distinct '字段'
order by '字段'
limit '条件'
复制代码
索引(
Index
)是帮助MySQL高效获取数据的数据结构。在数据以外,数据库系统还维护着知足特定查找算法的数据结构,这些数据结构以某种方式指向数据,这样就能够在这些数据结构上实现高效的查找算法.这种数据结构,就是 索引.
通常来讲索引自己也很大,不可能所有存储在内存中,所以每每以索引文件的形式存放在磁盘中.咱们日常所说的索引,若是没有特别说明都是指BTree
索引(平衡多路搜索树).其中汇集索引,次要索引,覆盖索引复合索引,前缀索引,惟一索引默认都是使用的BTree
索引,统称索引. 除了BTree
索引以后,还有哈希索引mysql
Index
)是帮助MySQL高效获取数据的数据结构。不一样的引擎使用的数据结构也不尽相同.BTree
, InnoDB采起的是B+TREE
B+TREE
.B+树索引是B+树在数据库中的一种实现,是最多见也是数据库中使用最为频繁的一种索引。B+树中的B表明平衡(balance),而不是二叉(binary),由于B+树是从最先的平衡二叉树演化而来的。在讲B+树以前必须先了解二叉查找树、平衡二叉树(AVLTree
)和平衡多路查找树(B-Tree
),B+树即由这些树逐步优化而来。二叉树具备如下性质:左子树的键值小于根的键值,右子树的键值大于根的键值。 以下图所示就是一棵二叉查找树算法
二叉查找树能够任意地构造,一样是2,3,5,6,7,8这六个数字,也能够按照下图的方式来构造:sql
平衡二叉树(AVL树)在符合二叉查找树的条件下,还知足任何节点的两个子树的高度最大差为1。下面的两张 图片,左边是AVL树,它的任何节点的两个子树的高度差<=1;右边的不是AVL树,其根节点的左子树高度为3,而 右子树高度为1;数据库
这四种失去平衡的姿态都有各自的定义:缓存
LL:LeftLeft,也称“左左”。插入或删除一个节点后,根节点的左孩子(Left Child)的左孩子(Left Child)还有非 空节点,致使根节点的左子树高度比右子树高度高2,AVL树失去平衡。
RR:RightRight,也称“右右”。插入或删除一个节点后,根节点的右孩子(Right Child)的右孩子(Right Child) 还有非空节点,致使根节点的右子树高度比左子树高度高2,AVL树失去平衡。
LR:LeftRight,也称“左右”。插入或删除一个节点后,根节点的左孩子(Left Child)的右孩子(Right Child)还有 非空节点,致使根节点的左子树高度比右子树高度高2,AVL树失去平衡。
RL:RightLeft,也称“右左”。插入或删除一个节点后,根节点的右孩子(Right Child)的左孩子(Left Child)还有 非空节点,致使根节点的右子树高度比左子树高度高2,AVL树失去平衡。服务器
AVL树失去平衡以后,能够经过旋转使其恢复平衡。下面分别介绍四种失去平衡的状况下对应的旋转方法。 LL的旋转。LL失去平衡的状况下,能够经过一次旋转让AVL树恢复平衡。步骤以下:数据结构
LL旋转示意图以下:并发
RR的旋转:RR失去平衡的状况下,旋转方法与LL旋转对称,步骤以下:函数
RR旋转示意图以下:高并发
LR的旋转:LR失去平衡的状况下,须要进行两次旋转,步骤以下:
LR的旋转示意图以下:
RL的旋转:RL失去平衡的状况下也须要进行两次旋转,旋转方法与LR旋转对称,步骤以下:
RL的旋转示意图以下:
B-Tree是为磁盘等外存储设备设计的一种平衡查找树。所以在讲B-Tree以前先了解下磁盘的相关知识。
系统从磁盘读取数据到内存时是以磁盘块(block)为基本单位的,位于同一个磁盘块中的数据会被一次性读取出 来,而不是须要什么取什么。
InnoDB存储引擎中有页(Page)的概念,页是其磁盘管理的最小单位。InnoDB存储引擎中默认每一个页的大小为 16KB,可经过参数innodb_page_size将页的大小设置为4K、8K、16K.
而系统一个磁盘块的存储空间每每没有这么大,所以InnoDB每次申请磁盘空间时都会是若干地址连续磁盘块来达到 页的大小16KB。InnoDB在把磁盘数据读入到磁盘时会以页为基本单位,在查询数据时若是一个页中的每条数据都 能有助于定位数据记录的位置,这将会减小磁盘I/O次数,提升查询效率。
一棵m阶的B-Tree有以下特性:
B-Tree中的每一个节点根据实际状况能够包含大量的关键字信息和分支,以下图所示为一个3阶的B-Tree:
每一个节点占用一个盘块的磁盘空间,一个节点上有两个升序排序的关键字和三个指向子树根节点的指针,指针存储 的是子节点所在磁盘块的地址。两个关键词划分红的三个范围域对应三个指针指向的子树的数据的范围域。以根节 点为例,关键字为17和35,P1指针指向的子树的数据范围为小于17,P2指针指向的子树的数据范围为17~35,P3 指针指向的子树的数据范围为大于35。
模拟查找关键字29的过程:
分析上面过程,发现须要3次磁盘I/O操做,和3次内存查找操做。因为内存中的关键字是一个有序表结构,能够利用 二分法查找提升效率。而3次磁盘I/O操做是影响整个B-Tree查找效率的决定因素。B-Tree相对于AVLTree缩减了节 点个数,使每次磁盘I/O取到内存的数据都发挥了做用,从而提升了查询效率。
B+Tree是在B-Tree基础上的一种优化,使其更适合实现外存储索引结构,InnoDB存储引擎就是用B+Tree实现其索 引结构。
从上一节中的B-Tree结构图中能够看到每一个节点中不只包含数据的key值,还有data值。而每个页的存储空间是 有限的,若是data数据较大时将会致使每一个节点(即一个页)能存储的key的数量很小,当存储的数据量很大时同 样会致使B-Tree的深度较大,增大查询时的磁盘I/O次数,进而影响查询效率。在B+Tree中,全部数据记录节点都 是按照键值大小顺序存放在同一层的叶子节点上,而非叶子节点上只存储key值信息,这样能够大大加大每一个节点 存储的key值数量,下降B+Tree的高度。
B+Tree相对于B-Tree有几点不一样:
将上一节中的B-Tree优化,因为B+Tree的非叶子节点只存储键值信息,假设每一个磁盘块能存储4个键值及指针信 息,则变成B+Tree后其结构以下图所示:
一般在B+Tree上有两个头指针,一个指向根节点,另外一个指向关键字最小的叶子节点,并且全部叶子节点(即数据 节点)之间是一种链式环结构。所以能够对B+Tree进行两种查找运算:一种是对于主键的范围查找和分页查找,另 一种是从根节点开始,进行随机查找。
可能上面例子中只有22条数据记录,看不出B+Tree的优势,下面作一个推算:
InnoDB存储引擎中页的大小为16KB,通常表的主键类型为INT(占用4个字节)或BIGINT(占用8个字节),指针 类型也通常为4或8个字节,也就是说一个页(B+Tree中的一个节点)中大概存储16KB/(8B+8B)=1K个键值(由于 是估值,为方便计算,这里的K取值为〖10〗^3)。也就是说一个深度为3的B+Tree索引能够维护10^3 * 10^3 * 10^3 = 10亿 条记录。
实际状况中每一个节点可能不能填充满,所以在数据库中,B+Tree的高度通常都在2~4层。mysql的InnoDB存储引擎 在设计时是将根节点常驻内存的,也就是说查找某一键值的行记录时最多只须要1~3次磁盘I/O操做。
数据库中的B+Tree索引能够分为汇集索引(clustered index)和辅助索引(secondary index)。上面的B+Tree示 例图在数据库中的实现即为汇集索引,汇集索引的B+Tree中的叶子节点存放的是整张表的行记录数据。辅助索引与 汇集索引的区别在于辅助索引的叶子节点并不包含行记录的所有数据,而是存储相应行数据的汇集索引键,即主 键。当经过辅助索引来查询数据时,InnoDB存储引擎会遍历辅助索引找到主键,而后再经过主键在汇集索引中找到 完整的行记录数据
是最基本的索引,它没有任何限制.
CREATE index 索引名 on 表名(列名)
复制代码
与前面的普通索引相似,不一样的就是:索引列的值必须惟一,但容许有空值。若是是组合索引,则列值的组合必须 惟一。
CREATE UNIQUE index 索引名 on 表名(列名)
复制代码
是一种特殊的惟一索引,一个表只能有一个主键,不容许有空值。通常是在建表的时候同时建立主键索引.也就 是说主键约束默认索引
指多个字段上建立的索引,只有在查询条件中使用了建立索引时的第一个字段,索引才会被使用。使用组合索引时 遵循最左前缀集合
CREATE index 索引名 on 表名(列名,列名...)
复制代码
主要用来查找文本中的关键字,而不是直接与索引中的值相比较。fulltext索引跟其它索引大不相同,它更像是一个 搜索引擎,而不是简单的where语句的参数匹配。fulltext索引配合match against操做使用,而不是通常的where 语句加like。它能够在create table,alter table ,create index使用,不过目前只有char、varchar,text 列上能够 建立全文索引。值得一提的是,在数据量较大时候,现将数据放入一个没有全局索引的表中,而后再用CREATE index建立fulltext索引,要比先为一张表创建fulltext而后再将数据写入的速度快不少
CREATE TABLE `table` (
`id` int(11) NOT NULL AUTO_INCREMENT ,
`title` char(255) CHARACTER NOT NULL ,
`content` text CHARACTER NULL ,
`time` int(10) NULL DEFAULT NULL ,
PRIMARY KEY (`id`), FULLTEXT (content)
);
复制代码
ALTER mytable ADD [UNIQUE] INDEX [indexName] ON 表名(列名)
复制代码
DROP INDEX [indexName] ON 表名;
复制代码
SHOW INDEX FROM 表名
复制代码
‐‐ 有四种方式来添加数据表的索引:
ALTER TABLE tbl_name ADD PRIMARY KEY (column_list): 该语句添加一个主键,这意味着索引值必须是惟一的, 且不能为NULL。
ALTER TABLE tbl_name ADD UNIQUE index_name (column_list): 这条语句建立索引的值必须是惟一的(除了NULL 外,NULL可能会出现屡次)。
ALTER TABLE tbl_name ADD INDEX index_name (column_list): 添加普通索引,索引值可出现屡次。
ALTER TABLE tbl_name ADD FULLTEXT index_name (column_list):该语句指定了索引为 FULLTEXT ,用于全文索引。
复制代码
MySQL Optimizer是一个专门负责优化SELECT 语句的优化器模块,它主要的功能就是经过计算分析系统中收 集的各类统计信息,为客户端请求的Query 给出他认为最优的执行计划,也就是他认为最优的数据检索方式。
使用explain关键字能够模拟优化器执行SQL查询语句,从而知道MYSQL是如何处理SQL语句的.咱们能够用执行 计划来分析查询语句或者表结构的性能瓶颈
explain sql语句
复制代码
create table t1(
id int primary key,
name varchar(20),
col1 varchar(20),
col2 varchar(20),
col3 varchar(20)
);
create table t2(
id int primary key,
name varchar(20),
col1 varchar(20),
col2 varchar(20),
col3 varchar(20)
);
create table t3(
id int primary key,
name varchar(20),
col1 varchar(20),
col2 varchar(20),
col3 varchar(20)
);
insert into t1 values(1,'zs1','col1','col2','col3');
insert into t2 values(1,'zs2','col2','col2','col3');
insert into t3 values(1,'zs3','col3','col2','col3');
create index ind_t1_c1 on t1(col1);
create index ind_t2_c1 on t2(col1);
create index ind_t3_c1 on t3(col1);
create index ind_t1_c12 on t1(col1,col2);
create index ind_t2_c12 on t2(col1,col2);
create index ind_t3_c12 on t3(col1,col2);
复制代码
select 查询的序列号,包含一组数字,表示查询中执行Select子句或操做表的顺序
三种状况:
explain select t2.* from t1,t2,t3 where t1.id = t2.id and t1.id= t3.id and t1.name = 'zs';
复制代码
explain select t2.* from t2 where id = (select id from t1 where id = (select t3.id from t3 where t3.name='zs3'));
复制代码
explain select t2.* from (select t3.id from t3 where t3.name='zs3') s1,t2 where s1.id = t2.id;
复制代码
查询类型,主要用于区别
explain select col1,col2 from t1 union select col1,col2 from t2;
复制代码
显示这一行的数据是和哪张表相关
访问类型: all, index,range,ref,eq_ref, const,system,null
最好到最差依次是: system > const > eq_ref>ref >range > index > all , 最好能优化到range级别或则ref级别
explain select * from (select * from t1 where id=1) s1;
复制代码
explain select * from t1,t2 where t1.id = t2.id;
复制代码
explain select * from t1 where col1='zs1';
复制代码
explain select * from t1 where id between 1 and 10;
复制代码
explain select id from t1;
复制代码
explain select * from t1;
复制代码
SQL查询中可能用到的索引,但查询的过程当中不必定真正使用
查询过程当中真正使用的索引,若是为null,则表示没有使用索引
查询中使用了覆盖索引,则该索引仅出如今key列表中
explain select t2.* from t1,t2,t3 where t1.col1 = ' ' and t1.id = t2.id and t1.id= t3.id;
复制代码
explain select col1 from t1;
复制代码
索引中使用的字节数,可经过该列计算查询中使用的索引的长度,在不损失精确度的状况下,长度越短越好, key_len显 示的值为索引字段的最大可能长度,并不是实际使用长度, 即key_len是根据表定义计算而得
explain select * from t1 where col1='c1';
复制代码
explain select * from t1 where col1='col1' and col2 = 'col2';
‐‐ 注意: 为了演示这个结果,咱们删除了c1上面的索引
alter table t1 drop index ind_t1_c1;
‐‐ 执行完成以后,再次建立索引
create index ind_t1_c1 on t1(col1);
复制代码
显示索引的哪一列被使用了,若是可能的话,是一个常数.哪些列或者常量被用于查找索引列上的值
explain select * from t1,t2 where t1.col1 = t2.col1 and t1.col2 = 'col2';
复制代码
根据表统计信息及索引选用的状况,估算找出所需记录要读取的行数 (有多少行记录被优化器读取) ,越少越好
包含其它一些很是重要的额外信息
explain select col1 from t1 where col1='col1' order by col3;
复制代码
‐‐ 上面这条SQL语句出现了using filesort,可是咱们去执行下面这条SQL语句的时候它,又不会出现using filesort
explain select col1 from t1 where col1='col1' order by col2;
复制代码
‐‐ 如何优化第一条SQL语句 ?
create index ind_t1_c13 on t1(col1,col3);
explain select col1 from t1 where col1='col1' order by col3;
复制代码
explain select col1 from t1 where col1>'col1' group by col2;
复制代码
explain select col1 from t1 where col1 >'col1' group by col1,col2;
复制代码
explain select col2 from t1 where col1='col1';
复制代码
explain select col2 from t1;
复制代码
explain select * from t1 where col1='zs' and col1='ls';
复制代码
需求: 查询 category_id 为1 且 comments 大于 1 的状况下,views 最多的 article_id。
CREATE TABLE IF NOT EXISTS `article` (
`id` INT(10) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
`author_id` INT(10) UNSIGNED NOT NULL,
`category_id` INT(10) UNSIGNED NOT NULL,
`views` INT(10) UNSIGNED NOT NULL,
`comments` INT(10) UNSIGNED NOT NULL,
`title` VARBINARY(255) NOT NULL,
`content` TEXT NOT NULL
);
INSERT INTO `article`(`author_id`, `category_id`, `views`, `comments`, `title`, `content`) VALUES
(1, 1, 1, 1, '1', '1'),
(2, 2, 2, 2, '2', '2'),
(1, 1, 3, 3, '3', '3');
复制代码
EXPLAIN SELECT id,author_id FROM article WHERE category_id = 1 AND comments > 1 ORDER BY views DESC LIMIT 1;
复制代码
create index idx_article_ccv on article(category_id,comments,views);
复制代码
结论: type 变成了 range,这是能够忍受的。可是 extra 里使用 Using filesort 还是没法接受的。可是咱们已经创建 了索引,为啥没用呢? 这是由于按照 BTree 索引的工做原理, 先排序 category_id, 若是遇到相同的 category_id 则再 排序 comments,若是遇到相同的 comments 则再排序 views。当 comments 字段在联合索引里处于中间位置时,因 comments > 1 条件是一个范围值(所谓 range),MySQL 没法利用索引再对后面的 views 部分进行检索,即 range 类 型查询字段后面的索引无效。
‐‐ 先删除优化一索引
DROP INDEX idx_article_ccv ON article;
‐‐ 建立索引
create index idx_article_cv on article(category_id,views);
复制代码
需求: 使用左外链接查询class和book
CREATE TABLE IF NOT EXISTS `class` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`card` INT(10) UNSIGNED NOT NULL,
PRIMARY KEY (`id`)
);
CREATE TABLE IF NOT EXISTS `book` (
`bookid` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`card` INT(10) UNSIGNED NOT NULL,
PRIMARY KEY (`bookid`)
);
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
复制代码
EXPLAIN SELECT * FROM class LEFT JOIN book ON class.card = book.card;
复制代码
ALTER TABLE `book` ADD INDEX Y ( `card`);
复制代码
能够看到第二行的 type 变为了 ref,rows 也变成了优化比较明显。
这是由左链接特性决定的。LEFT JOIN 条件用于肯定如何从右表搜索行,左边必定都有,因此右边是咱们的关键点,必定 须要创建索引。
也就是说: 外链接在相反方建立索引
select a.name ,bc.name from t_emp a left join
(select b.id , c.name from t_dept b
inner join t_emp c on b.ceo = c.id)bc
on bc.id = a.deptid
上段查询中用到了子查询,必然 bc 表没有索引。确定会进行全表扫描
上段查询 能够直接使用 两个 left join 优化
select a.name , c.name from t_emp a
left outer join t_dept b on a.deptid = b.id
left outer join t_emp c on b.ceo=c.id
全部条件均可以使用到索引
若必须用到子查询,可将子查询设置为驱动表,由于驱动表的type 确定是 all,而子查询返回的结果表没有索引,一定 也是all
复制代码
CREATE TABLE tblA(
id int primary key not null auto_increment,
age INT,
birth TIMESTAMP NOT NULL,
name varchar(200)
);
INSERT INTO tblA(age,birth,name) VALUES(22,NOW(),'abc');
INSERT INTO tblA(age,birth,name) VALUES(23,NOW(),'bcd');
INSERT INTO tblA(age,birth,name) VALUES(24,NOW(),'def');
CREATE INDEX idx_A_ageBirth ON tblA(age,birth,name);
复制代码
MySQL支持二种方式的排序,FileSort和Index,Index效率高. 它指MySQL扫描索引自己完成排序。FileSort方式效 率较低。
EXPLAIN SELECT * FROM tbla WHERE age > 1 ORDER BY age
复制代码
EXPLAIN SELECT * FROM tbla WHERE age = 1 ORDER BY birth
复制代码
group by实质是先排序后进行分组,遵守索引建的最佳左前缀
where高于having,能写在where限定的条件就不要去having限定了
limit经常使用于分页处理,时常会伴随order by 从句使用, 所以大多时候会使用Filesorts,这样会形成大量的IO问题