一.存储引擎
1.创建的存储引擎
- .frm(存储表定义)
- .MYD(MYData,存储数据)
- .MYI (MYIndex,存储索引)
- .frm(存储表的定义)
- .ibd(存储数据和索引)
show engines;
可以进入mysql的存储目录查看存储的文件
2.区别
种类 | 锁机 制 | B- 树索 引 | 哈希索 引 | 外键 | 事务 | 索引缓存 | 数据缓存 |
MyISAM | 表锁 | 支持 | 不支持 | 不支持 | 不支持 | 支持 | 不支持 |
InnoDB | 行锁 | 不支持 | 支持 | 支持 | 支持 | 支持 | 支持 |
Memory | 表锁 | 支持 | 支持 | 不支 持 | 不支 持 | 支持 | 支持 |
- 锁机制:表示数据库在并发请求访问的时候,多个事务在操作时,并发操作的粒度。
- B-树索引和哈希索引:主要是加速SQL的查询速度。
- 外键:子表的字段依赖父表的主键,设置两张表的依赖关系。
- 事务:多个SQL语句,保证它们共同执行的原子操作,要么成功,要么失败,不能只成功一部分,失败需要回滚事务。
- 索引缓存和数据缓存:和MySQL Server的查询缓存相关,在没有对数据和索引做修改之前,重复查询可以不用进行磁盘I/O(数据库的性能提升,目的是为了减少磁盘I/O操作来提升数据库访问效率),读取上一次内存中查询的缓存就可以了。
二.索引
1.简介
2.索引的分类
逻辑上可以分为以下几类
- 普通索引:没有任何限制条件,可以给任何类型的字段创建普通索引(创建新表&已创建表,数量是不限的,一张表的一次sql查询只能用一个索引 where a=1 and b='M')
- 唯一性索引:使用UNIQUE修饰的字段,值不能够重复,主键索引就隶属于唯一性索引
- 主键索引:使用Primary Key修饰的字段会自动创建索引(MyISAM, InnoDB)
- 单列索引:在一个字段上创建索引
- 多列索引:在表的多个字段上创建索引 (uid+cid,多列索引必须使用到第一个列,才能用到多列索引,否则索引用不上)
- 全文索引:使用FULLTEXT参数可以设置全文索引,只支持CHAR,VARCHAR和TEXT类型的字段上,常用于数据量较大的字符串类型上,可以提高查询速度(线上项目支持专门的搜索功能,给后台服务器增加专门的搜索引擎支持快速高效的搜索 elasticsearch 简称es C++开源的搜索引擎 搜狗的workflow)
3.索引的创建和删除
CREATE TABLE index1(id INT ,name VARCHAR ( 20 ),sex ENUM ( 'male' , 'female' ),INDEX(id,name));
CREATE [UNIQUE] INDEX 索引名 ON 表名(属性名( length ) [ ASC | DESC ]);
例如
create index nameidx on student(name(10));
DROP INDEX 索引名 ON 表名 ;
结合实践讲出来
注意点:
- 经常作为where条件过滤的字段考虑添加索引
- 字符串列创建索引时,尽量规定索引的长度,而不能让索引值的长度key_len过长
- 索引字段涉及类型强转、mysql函数调用、表达式计算等,索引就用不上了
4.索引的执行过程
1.explain查看执行计划
使用explain查看sql的执行计划,分析索引的执行过程,查看user表的索引:
explain select * from student where uid=1;
explain select * from student where name='zhangsan';
2.explain结果字段分析
- select_type
simple:表示不需要union操作或者不包含子查询的简单select语句。有连接查询时,外层的查询为simple且只有一个。
primary:一个需要union操作或者含有子查询的select,位于最外层的单位查询的select_type即为primary且只有一个。
union:union连接的两个select查询,除了第一个表外,第二个以后的表的select_type都是 union。
union result:包含union的结果集,在union和union all语句中,因为它不需要参与查询,所以id字段为null。 - table
显示查询的表名;
如果不涉及对数据库操作,这里显示null;
如果显示为尖括号就表示这是个临时表,后边的N就是执行计划中的id,表示结果来自于这个查询产生的;
如果是尖括号括起来<union M,N>也是一个临时表,表示这个结果来自于union查询的id为M,N的结果集; - type
const:使用唯一索引或者主键,返回记录一定是1行记录的等值where条件时,通常type就是 const。
ref:常见于辅助索引的等值查找,或者多列主键、唯一索引中,使用第一个列之外的列作为等值查找会出现;返回数据不唯一的等值查找也会出现。
range:索引范围扫描,常见于使用<、>、is null、between、in、like等运算符的查询中。
index:索引全表扫描,把索引从头到尾扫一遍;常见于使用索引列就可以处理不需要读取数据文件的查询,可以使用索引排序或者分组的查询。
all:全表扫描数据文件,然后在server层进行过滤返回符合要求的记录。 - ref
如果使用常数等值查询,这里显示const;
如果是连接查询,被驱动表的执行计划这里会显示驱动表的关联字段; - Extra
using filesort:排序时无法用到索引,常见于order by和group by语句中。
using index:查询时不需要回表查询,直接通过索引就可以获取查询的数据。
5.索引的底层实现原理
数据库索引是存储在磁盘上的,当数据量大时,就不能把整个索引全部加载到内存了,只能逐一加载每一个磁盘块(对应索引树的节点),索引树越低,越 “ 矮胖 ” ,磁盘 IO 次数就少
1.B树
data存储的是数据本身还是磁盘上的地址?
不同的存储引擎对应的不同,对于MyISAM存储的是磁盘上的地址,因为存储数据的文件与存储索引的文件不同,而InnoDB存储的是数据本身,因为数据和索引存储在一个文件中
AVL树和B树的搜索时间是一样的,但是B树更加矮胖,因此它的磁盘IO的次数更少
- 每个节点中有key,也有data,但是每一个节点的存储空间是有限的,如果data数据较大时会导致每个节点能存储的key的数据很小
- 当存储的数据量很大时同样会导致B树的高度较大,磁盘IO次数花费增大,效率降低
2.B+树
- B树的每一个节点,存了关键字和对应的数据地址,而B+树的非叶子节点只存关键字,不存数据地址。因此B+树的每一个非叶子节点存储的关键字是远远多于B树的,B+树的叶子节点存放关键字和数据,因此,从树的高度上来说,B+树的高度要小于B树,使用的磁盘I/O次数少,因此查询会更快一些。
- B树由于每个节点都存储关键字和数据,因此离根节点近的数据,查询的就快,离根节点远的数据,查询的就慢;B+树所有的数据都存在叶子节点上,因此在B+树上搜索关键字,找到对应数据的时间是比较平均的,没有快慢之分。
- 在B树上如果做区间查找,遍历的节点是非常多的;B+树所有叶子节点被连接成了有序链表结构,因此做整表遍历和区间查找是非常容易的。
6.InnoDB的主键和二级索引树
主键就是primary key,二级索引就是普通的索引和unique
1.主键索引树
场景一:uid是主键
data中存储的是表中的对应主键的数据
2.二级索引树
场景二:uid是主键,name创建普通索引(二级索引)
data中存储的是主键,所以如果查询了普通索引和主键的值,直接可以在二级索引树上查找到数据并且进行返回,如果查询到之外的数据,需要进行回表操作,也就是需要拿着查询到的主键索引,到主键索引树上查询到相应的数据进行返回
一次SQL查询只会查询一个索引树.但是如果我们SQL如果中涉及到两个字段,那么一个索引树可以索引命中呢?此时我们可以建立多列索引,比如对于以下的SQL
select * from user where age=20 order by name;
此时建立age和name的多列索引,二级索引树上key存储的就是age,name,data存储的是主键
注意:对于多列索引,查询中必须包含第一个字段(可以不包含其他的建立多列索引的字段),否则索引不命中.
如果对于含有多个字段的过滤,每个字段有单独的索引,因为mysql只会选择一个二级索引树进行搜索,所以之后选择其中一个,那么选择哪个呢?MySQL会看看到底是按哪个字段取的数据量少,就会选择哪个二级索引树.
如果想要强行使用某个索引,可以使用
select * from user force index(age) where name='zhangsan' and age=10;
如果name和age都建立了二级索引,因为name的数据量少,所以会选用name索引树进行搜索,此时我们可以使用强制索引,强制使用age索引树搜索
7.MyISAM的主键和二级索引树
1.主键索引树
data存放的是数据的地址
2.二级索引树
此时二级索引树data存放的不是主键,二级相应的数据地址,不涉及回表的操作了.
8.聚簇索引和非聚簇索引
MyISAM存储引擎,索引结构叶子节点存储关键字和数据地址,也就是说索引关键字和数据没有在一起存放,体现在磁盘上,就是索引在一个文件存储,数据在另一个文件存储,例如一个user表,会在磁盘上存储三个文件 user.frm(表结构文件) user.MYD(表的数据文件) user.MYI(表的索引文件)。
9.哈希索引
哈希表中的数据没有任何数据可言,只能做等值查询
1.没办法处理磁盘上的数据,加载到内存上构建高效的搜索数据结构,因为它没有办法减少磁盘IO的次数
2.只适合做等值搜索,其它的范围、排序、前缀搜索等不合适
10.InnoDB自适应哈希索引
lnnoDB存储引擎监测到同样的二级索引不断被使用,那么它会根据这个二级索引,在内存上根据二级索引树(B+树)上的二级索引值,在内存上构建一个哈希索引,来加速搜索
自适应哈希索引本身的数据维护也是要耗费性能的,并不是说自适应哈希索引在任何情况下都会提升二级索引的查询性能!根据参数指标,来具体分析是否打开或者关闭自适应哈希索引!! !
show engine innodb status\G
能看到两个比较重要的信息:
- RW-latch等待的线程数量(自适应哈希索引默认分配了8个分区),同一个分区等待的线程数量过多
- 走自适应哈希索引搜索的频率和二级索引树搜索的频率
如果等待的线程数量多并且哈希索引搜索的频率低,最好关闭自适应哈希索引
三.索引总结
- MySQL以主键的值构造成一颗树,叶子节点存放着该主键对应的整行数据。此为聚簇索引。
- 其他的索引为辅助索引,叶子节点存放着索引字段的值及对应的主键值。
- 一般情况下,—次查询只能使用一条索引
- 对查询where条件中区分度高的字段加索引
- 联合索引,叶子节点存储的顺序以创建时指定的顺序为准,因此区分度高的放左边,能被多个查询复用到的放左边
- 只select需要用到的字段,尽量避免select*
- 如有必要,可使用FORCE INDEX强制索引
- 多表JOIN,先按各表的查询条件比较哪个开销小,从小表取出所有符合条件的,到大表循环查找
- 以下情况无法使用到索引,like通配符在最左,not in,=, <>,对列做函数运算,隐式数据类型转换,OR子句
面试题:如果优化SQL或索引
实际项目中是千万条的SQL语句,首先我们要能够查询到哪些SQL语句是慢的,然后通过explain进行具体情况进行分析
- 慢查询日志
- 压测执行各种业务!!
- 查看慢查询日志,找出所有执行耗时的sql
- 用explain分析这些耗时的sql
- 举例子。。
show variables like '%slow_query%';
set global slow_query_log=on;
show variables like 'long%';
set long_query_time = 1;
show profiles命令可有查看sql具体的运行时间,全局变量的名字是:profiling
set global profiling=on
show profiles查看
四.MySQL事务
1.什么是事务
- 事务是一组SQL语句的执行,要么全部成功,要么全部失败,不能出现部分成功,部分失败的结果。保证事务执行的原子操作。
- 事务的所有SQL语句全部执行成功,才能提交(commit)事务,把结果写回磁盘上。
- 事务执行过程中,有的SQL出现错误,那么事务必须要回滚(rollback)到最初的状态。
刚开始我们拿一个常见的操作来说明我们为什么需要事务.假如现在张三给李四转账100元,我们需要先把张三账户的钱减少100,然后再把李四账户的钱增加100.但是如果张三账户的钱减少100的时候突然发生网络异常,那么张三减少了100,但是李四却没有增加100,这个时候就会出现问题了.
update accout set money=money-100 where name='张三'
update accout set money=money+100 where name='李四'
为了避免这种问题的发生,因此出现了事务(transaction)
MyISAM是不支持事务的,InnoDB是支持事务,行锁的
select @@autocommit;
默认是自动提交的,可以自行设置为0,不自动提交
set autocommit=0;
2.事务的ACID特性
3.事务并发存在的问题
4.事务的隔离级别
5.MySQL的事务处理命令
0表示手动提交事务,1表示自动提交事务,设置事务提交方式为手动提交方式:
set autocommit= 0 ;
五.MySQL的锁机制
1.表级锁&行级锁
2.排它锁和共享锁
- 一个事务对数据对象 O 加了 S 锁,可以对 O 进行读取操作但不能进行更新操作。加锁期间其它事务能对O 加 S 锁但不能加 X 锁。
- 一个事务对数据对象 O 加了 X 锁,就可以对 O 进行读取和更新。加锁期间其它事务不能对 O 加任何锁。
- 显示加锁:select ... lock in share mode强制获取共享锁,select ... for update获取排它锁
3.InnoDB行级锁
recore lock:行锁
gap lock:间隙锁
next key lock:record lock+gap lock;
1.行级锁
- InnoDB行锁是通过给索引上的索引项加锁来实现的,而不是给表的行记录加锁实现的,这就意味着只有通过索引条件检索数据,InnoDB才使用行级锁,否则InnoDB将使用表锁。
- 由于InnoDB的行锁实现是针对索引字段添加的锁,不是针对行记录加的锁,因此虽然访问的是InnoDB引擎下表的不同行,但是如果使用相同的索引字段作为过滤条件,依然会发生锁冲突,只能串行进行,不能并发进行。
- 即使SQL中使用了索引,但是经过MySQL的优化器后,如果认为全表扫描比使用索引效率更高,此时会放弃使用索引,因此也不会使用行锁,而是使用表锁,比如对一些很小的表,MySQL就不会去使用索引。
- 通过普通索引加上的锁,实际上是根据普通索引映射到对应的主键,给对应的索引项加锁来实现的,因此通过不同的过滤条件,只要映射到了相同的索引项,也是被锁住的
2.间隙锁
4.MVCC
先来了解一下undolog
为什么无法解决”不可重复读”?因为每一次select都会重新产生一次数据快照,其它事务更新后而且已提交的数据,可以实时反馈到当前事务的select结果当中!
为什么无法解决”幻读”?因为每一次select都会重新产生一次数据快照,其它事务增加了和当前事务查询条件相同的新的数据并且已成功commit提交,导致当前事务再次查询时,数据多了
为什么解决了”不可重复读”?为什么无法解决”幻读”?第一次select产生数据快照,其它事务虽然修改了最新的数据,但是当前事务select时,依然查看的是最初的快照数据
- 版本未提交无法读取生成快照
- 版本已提交,但是在快照创建后提交的,无法读取
- 版本已提交,但是在快照创建前提交的,可以读取
- 当前事务内自己的更新,可以读到
4.意向共享锁和意向排他锁
要获取一张表的共享锁S或者排它锁X,最起码得确定,这张表没有被其它事务获取过X锁!这张表(1千万)里面的数据没有被其它事务获取过行锁X锁!
因此当我们加表锁的时候,需要一行一行的扫描是否加过行锁,这样效率是十分低的,因此出现了意向锁
- 意向锁是由InnoDB存储引擎获取行锁之前自己获取的
- 意向锁之间都是兼容的,不会产生冲突
- 意向锁存在的意义是为了更高效的获取表锁(表格中的X和S指的是表锁,不是行锁!!!)
- 意向锁是表级锁,协调表锁和行锁的共存关系。主要目的是显示事务正在锁定某行或者试图锁定某行。
5.InnoDB表级锁
6.死锁
7.锁的优化建议
- 尽量使用较低的隔离级别
- 设计合理的索引并尽量使用索引访问数据,使加锁更加准确,减少锁冲突的机会提高并发能力
- 选择合理的事务大小,小事务发生锁冲突的概率小
- 不同的程序访问一组表时,应尽量约定以相同的顺序访问各表,对一个表而言,尽可能以固定的顺序存取表中的行。这样可以大大减少死锁的机会
- 尽量用相等条件访问数据,这样可以避免间隙锁对并发插入的影响
- 不要申请超过实际需要的锁级别
- 除非必须,查询时不要显示加锁