赞
踩
Select [distinct ] <字段名称>
from 表1 [ <join类型> join 表2 on <join条件> ]
where <where条件>
group by <字段>
having <having条件>
order by <排序字段>
limit <起始偏移量,行数>
(8)Select
(9)distinct 字段名1,字段名2,
(7)[fun(字段名)]
(1)from 表1
(3)<join类型>join 表2
(2)on <join条件>
(4)where <where条件>
(5)group by <字段>
(6)having <having条件>
(10)order by <排序字段>
(11)limit <起始偏移量,行数>
MYSQL多表查询主要使用连接查询 , 连接查询的方式主要有 :
隐式内连接 : Select 字段 From 表A , 表B where 连接条件
显式内连接 : Select 字段 From 表A inner join 表B on 连接条件
外连接
左外连接 : Select 字段 From 表A left join 表B on 连接条件
右外连接 : Select 字段 From 表A right join 表B on 连接条件
全外连接:(很少用)
交叉连接 : 笛卡尔效应 (很少用)
内连接:只有两个元素表相匹配的才能在结果集中显示。
外连接:左外连接: 左边为驱动表,驱动表的数据全部显示,匹配表的不匹配的不会显示。
右外连接:右边为驱动表,驱动表的数据全部显示,匹配表的不匹配的不会显示。
全外连接:连接的表中不匹配的数据全部会显示出来。
交叉连接:笛卡尔效应,显示的结果是链接表数的乘积。
char的长度是不可变的,用空格填充到指定长度大小,而varchar的长度是可变的。
char的存取速度比varchar要快得多
char的存储方式是:对英文字符(ASCII)占用1个字节,对一个汉字占用两个字节。
varchar的存储方式是:对每个英文字符占用2个字节,汉字也占用2个字节。
索引是是在存储引擎中实现的,也就是说不同的存储引擎,会使用不同的索引。
InnoDB引擎默认用的是B+树,
memory存储引擎把HASH当成默认索引,
myisam存储引擎支持B+和全文索引,默认也是B+树。
B+树:
根和子节点,存储的是索引值,叶子节点存储的数据。
一个节点存储的索引值,取决与我们索引值的大小。默认一个节点页,16K 1K = 1024B
一个叶子节点存储的记录,取决于一条记录的大小,比如一条记录是1KB,一个叶能存16条
从存储角度,索引分为聚集索引与非聚集索引
聚集索引(主键):叶子节点存储的值是我们插入的原始数据
非聚集索引:叶子节点存储的是索引与主键值的对应关系。
B+树与B树:
最大区别,B树的每个节点都存储数据。
B+Tree是B-Tree的变种,如图所示:
我们可以看到,两部分:
绿色框框起来的部分,是索引部分,仅仅起到索引数据的作用,不存储数据。
红色框框起来的部分,是数据存储部分,在其叶子节点中要存储具体的数据。形成单向链表结构
拓展-一个三阶(三层高)的B+树,可以存储多少条数据:
非叶子节点都是由key+指针域组成的,一个key占8字节,一个指针占6字节,而一个节点总共容量是16KB,那么可以计算出一个节点可以存储的元素个数:16*1024字节 / (8+6)=1170个元素。
查看mysql索引节点大小:show global status like 'innodb_page_size'; -- 节点大小:16384
当根节点中可以存储1170个元素,那么根据每个元素的地址值又会找到下面的子节点,每个子节点也会存储1170个元素,那么第二层即第二次IO的时候就会找到数据大概是:1170*1170=135W。也就是说B+Tree数据结构中只需要经历两次磁盘IO就可以找到135W条数据。
对于第二层每个元素有指针,那么会找到第三层,第三层由key+数据组成,假设key+数据总大小是1KB,而每个节点一共能存储16KB,所以一个第三层一个节点大概可以存储16个元素(即16条记录)。那么结合第二层每个元素通过指针域找到第三层的节点,第二层一共是135W个元素,那么第三层总元素大小就是:135W*16结果就是2000W+的元素个数。
结合上述分析B+Tree有如下优点:
千万条数据,B+Tree可以控制在小于等于3的高度
所有的数据都存储在叶子节点上,并且底层已经实现了按照索引进行排序,还可以支持范围查询,叶子节点是一个双向链表,支持从小到大或者从大到小查找
关于B+树数据结构,感兴趣的同学可以通过一个网站进行演示:
https://www.cs.usfca.edu/~galles/visualization/BPlusTree.html
插入一组数据: 100 65 169 368 900 556 780 35 215 1200 234 888 158 90 1000 88 120 268 250 。然后观察一些数据插入过程中,节点的变化情况。
B+Tree索引(InnoDB引擎默认)、Hash索引(memory引擎默认)、Full-text索引(myisam存储引擎支持)
聚集索引(聚簇)、非聚集索引(非聚簇,二级索引)
主键索引、唯一索引、普通索引、全文索引
普通索引:MySQL中基本索引类型,没有什么限制,允许在定义索引的列中插入重复值和空值,纯粹为了查询数据更快一点。
唯一索引:索引列中的值必须是唯一的,但是允许为空值
主键索引:是一种特殊的唯一索引,不允许有空值,一个表只能由一个主键索引。
全文索引: 只有在MyISAM引擎、InnoDB(5.6以后)上才能使⽤用,而且只能在CHAR,VARCHAR,TEXT类型字段上使⽤用全⽂文索引。
单列索引 : 在MYSQL数据库表的某一列上面创建的索引叫单列索引 , 单列索引又分为
组合索引(联合索引) : 在MYSQL数据库表的多个字段组合上创建的索引 , 称为组合索引也叫联合索引
组合索引的使用,需要遵循左前缀原则
一般情况下,建议使用组合索引代替单列索引(主键索引除外)
create table index idxNameage on 表(name,age,addr)
分类 | 含义 | 特点 |
聚集(聚簇)索引(Clustered Index) | 将数据存储与索引放到了一块,索引结构的叶子节点保存了行数据 | 必须有,而且只有一个 |
二级索引/非聚集(非聚簇)(Secondary Index) | 将数据与索引分开存储,索引结构的叶子节点关联的是对应的主键 | 可以存在多个 |
聚集索引选取规则:
如果存在主键,主键索引就是聚集索引。
如果不存在主键,将使用第一个唯一(UNIQUE)索引作为聚集索引。
如果表没有主键,或没有合适的唯一索引,则InnoDB会自动生成一个rowid作为隐藏的聚集索引。
MYSQL存储引擎有很多, 常用的就二种 : MyISAM和InnoDB , 者两种存储引擎的区别 ;
MyISAM支持256TB的数据存储 , InnerDB只支持64TB的数据存储
MyISAM 不支持事务 , InnerDB支持事务
MyISAM 不支持外键 , InnerDB支持外键
创建索引
create [ unique ] index 索引名 on 表名 (字段名,... ) ;
为tb_emp表的name字段建立一个索引
create index idx_emp_name on tb_emp(name);
为tb_emp表的name、username字段建立一个联合索引
create index idx_emp_name_username on tb_emp(name,username);
需要查询二次
如果使用MyISAM存储引擎 , 会首先根据索引查询到数据行指针, 再根据指针获取数据
如果是InnoDB存储引擎 , 会根据索引查找指定数据关联的主键ID , 再根据主键ID去主键索引中查找数据
当我们为一张表的name字段建立了索引 , 执行如下查询语句 :
select name,age from user where name='Alice'
那么获取到数据的过程为 :
根据name='Alice'查找索引树 , 定位到匹配数据的主键值为 id=18
根据id=18到主索引获取数据记录 (回表查询)
**先定位主键值,再定位行记录就是所谓的回表查询,它的性能较扫一遍索引树低 **
覆盖索引是指只需要在一棵索引树上就能获取SQL所需的所有列数据 , 因为无需回表查询效率更高
实现覆盖索引的常见方法是:将被查询的字段,建立到联合索引里去。
执行如下查询语句 : select name,age from user where name='Alice'
因为要查询 name 和 age二个字段 , 那么我们可以建立组合索引
create index index_name_age on user(name,age)
那么索引存储结构如下 :
这种情况下, 执行select name,age from user where name='Alice' , 会先根据name='Alice', 找到记录 , 这条记录的索引上刚好又包含了 age 数据 , 直接把 Alice 77数据返回 , 就不会执行回表查询 , 这就是覆盖索引
在mysql建立联合索引时会遵循左前缀匹配的原则,即最左优先,在检索数据时从联合索引的最左边开始匹配,组合索引的第一个字段必须出现在查询组句中,这个索引才会被用到 ;
如果索引了多列(联合索引),要遵守最左前缀法则。最左前缀法则指的是查询从索引的最左列开始,并且不跳过索引中的列。如果跳跃某一列,索引将会部分失效(后面的字段索引失效)。
例如 : create index index_age_name_sex on tb_user(age,name,sex);
上述SQL语句对 age,name和sex建一个组合索引index_age_name_sex ,实际上这条语句相当于建立了(age) , (age,name) , (age,name,sex)三个索引 .
- select * from tb_user where age = 49 ; -- 使用索引
-
- select * from tb_user where age = 49 and name = 'Alice' ; -- 使用索引
-
- select * from tb_user where age = 49 and name = 'Alice' and sex = 'man'; -- 使用索引
-
- select * from tb_user where age = 49 and sex = 'man'; -- 使用索引 , 但是只有 age 匹配索引 sex没有走索引
-
- select * from tb_user where name = 'Alice' and age = 49 and sex = 'man' ; -- 使用索引 , 因为MySQL的查询优化器会自动调整 where 子句的条件顺序以使用适合的索引
-
- select * from tb_user where name = 'Alice' and sex = 'man' ; -- 不会使用索引
MySQL 索引通常是被用于提高 WHERE 条件的数据行匹配时的搜索速度,编写合理化的SQL能够提高SQL的执行效率
不要在列上使用函数和进行运算
不要在列上使用函数,这将导致索引失效而进行全表扫描。
尽量避免使用 != 或 <> 等否定操作符
联合索引中,出现范围查询(>,<),范围查询右侧的列索引失效。
比如如下查询: age+status
select * from tb_user where profession = '软件工程' and age > 30 and status = '0';
当范围查询使用> 或 < 时,走联合索引了,但是索引的长度只有age的长度,就说明范围查询右边的status字段是没有走索引的。
select * from tb_user where profession = '软件工程' and age >= 30 and status = '0';
当范围查询使用>= 或 <= 时,走联合索引了,索引的长度是age+status的长度,就说明所有的字段都是走索引的。
所以,在业务允许的情况下,尽可能的使用类似于 >= 或 <= 这类的范围查询,而避免使用 > 或 < 。
尽量避免使用 or 来连接条件
用or分割开的条件, 如果or前的条件中的列有索引,而后面的列中没有索引,那么涉及的索引都不会被用到。
- select * from tb_user where id = 10 or age = 23;
- select * from tb_user where phone = '17799990017' or age = 23;
由于age没有索引,所以即使id、phone有索引,索引也会失效。所以需要针对于age也要建立索引。
当or连接的条件,左右两侧字段都有索引时,索引才会生效。
多个单列索引并不是最佳选择,建立组合索引代替多个单列索引, 可以避免回表查询
查询中的某个列有范围查询,则其右边所有列都无法使用索引优化查找
索引不会包含有NULL值的列
当查询条件左右两侧类型不匹配的时候会发生隐式转换,隐式转换带来的影响就是可能导致索引失效而进行全表扫描。
like 语句的索引失效问题
like 的方式进行查询,在 like “value%” 可以使用索引,但是对于 like “%value%” 这样的方式,执行全表查询
尽量不要使用in或not in
使用in查询数据,会进行全表扫描,降低效率
对于连续的数值,能用 between 就不要用 in 了:
- #优化前:
- select id from t where num in(8,23,99)
- #优化后
- select id from t where num between 1 and 3
必须使用in 可以使用使用union all 或 union 拆分sql
- select id from t where id = 7
- union all
- select id from t where id = 5
- union all
- select id from t where id = 27
需要创建索引情况
主键自动建立主键索引
频繁作为查询条件的字段应该创建索引
多表关联查询中,关联字段应该创建索引 (on 两边都要创建索引)
查询中排序的字段,应该创建索引
频繁查找字段 , 应该创建索引
查询中统计或者分组字段,应该创建索引
不要创建索引情况
表记录太少
经常进⾏行行增删改操作的表
频繁更新的字段
where条件里使用频率不高的字段
索引设计原则
1). 针对于数据量较大,且查询比较频繁的表建立索引。
2). 针对于常作为查询条件(where)、排序(order by)、分组(group by)操作的字段建立索引。
3). 尽量选择区分度高的列作为索引,尽量建立唯一索引,区分度越高,使用索引的效率越高。
4). 如果是字符串类型的字段,字段的长度较长,可以针对于字段的特点,建立前缀索引。
5). 尽量使用联合索引,减少单列索引,查询时,联合索引很多时候可以覆盖索引,节省存储空间,避免回表,提高查询效率。
6). 要控制索引的数量,索引并不是多多益善,索引越多,维护索引结构的代价也就越大,会影响增删改的效率。
7). 如果索引列不能存储NULL值,请在创建表时使用NOT NULL约束它。当优化器知道每列是否包含NULL值时,它可以更好地确定哪个索引最有效地用于查询。
当字段类型为字符串(varchar,text,longtext等)时,有时候需要索引很长的字符串,这会让索引变得很大,查询时,浪费大量的磁盘IO, 影响查询效率。此时可以只将字符串的一部分前缀,建立索引,这样可以大大节约索引空间,从而提高索引效率。
create index idx_xxxx on table_name(column(n)) ;
为tb_user表的email字段,建立长度为5的前缀索引。
create index idx_email_5 on tb_user(email(5));
前缀长度
可以根据索引的选择性来决定,而选择性是指不重复的索引值(基数)和数据表的记录总数的比值,索引选择性越高则查询效率越高, 唯一索引的选择性是1,这是最好的索引选择性,性能也是最好的。
- select count(distinct email) / count(*) from tb_user ;
- select count(distinct substring(email,1,5)) / count(*) from tb_user ;
单列索引:即一个索引只包含单个列。
联合索引:即一个索引包含了多个列。
从设计方面 选择合适的存储引擎 , 合适的字段类型 , 遵循范式(反范式设计)
存储引擎 :
不需要事务, 不需要外键读写较多的的使用MyIsam
需要事务, 需要外键的使用InnoDB
合适的字段类型 , 例如 :
定长字符串用char , 不定长用varchar
状态, 性别等有限数量值的用tinyint
遵循范式 :
第一范式1NF,原子性
第二范式2NF,消除部分依赖
第三范式3NF,消除传递依赖
• 第⼀范式:1NF 原⼦性,列或者字段不能再分,要求属性具有原⼦性,不可再分解; 单一属性由基本类型构成,包括整型、实数、字符型、逻辑型、日期型等。
• 第⼆范式:2NF 唯⼀性,⼀张表只说⼀件事,是对记录的惟⼀性约束,要求记录有惟 ⼀标识;
• 第三范式:3NF 直接性,数据不能存在传递关系,即每个属性都跟主键有直接关系, ⽽不是间接关系。
2.从功能方面可以对索引优化,采用缓存缓解数据库压力,分库分表。
3.从架构方面可以采用主从复制,读写分离,负载均衡
- #效率低
- select * from table where limit 1000000,10
正确的处理方法是 : 先快速定位需要获取的id再关联查询获取数据
- # 效率高 使用覆盖索引
- select * from table where id in (select id from table limit 100000,10)
- # 效率高 定位数据,再使用覆盖索引
- select * from table where id in (select id from table where id>100000 order by id limit 10)
通过使用覆盖索引查询返回需要的主键,再根据主键关联原表获得需要的数据
可以在MYSQL配置文件中开启慢查询 , 有两种方式可以开启慢查询
方式一 : 修改my.ini配置文件 , 重启 MySQL 生效
- [mysqld]
- log_output='FILE,TABLE'
- slow_query_log='ON'
- long_query_time=0.001
方式二 : 设置全局变量
- SET GLOBAL slow_query_log = 'ON';
- SET GLOBAL log_output = 'FILE,TABLE';
- SET GLOBAL long_query_time = 0.001;
可以开启慢查询, 通过慢查询日志或者命令, 获取到执行慢的SQL语句
慢查询日志记录了所有执行时间超过指定参数(long_query_time,单位:秒,默认10秒)的所有SQL语句的日志。
MySQL的慢查询日志默认没有开启,我们可以查看一下系统变量 slow_query_log。
使用EXLPAIN命令分析SQL语句的执行过程
EXLPAIN命令, 比较重要的字段**(加黑加粗的是重要的)** :
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 的值越大越好。 |
extra | 额外信息 |
select_type重点解读
type重点解读:查询性能从上到下依次是最好到最差
extra重点解读
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。