#方式1:
SELECT ...,....,...
FROM ...,...,....
WHERE 多表的连接条件
AND 不包含组函数的过滤条件
GROUP BY ...,...
HAVING 包含组函数的过滤条件
ORDER BY ... ASC/DESC
LIMIT ...,...
#方式2:
SELECT ...,....,...
FROM ... JOIN ...
ON 多表的连接条件
JOIN ...
ON ...
WHERE 不包含组函数的过滤条件
AND/OR 不包含组函数的过滤条件
GROUP BY ...,...
HAVING 包含组函数的过滤条件
ORDER BY ... ASC/DESC
LIMIT ...,...
#其中:
#(1)from:从哪些表中筛选
#(2)on:关联多表查询时,去除笛卡尔积
#(3)where:从表中筛选的条件
#(4)group by:分组依据
#(5)having:在统计结果中再次筛选
#(6)order by:排序
#(7)limit:分页
FROM -> WHERE -> GROUP BY -> HAVING -> SELECT 的字段 -> DISTINCT -> ORDER BY -> LIMIT
SELECT DISTINCT player_id, player_name, count(*) as num # 顺序 5
FROM player JOIN team ON player.team_id = team.team_id # 顺序 1
WHERE height > 1.80 # 顺序 2
GROUP BY player.team_id # 顺序 3
HAVING num > 2 # 顺序 4
ORDER BY num DESC # 顺序 6
LIMIT 2 # 顺序 7
在 SELECT 语句执行这些步骤的时候,每个步骤都会产生一个 虚拟表 ,然后将这个虚拟表传入下一个步骤中作为输入
SELECT 是先执行 FROM 这一步的。在这个阶段,如果是多张表联查,还会经历下面的几个步骤
拿到查询数据表的原始数据(虚拟表vt1),再此基础上进行WHERE阶段,过滤得到虚拟表vt2
再进行GROUP 和 HAVING 阶段,对vt2进行分组和分组过滤,得到中间虚拟表vt3、vt4
随后进入 SELECT 和 DISTINCT 阶段,分别得到中间的虚拟表 vt5-1、vt5-2
指定字段排序,ORDER BY 阶段,得到虚拟表vt6
取出指定行记录,LIMIT阶段,得到虚拟表 vt7
MySQL5.5之后默认采用的引擎
MySQL5.5之前默认的存储引擎
用于数据存档
特征 | 支持 |
---|---|
压缩数据 | 支持 |
备份/时间点恢复(在服务器中实现,而不是在存储引擎中) | 支持 |
地理空间数据类型 | 支持 |
加密数据(在服务器中实现) | 支持 |
更新数据字典的统计信息 | 支持 |
锁粒度 | 行锁 |
数据缓存 | 不支持 |
外键 | 不支持 |
全文检索索引 | 不支持 |
聚集索引 | 不支持 |
地理空间索引 | 不支持 |
哈希索引 | 不支持 |
索引缓存 | 不支持 |
B树索引 | 不支持 |
MVCC | 不支持 |
存储限制 | 无限制 |
交易 | 不支持 |
集群数据库 | 不支持 |
置于内存的表,采用逻辑介质是内存,响应速度快。当mysqld守护进程崩溃时数据会丢失,另外要求存储的数据是数据长度不变的格式
特征:
使用场景:
特点 | MyISAM | InnoDB | MEMORY | MERGE | NDB |
---|---|---|---|---|---|
存储限制 | 有 | 64TB | 有 | 没有 | 有 |
事务安全性 | 支持 | ||||
锁机制 | 表锁 | 行锁 | 表锁 | 表锁 | 行锁 |
B树索引 | 支持 | 支持 | 支持 | 支持 | 支持 |
哈希索引 | 支持 | 支持 | |||
全文索引 | 支持 | ||||
集群索引 | 支持 | ||||
数据缓存 | 支持 | 支持 | 支持 | ||
索引缓存 | 缓存索引,不缓存数据 | 缓存索引、数据 | 支持 | 支持 | 支持 |
数据可压缩 | 支持 | ||||
空间使用 | 低 | 高 | N/A | 低 | 低 |
内存使用 | 低 | 高 | 中等 | 低 | 高 |
批量插入的速度 | 高 | 低 | 高 | 高 | 高 |
支持外键 | 支持 |
1、原子,主要涉及InnoDB事务,与MySQL相关的特性主要包括:
2、一致性,主要涉及保护数据不崩溃的内部InnoDB处理过程,与MySQL相关的特性主要包括:
3、隔离,应用于事务的级别,与MySQL相关的特性主要包括:
4、耐久性, ACID模型的耐久性主要涉及与硬件配置相互影响的MySQL软件特性。由于硬件复杂多样 化,耐久性方面没有具体的规则可循。与MySQL相关的特性有:
事务:一组逻辑操作单元,使数据从一种状态变换到另一种状态
事务处理原则:保证所有事务都作为一个工作单元来执行
MySQL 服务端是允许多个客户端连接的,这意味着 MySQL 会出现同时处理多个事务的情况
在同时处理多个事务时,会可能出现脏读(dirty read)、不可重复读(non-repeatable read)、幻读(phantom read)
严重性排序:脏读 > 不可排序 > 幻读
SQL标准提出了四种隔离级别来规避这些现象,隔离级别越高,效率越低
针对不同的隔离级别,并发事务可能发生的现象如下
这四种隔离级别是如何实现的呢?
读未提交:直接读取最新的数据
读提交、可重复读:通过Read View实现,可将Read View理解为一个快照
串行化:加读写锁
Read View有四个重要字段:
对于使用InnoDB存储引擎的数据库表,它的聚簇索引记录都包含下面两个隐藏列:
在创建 Read View 后,可将记录中 trx_id 划分为三种情况:
这种通过版本链来控制并发事务访问同一个记录的行为被称为 MVCC(多版本并发控制)
MySQL官方定义:帮助MySQL高效获取数据的数据结构
下图是MySQL的结构图,索引和数据位于存储引擎中
优点
缺点
需要索引
where
查询条件的字段,若查询调节不是一个字段,可建立联合索引GROUP BY
和ORDER BY
的字段,这样在查询的时候就不需要再去做一次排序了,因为在建立索引之后 B+Tree中的记录都是排序好的不需要索引
where
、group by
、order by
里用到的字段,索引的价值是快速定位,若起不到定位的字段通常不需要创建索引可以按照四个角度来分类索引
从数据结构的角度来看,MySQL常见的索引有B+Tree索引、Hash索引、Full-Text索引
每一种存储引擎支持的索引类型不一定相同,下面是MySQL中常见的存储引擎所支持的索引类型
InnoDB 是在 MySQL 5.5 之后成为默认的 MySQL 存储引擎,B+Tree 索引类型也是 MySQL 存储引擎采用最多的索引类型
在创建表时,InnoDB存储引擎会根据不同的场景选择不同的列作为索引:
其他索引都属于辅助索引,也称为二级索引或非聚簇索引,创建的主键索引和二级索引默认使用B+Tree索引
主键索引的叶子节点存放实际数据;二级索引的叶子节点存放主键值
回表:先检查二级索引找那个的 B+Tree 的索引值,找到对应的叶子节点,然后获取主键值,再由主键索引中的 B+Tree 树查询到对应的叶子节点,然后获取整行数据,这个过程叫做回表,也就是说要查两个 B+Tree才能查到数据
覆盖索引:当要查询的数据能在二级索引的 B+Tree的叶子节点里能查询到时(查主键值),就不需要再查主键索引查,这个过程叫做覆盖索引,也就是只需要一个 B+Tree就能找到数据
为什么MySQL InnoDB选择 B+Tree作为索引的数据结构?
B+Tree vs B Tree
B+Tree 只在叶子节点存储数据,而 B 树 的非叶子节点也要存储数据,所以 B+Tree 的单个节点的数据量更小,在相同的磁盘 I/O 次数下,就能查询更多的节点;另外,B+Tree 叶子节点采用的是双链表连接,适合 MySQL 中常见的基于范围的顺序查找,而 B 树无法做到这一点
B+Tree vs 二叉树
对于有 N 个 叶子节点的 B+Tree,其搜索复杂度为O(logdN),其中d表示节点允许的最大子节点个数为d个
在实际应用中,d值是大于100的,这样就保证了即使数据达到千万级别,B+Tree的高度依然维持在 3~4 层左右,一次数据查询操作只需要做3~4此的磁盘I/O操作就能查询到目标数据
二叉树的每个父节点的儿子节点个数只能是2个,意味着其搜索复杂度为O(logN),着比B+Tree高出不少,因此二叉树检索的目标数据所经历的磁盘I/O次数要更多
B+Tree vs Hash
Hash在做等值查询时效率很快,搜索复杂度为O(1),但是不适合做范围查询。这也是B+Tree索引要比Hash表索引有着更广泛的适用场景的原因
按物理存储的角度来看,索引分为:聚簇索引(主键索引)、二级索引(辅助索引)
这两者的区别也就是:
在查询时使用了二级索引,若查询的数据能在二级索引里查询的到,就不需要回表,这个过程为覆盖索引;若查询的数据不再二级索引里,那么会先检索二级索引,找到对应的叶子节点,获取到主键值,然后再检索主键索引,就能查询到数据了,这个过程为回表
按字段特性的角度来看,索引分为:主键索引、唯一索引、普通索引、前缀索引
按字段个数的角度来看,索引分为:单列索引、联合索引(复合索引)
单列索引:建立在单列上的索引称为单列索引,如主键索引
联合索引:建立在多列上的索引称为联合索引
最左匹配原则:按照最左优先的方式进索引的匹配
利用索引的前提是索引里的 key 是有序的
联合索引的最左匹配原则失效:在遇到范围查询时(>、<、between、like),会停止匹配,范围列可以用到联合索引,但是范围列后面的列无法用到联合索引
索引下推优化:在联合索引遍历过程中,对联合索引中包含的字段先做判断,直接过滤掉不满足条件的记录,减少回表次数
提高索引效率:建立联合索引时,把区分度大的字段排在前面,这样区分度大的字段越有可能被更多的 SQL 使用到
区分度为某个字段 column 不同值的个数除以表的总行数
区分度 = d i s t t i n c t ( c o l u m n ) c o u n t ( ∗ ) 区分度 = \frac{disttinct(column)}{count(*)} 区分度=count(∗)disttinct(column)
常见的优化索引方法:前缀索引优化、覆盖索引优化、主键索引最好是自增、防止索引失效
使用某个字段中字符串的前几个字符建立索引,减小索引字段大小,可以增加一个索引页来存储索引值,有效提高索引的查询速度
前缀索引的局限性:
覆盖索引:SQL中查询的所有字段,在索引 B+Tree 的叶子节点上都能找到那些索引,从二级索引中查询得到记录,而不需要通过聚簇索引查询获得,可以避免回表操作
覆盖索引的好处:不需要查询出包含整行记录的所有信息,也就减少了大量的 I/O 操作
建表时,默认将主键索引设置为自增
InnoDB创建主键索引默认为聚簇索引,数据被存放在 B+Tree的叶子节点上。同一个叶子节点内的各个数据时按主键顺序存放的,每当一条新的数据插入时,数据库会根据主键值将其插入到对应的叶子节点中
使用自增主键:每次插入的新数据就会拿顺序添加到当前索引节点的位置,不需要移动已有的数据,当页面写满,就会自动开辟一个新页面。每次插入一条新记录,都是追加操作,不需要重新移动数据,一次这种插入数据的方法效率非常高
使用非自增主键:每次插入主键的索引值都是随机的,因此每次插入新的数据时,就可能会插入到现有数据页中间的某个位置,这将不得不移到其他数据来满足新数据的插入,甚至需要从一个页面复制数据到另一个页面,我们通常将这个情况称为页分裂。页分裂可能会造成大量的内存碎片,导致索引结构不紧凑,从而影响查询效率
用上了索引并不意味着查询的时候回使用到索引,所以我们心里清楚有哪些情况会导致索引失效,从而避免写出索引失效的查询语句
常见发生索引失效的情况:
like %xx
或者like %xx%
这两种方式都会造成索引失效根据加锁范围,可分为:全局锁、表级锁、行锁
全局锁
使用全局锁 -> flush tables with read lock
执行后,整个数据库就只处于只读状态,其他线程执行以下操作,都会被阻塞:
-对数据的增删改操作,如insert、delete、update语句
-对标结构的更改操作,如alter table、drop table语句
释放全局锁 -> unlock tables
全局锁应用场景?
主要应用于全库逻辑备份,这样在备份数据库期间,不会因为数据或表结构的更新,而出现备份文件的数据与预期不一样
加全局锁的缺点?
整个数据库为只读状态。若数据库中有很多数据,备份将花费很多时间,这期间业务只能读数据,而不能更新数据,会造成业务停滞
使用全局锁会影响业务,如何避免?
若数据库引擎支持的事务支持 可重复读的隔离级别,那么在备份数据库之前开启事务,会先创建 Read View,然后整个事务执行期间都在用这个 Read View,并且由于 MVCC 的支持,备份期间业务依然可以对数据进行更新操作
表级锁
MySQL里面表级别的锁有:表锁;元数据锁(MDL);意向锁;AUTO-INC锁
表锁:
表级别的共享锁(读锁) -> lock tables t_student read;
表级别的独占锁(写锁) -> lock tables t_stuent wirte;
表锁除了会限制别的线程的读写外,还会限制本线程接下来的读写操作
尽量避免在使用InnoDB引擎表时使用表锁,表锁颗粒度太大,会影响并非性能
元数据锁(MDL):
不需要显示使用MDL,当我们对数据库表进行操作时,会自动给表加上MDL
-对一张表进行 CRUD 操作时,加的是MDL读锁
-对一张表做结构变更操作时,加的是MDL写锁
MDL是为了保证当用户对表执行 CRUD 操作时,防止其他线程对表结构做变更
-当有线程执行 select 语句(加MDL读锁)的期间,若有其他线程要更改该表结构(申请MDL写锁),那么将会被阻塞,直到执行完 select 语句(释放MDL读锁)
-当有线程对表结构进行变更(加MDL写锁)的期间,若有其他线程执行 CRUD 操作(申请MDL读锁),那么就会被阻塞,直到表结构变更完成(释放MDL写锁)
MDL不需要显示调用,那么它是什么时候释放的? -> MDL在事务提交后才会释放,事务执行期间,MDL一直持有
为什么线程因为申请不到MDL写锁,会导致后续的申请读锁的查询操作会被阻塞? -> 申请MDL锁的操作会形成一个队列,队列中写锁获取优先级高于读锁
意向锁:
意向锁的目的:快速判断表里是否有记录被加锁
-在使用InnoDB引擎的表里对某些记录加上 共享锁 之前,需要先在表级别加上一个 意向共享锁
-在使用InnoDB引擎的表里对某些记录加上 独占锁 之前,需要先在表级别加上一个 意向独占锁
-若没有意向锁,那么在加独占锁时,就需要遍历所有记录,查看记录是否存在独占锁,效率很慢
-有意向锁,那么在加独占锁时,直接查看该表是否有意向独占锁,不用遍历表里的记录
意向锁是表级锁,意向锁之间不会发生冲突;会和表锁发生冲突;不会和行级锁发生冲突
AUTO-INC锁:
特殊的表锁机制,锁不再是一个事务提交后才释放,而是再执行完插入语句后就会立即释放
一个事务在持有 AUTO-INC 锁的过程中,其他事务向该表插入语句都会被阻塞,从而保证插入数据时,被AUTO_INCREMENT修饰的字段是递增的
MySQL 5.1.22 版本开始,InnoDB 存储引擎提供了一种轻量级的锁来实现自增
-当 innodb_autoinc_lock_mode = 0,就采用 AUTO-INC 锁
-当 innodb_autoinc_lock_mode = 2,就采用轻量级锁
-当 innodb_autoinc_lock_mode = 1,两锁混用,确定插入记录的数量就采用轻量级锁,不确定时就采用 AUTO-INC 锁
行级锁
行级锁分为三类:
-Record Lock(记录锁):也就是仅仅把一条记录锁上
-Gap Lock(间隙锁):锁定一个范围,不包含记录本身
-Next-Key Lock:Record Lock + Gap Lock的组合,锁定一个范围,包含记录本身
行级锁加锁规则较为复杂,不同场景加锁形式不同,next-key lock在一些场景下会退化为记录锁或间隙锁
不同版本的加锁规则可能不同,以下MySQL版本为8.0.26
唯一索引和非唯一索引的范围查询的加锁规则不同之处在于
MySQL有不同类型的日志,用来存储不同类型的日志:慢查询日志、通用查询日志、错误日志、二进制日志。在MySQL8之后又新增了两种日志:中继日志、数据定义语句日志
除了二进制日志外,其他日志均为文本文件。默认情况下,所有日志均创建于 MySQL数据目录中
binlog的三种格式:Row、Statement、Mixed
事务执行过程中,先把日志写入binlog cache,事务提交时,再把binlog cache写到binlog文件中。一个事务的binlog不能被拆开,确保一次性写入,系统将给每个线程分配一块内存作为binlog cache
write和fsync的时机,由参数sync_binlog控制,默认为0
在出现I0瓶颈时,将sync_binlog设置成一个较大的值,能提升性能。同样的,若机器宕机会丢失最近N个事务的binlog日志
在执行更新语句过程中,会记录redolog、binlog两块日志,以基本的事务为单位。
redolog在事务执行过程中可以不断写入,binlog只在提交事务时写入
为了解决日志之间的逻辑一致问题,InnoDB存储引擎使用两阶段提交方案
实际上主从同步的原理是基于binlog进行数据同步的。在主从复制过程中,会基于3个线程来操作,一个主库线程,两个从库线程
简单来说为以下三步:
主从同步要求
主从延迟原因
网络正常情况下,主从延迟的主要来源是备库接收完binlog和执行完这个事务之间的时间差
主从延迟的直接表现:从库消费中继日志的速度比主库生产binlog的速度慢
减少主从延迟的方案
数据一致性问题的解决
若操作的数据存储在同一个数据库中,那么对数据进行更新时,可对记录加写锁,这样在读取时就不会发生数据不一致的情况,但从库的作用仅为备份,未起到读写分离、分担主库读压力的作用
读写分离情况下,解决主从同步中数据不一致的问题,就是解决主从之间数据复制方式的问题。若按照数据一致性的从若到强划分,有3中复制方式:异步复制、半同步复制、组复制
异步复制
半同步复制
组复制
异步复制、半同步复制都无法最终保证数据一致性问题
组复制技术,MRG(MySQL Group Replication),于MySQL在5.7.17推出的一种新的数据复制技术,基于Paxos协议的状态机复制
MGR如何工作?
将多个节点共同组成一个复制组,在执行读写(RW)事务时,需要通过一致性协议层同样,当同意节点数量大于(N/2+1)时才可进行提交,针对只读(RO)事务则不需要组内同意,直接COMMIT即可
在一个复制组内有多个节点组成,它们各自维护了自己的数据副本,并且在一致性协议层实现了原子消息和全局有序消息,从而保证组内数据一致性
MySQL数据库教程天花板,mysql安装到mysql高级,强!硬!_哔哩哔哩_bilibili
图解MySQL介绍 | 小林coding (xiaolincoding.com)
原文链接