DDL(Data Definition Languages):数据定义语句,经常使用的语句关键字主要包括 create、drop、alter等操做表结构html
DML(Data Manipulation Language):数据操做语句,经常使用的语句关键字主要包括 insert、delete、udpate 和select 等操做数据mysql
DCL(Data Control Language):数据控制语句,用户的访问权限和安全级别。主要的语句关键字包括 grant、revoke 等算法
链接者:不一样语言的代码程序和mysql的交互(SQL交互)sql
链接池:管理、缓冲用户的链接,线程处理等须要缓存的需求数据库
管理服务和工具组件:系统管理和控制工具,例如备份恢复、Mysql复制、集群等缓存
SQL接口:接受用户的SQL命令,而且返回用户须要查询的结果安全
解析器:对SQL进行解析,判断语法是否正确session
查询优化器:SQL语句在查询以前会使用查询优化器对查询进行优化数据结构
举个例子并发
-- 下面的SQL company 和 name 都有索引,索引类型也相同,字段类型也相同 -- 索引字段能匹配的数据越少,优先使用该索引字段 select * from dept_table where company = '集团' and name = '财务部';
缓存:若是查询缓存有命中的查询结果,查询语句就能够直接去查询缓存中取数据
存储引擎:INSERT DELETE UPDATE SELECT 数据的一种方式
存储引擎名称 | 特色 | 应用场景 |
---|---|---|
InnoDB | 支持事务、行锁、支持MVCC多版本并发控制,并发性高 | 应用OLTP业务系统 |
MyISAM | 不支持事务,MySQL8以后被废弃了,并发很低,资源利用率也很低 | 应用OLAP业务系统,建议生产环境尽可能少少使用 MyISAM 存储引擎 |
MariaDB columnstore | 列式存储引擎,高压缩功能 | 数据仓库,OLAP业务系统 |
OLTP(On-Line Transaction Processing):联机事务处理,好比增删改查,完成一笔交易处理
OLAP(On-Line Analytical Processing):联机分析处理,支持复杂的分析操做,侧重决策支持,作数据分析,决策,好比 ElasticSearch
SQL执行过程
原子性(A):要么全完成,要么全不完成
一致性(C):在事务开始以前和事务结束之后,数据库的完整性约束没有被破坏(好比A给B转帐100元,A减小100,B增长100)
隔离性(I):事务之间互不干扰
持久性(D):事务提交后保存起来了
读未提交(read uncommitted):A事务变动后没提交,B能看到
读已提交(read committed):A事务变动后提交,B才能看到
可重复读(repeatable read):一个事务执行过程当中看到的数据,老是跟这个事务在启动时看到的数据是一致的
串行化(serializable):同一行记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行
脏读:读未提交的隔离级别下,A事务读到B更新却没commit的数据
不可重复读:侧重修改,A事务两次读取同一数据两次不一致
幻读:侧重数据增减,A事务两次读取数据不一样
事务隔离级别与并发问题
事务隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交(read uncommitted) | 是 | 是 | 是 |
读已提交(read committed) | 否 | 是 | 是 |
可重复读(repeatable read) | 否 | 否 | 是 |
串行化(serializable) | 否 | 否 | 否 |
验证事务的隔离级别
事务A | 事务B |
---|---|
begin; | begin; |
select * from dept_table; --V1 | |
select * from dept_table; --V2 | |
update dept_table set name = '研发部' where id = 1; | |
select * from dept_table; --V3 | |
commit; | |
select * from dept_table; --V4 | |
commit; | |
select * from dept_table; --V5 |
事务相关SQL
# 设置事务隔离级别 set session transaction isolation level read uncommitted; set session transaction isolation level read committed; set session transaction isolation level repeatable read; # 查询当前事务隔离级别 select @@tx_isolation; # 查询全局事务隔离级别 select @@global.tx_isolation; # 开启事务 begin; start transaction; # 提交事务 commit; # 回滚事务 rollback;
多版本并发控制 MVCC(Multi-Version Concurrency Control): MySQL 的 InnoDB 存储引擎实现隔离级别的一种具体方式,在每行数据都增长两个隐藏字段,一个记录建立的版本号,一个记录删除的版本号,用于实现提交读和可重复读这两种隔离级别。而未提交读隔离级别老是读取最新的数据行,无需使用 MVCC。可串行化隔离级别须要对全部读取的行都加锁,单纯使用 MVCC 没法实现
系统版本号:是一个递增的数字,每开始一个新的事务,系统版本号就会自动递增
事务版本号:事务开始时的系统版本号
插入一条新数据(事务版本号 = 1)
id | name | create_version | delete_version |
---|---|---|---|
1 | Tom | 1 |
执行更新操做(事务版本号 = 2)
update table set name= 'Tom2' where id = 1;
id | name | create_version | delete_version |
---|---|---|---|
1 | Tom | 1 | 2 |
1 | Tom2 | 2 |
删除操做(事务版本号 = 3)
delete from table where id = 1;
id | name | create version | delete version |
---|---|---|---|
1 | Tom2 | 2 | 3 |
查询操做要知足两个条件才能查询出来
-- 1. T事务(假设:当前事务版本号 = 1)插入一条数据 -- create_version = 1 delete_version = null insert into `up`.`dept_table`(`id`, `company`, `name`) values (5, '总部', '综合创新部'); -- 1. A事务(假设:当前事务版本号 = 2) 查询数据 4条结果 select * from dept_table; -- 2. B事务(假设:当前事务版本号 = 3) 更新ID = 1 的数据 update dept_table set name = '金融服务部' where id = 5; -- 3. A事务继续查询 select * from dept_table;
当执行select操做时 InnoDB默认会执行快照读,会记录下此次select后的结果,以后select 的时候就会返回此次快照的数据,即便其余事务提交了不会影响当前select的数据,这就实现了可重复读了。快照的生成当在第一次执行select的时候,也就是说假设当A开启了事务,而后没有执行任何操做,这时候B insert了一条数据而后commit,这时候A执行 select,那么返回的数据中就会有B添加的那条数据。以后不管再有其余事务commit都没有关系,由于快照已经生成了,后面的select都是根据快照来的
示例1 B事务的查询结果是否有A新增的数据?
A事务 | B事务 |
---|---|
start transaction; | start transaction; |
insert into up .dept_table (id , company , name ) values (6, '总部', '综合创新部'); |
|
select * from dept_table; | |
commit; | |
select * from dept_table; | |
commit |
示例2 B事务的两次查询结果是否相同?第二次查询是否有A新增的数据?
A事务 | B事务 |
---|---|
start transaction; | start transaction; |
select * from dept_table; | |
insert into up .dept_table (id , company , name ) values (6, '总部', '综合创新部'); |
|
select * from dept_table; | |
commit; | |
select * from dept_table; | |
commit; |
对数据修改的操做(update、insert、delete、select …... lock in share mode、select …... for update)都是采用当前读的模式。在执行这几个操做时会读取最新的记录,即便是别的事务提交的数据也能够查询到。假设要update一条记录,可是在另外一个事务中已经delete掉这条数据而且commit了,若是update就会产生冲突,因此在update的时候须要知道最新的数据。也正是由于这样因此才致使上面咱们测试的那种状况
示例
A事务 | B事务 |
---|---|
start transaction; | start transaction; |
select * from dept_table; -- V1 | |
insert into up .dept_table (id , company , name ) values (6, '总部', '综合创新部'); |
|
commit; | |
select * from dept_table; -- V2 | |
update dept_table set name = '新部门' where id = 6; | |
select * from dept_table; -- V3 | |
commit; |
快照读 | 当前读 |
---|---|
select * from table | select * from table for update |
select * from table lock in share mode | |
insert、delete、insert |
数据库锁机制简单来讲,就是数据库为了保证数据的一致性,使各类共享资源在被并发访问时变得有序而设计的一种规则
MySQL的锁机制比较简单,最显著的特色时不一样的存储引擎支持不一样的锁机制,咱们锁知道的,InnoDB支持行锁,有时也会升级为表锁,MyISAM只支持表锁
表锁 | 行锁 |
---|---|
开销小 | 开销大 |
加锁快 | 加锁慢 |
不会出现死锁 | 会出现死锁 |
锁粒度大 | 锁粒度小 |
发生锁冲突的几率高 | 发生锁冲突的几率低 |
并发度相对低 | 并发度相对高 |
简称S锁,若事务T对数据对象加上读锁,则事务T能够读但不能修改,其余事务只能再对该数据加读锁不能加写锁,直到T释放该数据的读锁。即一个事务在读取一个数据行时,其余事务也能够读,但不能对该数据进行增删改的操做
读锁的实现方式
-- 查询是否自动提交模式 show variables like '%auto%';
简称X锁,若事务T对数据对象加上写锁,事务T能够读也能够修改,其余事务不能再对该数据加任何锁,不能读也不能写,直到T释放该数据上的锁。即一个事务获取了一个数据行的写锁,其余事务就不能再获取该行的其余锁,写锁优先级最高
写锁的实现方式
在事务A中开启查询,会自动得到一个MDL锁,事务B就不能够执行任何DDL语句的操做
事务A | 事务B |
---|---|
begin; | begin; |
select * from score_table; | |
alter table score_table add num tinyint(1) not null default 0; -- 阻塞 | |
commit; | |
commit; |
-- 查询进程列表 show full processlist;
在MySQL存储引擎InnoDB中,意向锁是表级锁。并且有两种意向锁类型,分别为意向共享锁和意向排他锁
其实意向锁的做用跟MDL锁相似,都是防止在事务进行过程当中,执行DDL语句的操做而致使数据不一致
在默认的事务隔离级别为RR,而且参数 innodb_locks_unsafe_for_binlog = 0 的模式下,行锁的种类有三种
注:主键和惟一索引都是行记录的锁模式。在RC隔离级别下,只有 record lock 记录锁模式
注:普通索引默认的就是 next-key lock模式
-- 查看索引 show index from dept_table;
company 字段有索引
事务A | 事务B |
---|---|
begin; | begin; |
update score_table set name = 'a' where score= 60; | |
update score_table set name = 'aaa' where score= 60; -- 出现锁等待 | |
update score_table set name = 'bbb' where score= 70; -- 正常更新 | |
commit; | |
commit; |
company 字段无索引
事务A | 事务B |
---|---|
begin; | begin; |
update score_table set name = 'a' where score= 60; | |
update score_table set name = 'bbb' where score= 70; -- 出现锁等待 | |
commit; | |
commit; |
InnoDB的锁是加在索引上的
在RR事务隔离级别,为了不幻读现象,引入了 gap lock,但它只锁定记录的范围,不包含记录自己,即不容许在次范围内插入任何数据
事务隔离级别RR
事务A | 事务B |
---|---|
begin; | begin; |
select * from score_table where score < 80 lock in share mode; | |
insert into score_table(name, score) values ('dd', 75); -- 等待 | |
commit; | |
commit; |
事务隔离级别RC
-- 查看事务隔离级别 show variables like '%tx_isolation%';
事务A | 事务B |
---|---|
begin; | begin; |
set session transaction isolation level read committed; | set session transaction isolation level read committed; |
select * from score_table where score < 80 lock in share mode; | |
insert into score_table(name, score) values ('dd', 75); -- 可提交 | |
commit; | |
commit; |
证实间隙锁只是针对RR隔离级别才管用,从锁的角度避免了幻读发生
记录锁与间隙锁的组合,当InnoDB扫描表记录时,会先对选中的索引加上记录锁,再对索引记录两边的间隙加上间隙锁
事务A | 事务B |
---|---|
begin; | begin; |
select * from score_table where score < 85 for update; | |
insert into score_table(name, score) values ('dd', 85); -- 等待 | |
commit; | |
commit; |
证实不光锁定 score < 85 这个区间的数据,还包含 85 这个值的自己
锁等待是指一个事务过程当中产生的锁,其余事务须要等待上一个事务释放它的锁,才能占用该资源。若是该事务一直不释放,就须要持续等待下去,直到超过锁等待时间,会报一个等待超时的错误
-- 查询锁等待时间 show variables like '%innodb_lock_wait%'; -- 修改全局变量方式 set global innodb_lock_wait_timeout = 2; set @@global.innodb_lock_wait_timeout = 5; -- 修改当前事务变量方式 set innodb_lock_wait_timeout = 2;
死锁是指两个或两个以上的进程在执行过程当中,因争夺资源而形成的一种互相等待的现象,即事务1锁住数据D1,正请求对D2加锁,而事务2锁住了D2,正请求加锁D1,这样就会致使死锁
事务A | 事务B |
---|---|
begin; | begin; |
update score_table set name = 'a' where score= 60; | |
update score_table set name = 'bbb' where score= 70; | |
update score_table set name = 'b' where score= 70; | |
update score_table set name = 'aaa' where score= 60; | |
commit; | |
commit; |
-- 查看死锁展现信息 show engine innodb status;
在工做中,开发人员在开发初期因为多种缘由设计表的过程当中,没有给后面可能会常常访问的字段增长索引这个概念,因为后期的业务调整需求变动且数据也在不断增加,会根据某些已存在的字段做为条件查询。后面再增长索引会很麻烦,须要考虑现有系统的可用性,还须要考虑到当前增长索引列的内容。因此建立索引须要结合现有业务还要预知后期可能存在的业务变动,使设计的数据表伸缩性更强,在设计初期把数据表设计完善也可以让后面的开发事半功倍。
索引利用好了就是一辆”法拉利“,利用很差就成了”三轮车“了。
帮助MySQL作高效查询的一种数据结构,比如是一本书的目录,经过目录能快速找到要查看的内容
提升查询效率
从存储结构划分:BTree索引(B-Tree或B+Tree索引),Hash索引
从应用层划分:主键索引,惟一索引,普通索引,复合索引,全文索引,SPATIAL
根据键值的逻辑顺序和表数据行的物理存储顺序:汇集索引,非汇集索引
汇集索引:叶子节点存放表中全部行数据记录的信息
非汇集索引:不是汇集索引就是非汇集索引
索引通常在 where,order by,group by,表链接的条件列起做用
索引无效:
-- 1. 查询条件的值是null explain select * from dept_table where name = null; -- 2. NOT条件 好比:<>、in、not in、exists、not exists explain select * from dept_table where name <> '新部门'; explain select * from dept_table where name in ('财务部2'); -- 可使用索引 explain select * from dept_table where name in ('财务部2', '新部门'); -- 不使用索引 explain select * from dept_table where name not in ('新部门'); -- 一个和多个都不使用索引 explain select * from dept_table where exists (select * from dept_table where name = '新部门'); explain select * from dept_table where not exists (select * from dept_table where name = '新部门'); -- 3. LIKE通配符在前面 explain select * from dept_table where name like '%部门'; explain select * from dept_table where name like '部门%'; -- 可使用索引 explain select * from dept_table where name like '新%门%'; -- 可使用索引 -- 4. 条件列上包括函数 explain select * from dept_table where upper(name) = 'NEW'; explain select * from dept_table where name = upper('new'); -- 可使用索引
索引是存储到磁盘上的文件
MyISAM引擎和InnoDB引擎共有文件
.frm文件:在MYSQL中创建任何一张数据表,在其数据目录对应的数据库目录下都有对应表的.frm文件,.frm文件是用来保存每一个数据表的元数据(meta)信息,包括表结构的定义等,.frm文件跟数据库存储引擎无关,也就是任何存储引擎的数据表都必须有.frm文件,命名方式为数据表名.frm,如user.frm. .frm文件能够用来在数据库崩溃时恢复表结构
InnoDB引擎文件
.ibd文件:单表表空间文件,每一个表使用一个表空间文件(file per table),存放用户数据库表数据和索引
MyISAM引擎
.MYD:即 my data,表数据文件
.MYI:即 my index,索引文件
推荐一个数据结构网站:www.cs.usfca.edu/~galles/visualization/Algorithms.html
若有 三、一、二、十、九、0、四、6这8个数据
哈希结构
二叉树结构(右子树大于左子树)
不使用二叉树缘由:若是索引列查入的数据是单边增加的,会致使查询时依旧变慢,最差状况时间复杂度会变成O(N)
红黑树结构(是一种平衡的二叉查找树)
特色:
MySQL的数据实际是存储在文件中,而磁盘IO的查找速度是要远小于内存速度的,因此减小磁盘IO的次数能很大程度的提升MySQL性能
磁盘IO时间 = 寻道 + 磁盘旋转 + 数据传输时间
从磁盘读取数据时,系统会将逻辑地址发给磁盘,磁盘将逻辑地址转换为物理地址(哪一个磁道,哪一个扇区)。 磁头进行机械运动,先找到相应磁道,再找该磁道的对应扇区,扇区是磁盘的最小存储单元
性能对比
机械硬盘的连续读写性能很好,但随机读写性能不好。
随机读写时,磁头须要不停的移动,时间都浪费在了磁头寻址上。 而在实际的磁盘存储里,是不多顺序存储的,由于这样的维护成本会很高
因为存储介质的特性,磁盘自己存取就比主存慢不少,再加上机械运动耗费,磁盘的存取速度每每是主存的几百分分之一,所以为了提升效率,要尽可能减小磁盘I/O。为了达到这个目的,磁盘每每不是严格按需读取,而是每次都会预读,即便只须要一个字节,磁盘也会从这个位置开始,顺序向后读取必定长度的数据放入内存。这样作的理论依据是计算机科学中著名的局部性原理:
当一个数据被用到时,其附近的数据也一般会立刻被使用。
程序运行期间所须要的数据一般比较集中。
因为磁盘顺序读取的效率很高(不须要寻道时间,只需不多的旋转时间),所以对于具备局部性的程序来讲,预读能够提升I/O效率。
预读的长度通常为页(page)的整倍数。页是计算机管理存储器的逻辑块,硬件及操做系统每每将主存和磁盘存储区分割为连续的大小相等的块,每一个存储块称为一页(在许多操做系统中,页得大小一般为4k),主存和磁盘以页为单位交换数据。当程序要读取的数据不在主存中时,会触发一个缺页异常,此时系统会向磁盘发出读盘信号,磁盘会找到数据的起始位置并向后连续读取一页或几页载入内存中,而后异常返回,程序继续运行。
度(m):节点数据存储个数
深度关系(h):h=log(m+1)N
B+Tree相对于BTree区别:
MyISAM引擎使用B+Tree做为索引结构,叶节点的data域存放的是数据记录的地址
这里设表一共有三列,假设咱们以Col1为主键,上图是一个MyISAM表的主索引(Primary key)示意。能够看出MyISAM的索引文件仅仅保存数据记录的地址。在MyISAM中,主索引和辅助索引(Secondary key)在结构上没有任何区别,只是主索引要求key是惟一的,而辅助索引的key能够重复。若是咱们在Col2上创建一个辅助索引,则此索引的结构以下图所示
一样也是一颗B+Tree,data域保存数据记录的地址。所以,MyISAM中索引检索的算法为首先按照B+Tree搜索算法搜索索引,若是指定的Key存在,则取出其data域的值,而后以data域的值为地址,读取相应数据记录
因此MyISAM是非“汇集索引”,从索引文件和数据文件分别存储就能够看出。也与下面介绍的InnoDB的“汇集索引”作区分
虽然InnoDB也使用B+Tree做为索引结构,但具体实现方式却与MyISAM大相径庭
第一个不一样:MyISAM索引文件和数据文件是分离的,索引文件仅保存数据记录的地址。InnoDB的数据文件自己就是索引文件,从文件存储上就能够知道,表数据文件自己就是按B+Tree组织的一个索引结构,这棵树的叶节点data域保存了完整的数据记录。这个索引的key是数据表的主键,所以InnoDB表数据文件自己就是主索引
InnoDB主索引(同时也是数据文件)的示意图
能够看到叶节点包含了完整的数据记录。这种索引叫作汇集索引。由于InnoDB的数据文件自己要按主键汇集,因此InnoDB要求表必须有主键(MyISAM能够没有),若是没有显式指定,则MySQL系统会自动选择一个能够惟一标识数据记录的列做为主键,若是不存在这种列,则MySQL自动为InnoDB表生成一个隐含字段做为主键,这个字段长度为6个字节,类型为长整形。
第二个不一样:与MyISAM索引的不一样是InnoDB的辅助索引data域存储相应记录主键的值而不是地址。换句话说,InnoDB的全部辅助索引都引用主键做为data域
InnoDB辅助索引(辅助索引得到主键索引)的示意图
这里以英文字符的ASCII码做为比较准则。汇集索引这种实现方式使得按主键的搜索十分高效,可是辅助索引搜索须要检索两遍索引:首先检索辅助索引得到主键,而后用主键到主索引中检索得到记录。
空间方面:uuid存储空间更大
比较关系:索引之间有比较关系,整型的比较会更快
结构方面(最重要一点):使用自增主键保证插入数据都是向叶子节点后面顺序插入,若是使用uuid比较,生成的uuid不必定顺序插入,致使插入到节点的中间位置,若是该节点已经满了,会致使节点进行分裂变成新的结构
一致性和节省存储空间
B-Tree节点的度设置等于一个页,每次新建节点直接申请一个页的空间,这样就保证一个节点物理上也存储在一个页里,就实现了一个节点的载入只须要载入一次I/O
redolog:数据日志
undolog:ibdata1
binlog:逻辑日志