1、存储引擎
MySQL体系结构:
-
连接层
最上层是一些客户端和链接服务,主要完成一些类似于连接处理、授权认证、及相关的安全方案。服务器也会为安全接入的每个客户端验证它所具有的操作权限
-
服务层
第二层架构主要完成大多数的核心服务功能,如SQL接口,并完成缓存的查询,SOL的分析和优化,部分内置函数的执行。所有跨存储引擎的功能也在这一层实现,如 过程、函数等
-
引擎层
存储引擎真正的负责了MVSL中数据的存储和提取,服务器通过AP和存储引擎进行通信。不同的存储引擎具有不同的功能,这样我们可以根据自己的需要,来选取合适的存储引擎
-
存储层
主要是将数据存储在文件系统之上,并完成与存储引擎的交互
存储引擎简介:
存储引擎就是存储数据、建立索引、更新/查询数据等技术的实现方式。存储引擎是基于表的,而不是基于库的,所以存储引擎也可被
称为表类型。
-
在创建表的时,指定存储引擎
CREATE TABLE 表名( 字段1 字段1类型 [ COMMENT 字段1注释], ... 字段n 字段n类型 [COMMENT 字段n注释] )ENGINE =INNODB [ COMMENT 表注释];
-
查看当前数据库支持的存储引擎
SHOW ENGINES;
存储引擎特点:
-
介绍
InnoDB是一种兼顾高可靠性和高性能的通用存储引擎,在 MySQL 5.5 之后,InnoDB是默认的 MySOL 存储引擎
-
特点
DML操作遵循ACID模型,支持事务 ;
行级锁,提高并发访问性能;
支持外键 FOREIGN KEY约束,保证数据的完整性和正确性 -
文件
xxxibd:xxx代表的是表名,innoDB引擎的每张表都会对应这样一个表空间文件,存储该表的表结构(frm、sdi)、数据和索引。参数: innodb fieper table
INNODB:
MyISAM:
-
介绍
MyISAM是MySQL早期的默认存储引擎
-
特点
不支持事务,不支持外键
支持表锁,不支持行锁
访问速度快
-
文件
xxx.sdi:存储表结构信息
XXXMYD:存储数据
xxx.MYI:存储索引
Memory:
-
介绍
Memorv引擎的表数据时存储在内存中的,由于受到硬件问题、或断电问题的影响,只能将这些表作为临时表或缓存使用
-
特点
内存存放
hash索引(默认) -
文件
xxx.sdi:存储表结构信息
总结:
存储引擎的选择:
在选择存储引擎时,应该根据应用系统的特点选择合适的存储引擎。对于复杂的应用系统,还可以根据实际情况选择多种存储引擎进行组合
-
InnoDB
是Mysq的默认存储引擎,支持事务、外键。如果应用对事务的完整性有比较高的要求,在并发条件下要求数据的一致性,数据操作除了插入和查询之外,还包含很多的更新、删除操作,那么InnoDB存储引擎是比较合适的选择
-
MyISAM
如果应用是以读操作和插入操作为主,只有很少的更新和删除操作,并且对事务的完整性、并发性要求不是很高,那么选择这个存储引擎是非常合适的。
-
MEMORY
将所有数据保存在内存中,访问速度快,通常用于临时表及缓存。MEMORY的缺陷就是对表的大小有限制,太大的表无法缓存在内存中,而且无法保障数据的安全性
现在MyISAM常用MongoDB代替,MEMORY常用Redis代替
2、Linux下安装MySQL:
-
CentOS为例:
-
用Xftp或者类似的软件FileZilla之类的,传到Linux服务器上/root
-
创建目录并解压
mkdir mysql tar -xvf mysql-....rpm-bundle.tar -C mysql
解压出来的都是rpm的包
-
安装需要按照顺序来,不要弄错了
cd mysql rpm -ivh mysql-community-common-8.0.26-1.el7.x86 64.rpm rpm -ivh mysgl-community-client-plugins-8.0.26-1.el7.x86 64.rpm rpm -ivh mysgl-community-libs-8.0.26-1.e17.x86 64.rpm rpm -ivh mysgl-community-libs-compat-8.0.26-1.e17.x86 64.rpm rpm -ivh mysql-community-devel-8.0.26-1.e17.x86 64.rpm rpm -ivh mysgl-community-client-8.0.26-1.el7.x86 64.rpm // server之前需要按照那个包里的icu-data-files的包 rpm -ivh mysql-community-server-8.0.26-1.el7.x86 64.rpm
执行到第三条可能会出现依赖检测失败
需要执行
yum remove mysql-libs yum install openssl-devel
在安装最后一条服务端的时候可能有错误,需要先安装icu-data-files
-
启动MySQL服务
systemctl start mysqld systemctl restart mysqld systemctl stop mysqld // 打开mysql mysql -u root -p
-
通过这种方式会自动生成mysql密码,需要去日志文件里面找密码
grep 'temporary password' /var/log/mysqld.log
在xhell里面复制标签
cat /var/log/mysqld.log
在里面可以找到temporary password,复制过来打开
-
然后更改mysql的密码
ALTER USER 'root'@'localhost' IDENTIFIED BY '1234';
但是上面的不符合mysql的安全策略,会报错,所以可以先更改一下安全策略
set global validate_password.policy = 0; // 操作前需要先把密码改成自己的一个密码,临时密码不能执行这个命令 set global validate_password.length = 4; // 再执行 ALTER USER 'root'@'localhost' IDENTIFIED BY '1234';
-
创建用户
现在只是本地访问,如果想要所有用户都可以远程访问服务器的mysql
则需要以下设置,再创建一个root账户,用户远程访问
create user 'root'@'%' IDENTIFIED WITH mysql_native_password BY '1234';
-
给创建的这个root用户分配权限
grant all on *.* to 'root'@'%';
-
下面可以打开DataGrip设置远程的服务器
点击左上角的+号,添加Data Source的MySQL
连接不上可以看是不是Linux没有关闭防火墙
ip addr // 可以查看linux的ip信息 systemctl status firewalld.service // 查看防火墙active(running)说明正在运行 systemctl stop firewalld.service // 关闭防火墙inactive(dead)
3、索引:
介绍:
索引(index)是帮助MySQL高效获取数据的数据结构(有序。在数据之外,数据库系统维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指)数据,,这样就可以在这些数据结构上实现高级查找算法,这种数据结构就是索引。
优缺点:
优势 | 劣势 |
---|---|
提高数据检索的效率,降低数据库的IO成本 | 索引列也是要占用空间的 |
通过索引列对数据进行排序,降低数据排序的成本,降低CPU的消耗。 | 索引大大提高了查询效率,同时却也降低更新表的速度,如对表进行INSERT、UPDATE、DELETE时,效率降低。 |
索引结构:
索引结构 | 描述 |
---|---|
B+Tree索引 | 最常见的索引类型,大部分引擎都支持B+树索引 |
Hash索引 | 底层数据结构是用哈希表实现的,只有精准匹配索引列的查询才有效,不支持范围查询 |
R-Tree(空间索引) | 空间索引是MyISAM引擎的一个特殊索引类型,主要用于地理空间数据类型,通常使用较少 |
Full-text(全文索引) | 是一种通过简历倒排索引,快速匹配文档的方式。类似Lucene,Solr,ES |
索引 | InnoDB | MyISAM | Memory |
---|---|---|---|
B+tree索引 | 支持 | 支持 | 支持 |
Hash索引 | 不支持 | 不支持 | 支持 |
R-tree索引 | 不支持 | 支持 | 不支持 |
Full-text | 5.6版本之后支持 | 支持 | 不支持 |
我们平常说的索引,如果没有特别指明,都是指B+树结构组织的索引
-
二叉树的缺点:顺序插入时,会形成一个链表,查询性能大大降低。大数据量情况下,层级较深,检索速度慢。
-
红黑树:大数据量情况下,层级较深,检索速度慢。
-
B-Tree(多路平衡查找树):以一颗最大度数(max-degree)为5(5阶)的B-Tree为例(每个节点最多存储4个key,5个指针)
-
B+Tree:相对于B-Tree的区别:
- 所有的数据都会出现在叶子节点
- 叶子节点形成一个单向列表
-
MySQL的索引数据结构对经典的B+Tree进行了优化,在原B+Tree的基础上,增加一个指向相邻叶子节点的链表指针,就形成了带有顺序的指针的B+Tree,提高区间访问的性能,形成了一个双向循环链表
-
Hash
哈希索引就是采用一定的Hash算法,将键值换算成新的hash值,映射到对应的槽位上,然后存储在hash表中。如果两个(或多个)键值,映射到一个相同的槽位上,他们就产生了hash冲突(也称为hash碰撞),可以通过链表来解决
Hash索引的特点:
- Hash索引只能用于对等比较(=,in),不支持范围查询(between,>,<,...)
- 无法利用索引完成排序操作
- 查询效率高,通常只需要一次检索就可以了,效率通常要高于B+tree索引
存储引擎支持:
在MySQL中,支持hash索引的是Memory引擎,而InnoDB具有自适应hash功能,hash索引是存储引擎根据B+Tree索引在指定条件下自动构建成的
思考:
为什么InnoDB存储引擎选择使用B+tree索引结构
- 相对于二叉树,层级更少,搜索效率高
- 对于B-tree,无论是叶子节点还是非叶子结点,都会保存数据,这样导致一页中存储的键值减少,指针跟着减少,要同样保存大量数据,只能增加树的高度,导致性能降低
- 相对Hash索引,B+tree支持范围匹配及排序操作
索引分类:
分类 | 含义 | 特点 | 关键字 |
---|---|---|---|
主键索引 | 针对于表中主键创建的索引 | 默认自动创建,只能有一个 | primary |
唯一索引 | 避免同一个表中某数据列中的值重复 | 可以有多个 | unique |
常规索引 | 快速定位特定数据 | 可以有多个 | |
全文索引 | 全文索引查找的是文本中的关键词,而不是比较索引中的值 | 可以有多个 | fulltext |
在InnoDB存储引擎中,根据索引的存储形式,又可以分为以下两种:
分类 | 含义 | 特点 |
---|---|---|
聚集索引(Clustered Index) | 将数据存储与索引放到了一块,索引结构的叶子结点保存了行数据 | 必须有,而且只有一个 |
二级索引(Secondary Index) | 将数据与索引分开存储,索引结构的叶子节点关联的是对应的主键 | 可以存在多个 |
聚集索引选取规则:
- 如果存在主键,主键索引就是聚集索引
- 如果不存在主键,将使用的第一个唯一(UNIQUE)索引作为聚集索引
- 如果表没有主键,或没有合适的唯一索引,则InnoDB会自动生成一个rowid作为隐藏的聚集索引
InnoDB主键索引的B+tree高度为多高?
假设:一行数据大小为1k,一页中可以存储16行这样的数据,InnoDB的指针占用6个字节的空间,主键即使为bigint,占用字节数为8
高度为2:
n8 + (n+1)6 = 16*1024,算出n约为1170
1171*16=18736
高度为3:
1171*1171*16=21939856
索引语法:
-
创建索引
create [unique|fulltext] index index_name on table_name (index_col_name,...);
-
查看索引
show index from table_name;
-
删除索引
drop index index_name on table_name;
练习:
-
name字段为姓名字段,该字段的值可能会重复,为该字段创建索引
create index idx_user_name on tb_user(name); show index from tb_user;
-
phone手机号字段的值,是非空,且唯一的,为该字段创建唯一索引
create unique index idx_user_phone on tb_user(phone);
-
为profession,age,status创建联合索引
create index idx_user_pro_age_sta on tb_user(profession,age,status);
-
为email建立合适的索引来提升查询效率
create index idx_user_email on tb_user(email); drop index idx_user_email on tb_user;
SQL性能分析:
-
SQL执行频率
MySQL客户端连接成功后,通过show[session|global] status命令可以提供服务器状态信息,通过如下指令,可以查看当前数据库的INSERT、UPDATE、DELETE、SELECT的访问频次
show global status like 'Com_______'; -- 七个下划线代表模糊匹配七个字符
-
慢查询日志
慢查询日志记录了所有执行时间超过指定参数(long_query_time 单位:秒,默认10秒)的所有SQL语句的日志
MySQL的慢查询日志默认没有开启,需要早MySQL的配置文件(/etc/my.cnf)中配置如下信息:
# 查询是否开启慢查询 show variables like 'slow_query_log'; # 开启MySQL慢日志查询开关 show_query_log=1 # 设置慢日志的时间为2秒,SQL语句执行时间超过2秒,就会视为慢查询,记录慢查询日志 long_query_time = 2
配置完毕之后,通过一下指令重新启动MySQL服务器进行测试,查看慢日志文件中记录的信息/var/lib/mysql/localhost-slow.log
-
profile详情
show profiles能够在做SQL优化时帮助我们了解时间都耗费到哪里去了。通过have_profiling参数,能够看到当前MySQL是否支持profile操作:
select @@have_profiling;
默认profilinf是关闭的,可以通过set语句在session/global级别开启profiling;
set profiling = 1; select @@profiling;
#查看每一条SQL的耗时基本情况 show profiles; #查看query_id的SQL语句各个阶段的耗时情况 show profile for query query_id; #查看指定queri_id的SQL语句CPU的使用情况 show profile cpu for query query_id;
-
explain执行计划
explain或者desc命令获取mysql如何执行select语句的信息,包括在select语句执行过程中表如何连接和连接的顺序
语法:
#直接在select语句之前加上关键字explain/desc explain select 字段列表 from 表名 where 条件;
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的值越大越好
-
索引使用:
-
验证索引效率
未建立索引之前,执行如下SQL语句,查看SQL的耗时
select * form tb_sku where sn='12345605468468';
针对字段创建索引
create index idx_sku_sn on tb_sku(sn);
然后再次执行相同的SQL语句,再吃查看SQL的耗时
select * from tb_sku where sn='12345605468468';
-
最左前缀法则
如果索引了多列(联合索引),要遵守最左前缀法则,最左前缀法则指的是查询从索引的最左列开始,并且不跳过索引中的列。如果跳跃某一列,索引将部分失效(后面的字段索引失效)
explain select * from tb_user where profession='软件工程' and age = 31 and status = '0'; explain select * from tb_user where profession='软件工程' and age = 31 ;
-
范围查询
联合索引中,出现范围查询(>,<),范围查询右侧的列索引失效
explain select * from tb_user where profession='软件工程' and age>30 and status='0'; explain select * from tb_user where profession='软件工程' and age>=30 and status='0';
-
索引列运算
不要在索引列上进行运算操作,索引将失效
explain select * from tb_user where substring(phone,10,2) = '15';
-
字符串不加引号
字符串类型字段使用时,不加引号,索引将失效
explain select * from tb_user where profession='软件工程' and age = 31 and status = 0; explain select * from tb_user where phone = 17799990015;
-
模糊查询
如果仅仅是尾部模糊匹配,索引不会失效,如果是头部模糊匹配,索引失效
explain select * from tb_user where profession like '软件%'; explain select * from tb_user where profession like '%工程'; explain select * from tb_user where profession like '%工%';
-
or连接的条件
用or分割开的条件,如果or前的条件中的列有索引,而后面的列中没有索引,那么涉及的索引偶读不会被用到
explain select * from tb_user where id=10 or age=23; explain select * from tb_user where phone='1235789654654' or age=23;
-
数据分布影响
如果MySQL评估使用索引比全表更慢,则不使用索引
select * from tb_user where phone >= '17565498796';
-
SQL提示
SQL提示,是优化数据库的一个重要手段,简单来说,就是在SQL语句中加入一些认为的提示来达到优化操作的目的
-
use index:
explain select * from tb_user use index(idx_user_pro) where profession='软件工程';
-
ignore index:
explain select * from tb_user ignore index(idx_user_pro) where profession='软件工程';
-
force index:
explain select * from tb_user force index(idx_user_pro) where profession='软件工程';
-
-
覆盖索引
尽量使用覆盖索引(查询使用了索引,并且需要返回的列,在该索引中已经全部能够找到),减少select *
小贴士:
- using index condition:查找使用了索引,但是需要回表查询数据
- using where;using index:查找使用了索引,但是需要的数据都在索引列中能找到,所以不需要回表查询
-
前缀索引:
当字段类型为字符串(varchar,text等)时,有时候需要索引很长的字符串,这会让索引变的很大,查询时浪费大量的磁盘IO,影响查询效率。此时可以只将字符串的一部分前缀,建立索引,这样可以大大节约索引空间,从而提高索引效率
-
语法:
create index idx_xxxx on table_name(column(n));
-
前缀长度
可以根据索引的选择性来决定,而选择性是指不重复的索引值(基数)和数据表的记录总数的比值,索引选择性越高则查询效率越高,唯一索引的选择性是1,这是最好的索引选择性,性能也是最好的
select count(distinct email)/count(*) from tb_user; select count(distinct substring(email,1,5)/count(*) from tb_user;
-
-
单列索引与联合索引
单列索引:即一个索引只包含单个列
联合索引:即一个索引包含了多个列
在业务场景中,如果存在多个查询条件,考虑针对于查询字段建立索引时,建议建立联合索引,而非单列索引
单列索引情况:
explain select id,phone,name from tb_user where phone = '17799990010' and name = '韩信';
多条件联合查询时,MySQL优化器会评估哪个字段的索引效率更高,会选择该索引完成本次查询
联合索引情况:
索引设计原则:
- 针对于数据量较大,且查询比较频繁的表建立索引
- 针对于常作为查询条件 (where)、排序 (order by)、分组 (group by) 操作的字段建立索引
- 尽量选择区分度高的列作为索引,尽量建立唯一索引,区分度越高,使用索引的效率越高
- 如果是字符串类型的字段,字段的长度较长,可以针对于字段的特点,建立前缀索引
- 尽量使用联合索引,减少单列索引,查询时,联合索引很多时候可以覆盖索引,节省存储空间,避免回表,提高查询效率
- 控制索引的数量,索引并不是多多益善,索引越多,维护索引结构的代价也就越大,会影响增删改的效率
- 如果索引列不能存储NULL值,请在创建表时使用NOT NULL约束它。当优化器知道每列是否包含NULL值时,它可以更好地确定哪个索引最有效地用于查询
4、SQL优化:
插入数据:
-
批量插入
Insert into tb test values(1,'Tom'),(2,'Cat'),(3,erry');
-
手动提交事务
start transaction; insert into tb test values(1,'Tom'),(2,'Cat'),(3,'erry'); insert into tb test values(4,'Tom'),(5,'Cat'),(6,'Jerry'); insert into tb test values(7,'Tom'),(8,'Cat'),(9,'Jerry'); commit;
-
主键顺序插入
主键顺序插入性能高于乱序插入
-- 主键乱序插入:8 1 9 21 88 2 4 15 89 5 7 3 -- 主键顺序插入:1 2 3 4 5 7 8 9 15 21 88 89
-
大批量插入数据
如果一次性需要插入大批量数据,使用insert语句插入性能较低,此时可以使用MySQL数据库提供的load指令进行插入,操作如下
#客户端连接上服务端时,加上参数 --local-infile mysql --local-infile -u root -p #设置全局参数local_infile为1,开启从本地加载文件导入数据的开关 set global local_infile = 1; #执行load指令将准备好的数据,加载到表结构中 load data local infile '/root/sql1.log'into table 'tb_user' fields terminated by ',' lines terminated by '\n';
主键优化:
-
数据组织方式
在InnoDB存储引擎中,表数据都是根据主键顺序组织存放的,这种存储方式的表称为索引组织表(index organized table IOT)
-
页分裂
页可以为空,也可以填充一半,也可以填充100%。每个页包含了2-N行数据(如果一行数据过大,会行溢出),根据主键排列.
主键顺序插入、主键乱序插入
-
页合并
当删除一行记录时,实际上记录并没有被物理删除,只是记录被标记(flaged)为删除并且它的空间变得允许被其他记录声明使用。
当页中删除的记录达到 MERGE THRESHOLD(默认为页的50%),nnoDB会开始寻找最靠近的页(前或后)看看是否可以将两个页合并以优化空间使用。
补充:MERGE THRESHOLD: 合并页的闻值,可以自己设置,在创建表或者创建索引时指定
主键设计原则:
- 满足业务需求的情况下,尽量降低主键的长度
- 插入数据时,尽量选择顺序插入,选择使用AUTO_INCREMENT自增主键
- 尽量不要使用UUID做主键或者是其他自然主键,如身份证号
- 业务操作时,避免对主键的修改
order by优化:
-
Using filesort:通过表的索引或全表扫码,读取满足条件的数据行,然后再排序缓冲区sort buffer中完成排序操作,所有不是通过索引直接返回排序结果的排序都叫FileSort排序
-
Using index:通过有序索引顺序扫码直接返回有序数据,这种情况即为using index,不需要额外排序,操作效率高
-
根据排序字段建立合适的索引,多字段排序时,也遵循最左前缀法则
-
尽量使用覆盖索引
-
多字段排序,一个升序一个降序,此时需要注意联合索引在创建时的规则 (ASC/DESC)
-
如果不可避免的出现filesot,大数据量排序时,可以适当增大排序缓冲区大小 sot buffer size(默认256k)
group by优化:
- 在分组操作时,可以通过索引来提高效率
- 分组操作时,索引的使用也是满足最左前缀法则的
limit 优化:
一个常见又非常头疼的问题就是limit 2000000,10,此时需要MySQL排序前2000010记录,仅仅返回2000000-2000010的记录,其他记录丢弃,查询排序的代价非常大
优化思路:一般分页查询时,通过创建覆盖索引能够比较好的提高性能,可以通过覆盖索引加子查询的形式进行优化
explain select * from tb_sku t,(select id from tb_sku order by limit 2000000,10) a where t.id=a.id
count 优化:
explain select count(*) from tb_user;
- MyISAM引擎把一个表的总行数存在了磁盘上,因此执行count(*)的时候会直接返回这个数,效率很高
- InnoDB引擎就麻烦了,它执行count(*)的时候,需要把数据一行一行地从引擎里面读出来,然后累积计数
优化思路:自己计数
-
count的几种用法
-
count()是一个聚合函数,对于返回的结果集,一行行地进行判断,如果count函数的参数不是NULL,累计值就加1,否则不加,最后返回累计值
-
用法:count(*)、count(主键)、count(字段)、count(1)
-
count(主键)
lnnoDB 引擎会遍历整张表,把每一行的 主键id 值都取出来,返回给服务层。服务层拿到主键后,直接按行进行累加(主键不可能为null)
-
count (字段)
没有not null 约束:InnoDB 引会遍历整张表把每一行的字段值都取出来,返回给服务层,服务层判断是否为null,不为null,计数累加
有not nul 约束:InnoDB引警会遍历整张表把每一行的字段值都取出来,返回给服务层,直接按行进行累加
-
count (1)
lnnoDB引擎遍历整张表,但不取值。服务层对于返回的每一行,放一个数字“1”进去,直接按行进行累加
-
count (*)
InnoDB引擎并不会把全部字段取出来,而是专门做了优化,不取值,服务层直接按行进按累加
按照效率排序的话,count(字段)<count(主键 id)<count(1)≈count(*),所以尽量使用count(*)
-
update 优化:
update student set no='2000100100' where id=1;
update student set no='2000100105' where name='韦一笑';
InnoDB的行锁是针对索引加的锁,不是针对记录加的锁,并且该索引不能失效,否则会从行锁升级为表锁
5、视图:
- 介绍:
视图(View)是一种虚拟存在的表。视图中的数据并不在数据库中实际存在,行和列数据来自定义视图的查询中使用的表,并且是在使用视图时动态生成的。
通俗的讲,视图只保存了查询的SL逻辑,不保存查询结果。所以我们在创建视图的时候,主要的工作就落在创建这条SOL查询语句上
-
创建:
creare [or replace view 视图名称[(列明列表)] as select语句 [with [casecaded | local] check option]
-
查询:
#查看创建视图语句:show create view 视图名称; #查看视图数据:select * from 视图名称......;
-
修改:
#方式一:create [or replace] view 视图名称[(列名列表)] as select语句 [with | cascaded | local] check option] #方式二:alter view 视图名称[(列名列表)] as select语句 [with [cascaded | local] check option]
-
删除:
drop view [if exists] 视图名称 [,视图名称]...
-
视图的检查选项
当使用WITH CHECK OPTION子句创建视图时,MySQL会通过视图检查正在更改的每个行,例如 插入,更新,删除,以使其符合视图的定义。MySQL允许基于另一个视图创建视图,它还会检查依赖视图中的规则以保持一致性。为了确定检查的范围,mysql提供了两个选项:CASCADED 和 LOCAL,默认值为 CASCADED
cascaded:
create view v1 as select id,name from student where id <= 20 with cascaded check option; create view v2 as select id,name from v1 where id >= 10 with cascaded check option; create view v3 as select id,name from v2 where id <= 15;
cascaded它会对所有它引用的父辈、祖父辈视图做级联检查
-
视图的更新
要使视图可更新,视图中的行与基础表中的行之间必须存在一对一的关系,如果视图包含一下任何一项,则该视图不可更新
- 聚合函数或窗口函数(SUM()、MIN()、MAX()、COUNT()等)
- DISTINCT
- GROUP BY
- HAVING
- UNION或者UNION ALL
-
作用
-
简单
视图不仅可以简化用户对数据的理解,也可以简化他们的操作。那些被经常使用的查询可以被定义为视图,从而使得用户不必为以后的操作每次指定全部的条件
-
安全
数据库可以授权,但不能授权到数据库特定行和特定的列上。通过视图用户只能查询和修改他们所能见到的数据
-
数据独立
视图可帮助用户屏蔽真实表结构变化带来的影响
案例:
-- 1.为了保证数据库表的安全性,开发人员在操作tb_user表时,只能看到用户的基本字段,屏蔽手机号和邮箱两个字段 create view tb_user_view as select id,name,profession,age,gender,status,createtime from tb_user; select * from tb_user_view; -- 2.查询每个学生所选修的课程(三张表联查),这个功能在很多业务中都有使用,为了简化操作,定义一个视图 select s.name, s.no, c.name from student s, student_course sc,course c where s.id=sc.studentid and sc.courseid=c.id; create view tb_stu_course_view as select s.name student_name, s.no student_no, c.name course_name from student s, student_course sc,course c where s.id=sc.studentid and sc.courseid=c.id;
-
6、存储过程
存储过程是事先经过编译并存储在数据库中的一段 SOL语句的集合,调用存储过程可以简化应用开发人员的很多工作,减少数据在数据库和应用服务器之间的传输,对于提高数据处理的效率是有好处的存储过程思想上很简单,就是数据库 SQL 语言层面的代码封装与重用。
特点:
- 封装,复用
- 可以接收参数,也可以返回数据
- 减少网络交互,效率提升
语法:
创建
create procedure 存储过程名称[参数列表]
begin
--SQL语句
end;
调用
call 名称([参数]);
查看
select * from infomation_schema.routines where routine_schema='xxx'; -- 查询指定数据库的存储过程及状态信息
show create procedure 存储过程名称; --查询某个存储过程的定义
删除
drop procedure [if exists] 存储过程名称;
注意:在命令行中,执行创建存储过程的SQL时,需要通过关键字delimiter指定SQL语句的结束符
delimiter $$
create procedure p1()
begin
select count(*) from student;
end$$
变量:
系统变量是MySQL服务器提供,不是用户定义的,属于服务器层面。分为全局变量(GLOBAL)、会话变量(SESSION)
-
查看系统变量
show [session|global] varibles; -- 查看所有系统变量 show [session|global] varibles like '.....' -- 可以通过Like模糊匹配方式查找变量 select @@[session|global] 系统变量名; -- 查看指定变量的值
-
设置系统变量
set [session|global] 系统变量名 = 值; set @@[session|gloal] 系统变量名 = 值;
注意:
如果没有指定session/global,默认是session,会话变量
mysql服务重新启动之后,所设置的全局参数会失效,要想不失效,可以在/etc/my.cnf中配置
用户自定义变量是用户根据需要自己定义的变量,用户变量不用提前声明,在用的时候直接用"@变量名"使用就可以,其作用域为当前连接
-
赋值
set @var_name = expr [,@var_name = expr]...; set @var_name := expr [,@var_name := expr]...; select @var_name := expr [,@var_name := expr]...; select 字段名 into @var_name from 表名;
-
使用
select @var_name;
注意:用户自定义的变量无需对其进行声明或初始化,只不过获取到的值为NULL
-- 变量:用户变量
-- 赋值
set @myname = 'itcast';
set @myage := 10;
set @mygender := '男',@mybody := 'java';
select @mycolor := 'red';
select count(*) into @mycount from tb_user;
-- 使用
select @myname,@myage,@mygender,@myhobby;
select @mycolor,@mycount;
局部变量是根据需要定义的在局部生效的变量,访问之前,需要declare声明,可用作存储过程内的局部变量和输入参数,局部变量的范围是在其内声明的begin...end块
声明
delcare 变量名 变量类型[default...];
赋值
set 变量名 = 值;
set 变量名 := 值;
select 字段名 into 变量名 from 表名...;
-- 变量:局部变量
-- 声明 - declare
-- 赋值 -
create procedure p2()
begin
declare stu_count int default 0;
select count(*) into stu_count from student;
select stu_count;
end;
call p2();
if:
语法:
if 条件1 then
...
elseif 条件2 then -- 可选
...
else -- 可选
...
end if;
案例:
根据定义的分数的score变量,判定当前分数对应的分数等级
- score >= 85分,等级为优秀
- score >= 60分 且 score < 85分,等级为及格
- score < 60分,等级为不及格
create procedure p3()
begin
declare score int default 58;
declare result varchar(10);
if score >= 85 then
set result := '优秀';
elseif score >= 60 then
set result := '及格';
else
set result := '不及格';
end if;
select result;
end;
call p3();
参数:
类型 | 含义 | 备注 |
---|---|---|
in | 该类参数作为输入,也就是需要调用时传入值 | 默认 |
out | 该类参数作为输出,该参数可以作为返回值 | |
inout | 既可以作为输入参数,也可以作为输出参数 |
用法:
create procedure 存储过程名称 ([in/out/inout 参数名 参数类型])
begin
--SQL语句
end;
案例:
-
根据传入的参数score,判定当前分数对应的分数等级,并返回
-
score >= 85分,等级为优秀
-
score >= 60分 且 score < 85分,等级为及格
-
score < 60分,等级为不及格
create procedure p5(in score int, out result varchar(10)) begin if score >= 85 then set result := '优秀'; elseif score >= 60 then set result := '及格'; else set result := '不及格'; end if; end; call p5(68,@result); select @result;
-
-
将传入的200分制的分数,进行换算,换算成百分之,然后返回 --> inout
create procedure p7(inout score double) begin set score := score*0.5; end; set @score = 78; call p7(@score); select @score;
case:
语法一:
case case_value
when when_value1 then statement_list1
[when when_value2 then statement_list2]...
[else statement_list]
end case;
语法二:
case
when search_condition1 then statement_list1
[when search_condition2 then statement_list2]...
[else statement_list]
end case;
案例:
根据传入的月份,判定月份所属的季节(要求采用case结构)
1-3月份为第一季度
4-6月份为第二季度
7-9月份为第三季度
10-12月份为第四季度
create procedure p6(int month int)
begin
declare result varchar(10);
case
when month >= 1 and month <= 3 then
set result := '第一季度';
when month >= 4 and month <= 6 then
set result := '第二季度';
when month >= 7 and month <= 9 then
set result := '第三季度';
when month >= 10 and month <= 12 then
set result := '第四季度';
else
set result := '非法参数'
end case;
select concat('您输入的月份为:',month,',所属的季度为:',result);
end;
call p6(4);
while:
while循环是有条件的循环控制语句,满足条件后,在执行循环体中的SQL语句,具体语法为:
#先判定条件,如果条件为true,则执行逻辑,否则,不执行逻辑
while 条件 do
SQL逻辑...
end while;
案例:
计算从1累加到n的值,n为传入的参数值
create procedure p9(in n int)
begin
declare total int default 0;
while n>0 do
set total := total + n;
set n := n-1;
end while;
select total;
end;
call p9(100);
repeat:
repeat是有条件的循环控制语句,当满足条件的时候退出循环(有点像do while),具体语法为:
#先执行一次逻辑,然后判定逻辑是否满足,如果满足,则退出,如果不满足,则继续下一次循环
repeat
SQL逻辑...
until条件
end repeat;
案例:
计算从1累加到n的值,n为传入的参数值
create procedure p9(in n int)
begin
declare total int default 0;
repeat
set total := total + n;
set n := n-1;
until n<= 0
select total;
end;
call p9(100);
loop:
实现简单的循环,如果不在SQL逻辑中增加退出循环的条件,可以用其来实现简单的死循环。LOOP可以配合一下两个语句使用:
- leave:配合循环使用,退出循环
- ITERATE:必须用在循环中,作用是跳出当前循环剩下的语句,直接进入下一次循环
[begin_label:]loop
SQL逻辑...
end loop [end_label];
leave label; -- 退出指定标记的循环体
iterate label; -- 直接进入下一次循环
案例:
-- 1.计算从1累加到n的值,n为传入的参数值
create procedure p11(in n int)
begin
declare total int default 0;
sum:loop
if n<=0 then
leave sum;
end if;
set total := total + n;
set n := n-1;
end loop sum;
select total;
end;
call p11(10);
drop procedure p11;
-- 2.计算从1到n之间的偶数累加值,n为传入的参数值
create procedure p13(in n int)
begin
declare total int default 0;
sum:loop
if n<=0 then
leave sum;
end if;
if n%2 = 1 then
set n := n-1;
iterate sum;
end if;
set total := total + n;
set n := n-1;
end loop sum;
select total;
end;
call p13(100);
游标:
游标(cursor)是用来存储查询结果集的数据类型,在存储过程和函数中可以使用游标对结果集进行循环的处理,游标的使用包括游标的声明OPEN、FETCH和CLOSE,其语法分别如下
声明游标:
declare 游标名称 cursor for 查询语句;
打开游标:
open 游标名称;
获取游标记录:
fetch 游标名称 into 变量[,变量];
关闭游标:
close 游标名称;
条件处理程序:
条件处理程序(handler)可以用来定义在流程控制结构执行中遇到的问题时相应的处理步骤,具体语法为:
declare handler_action handler for condition_value [,condition_value]... statement;
handler_action
continue:继续执行当前程序
exit:终止执行当前程序
condition_value
sqlstate sqlstate_value:状态码,如02000
sqlwarning:所有以01开头的sqlstate代码的简写
not found:所有以02开头的sqlstate代码的简写
sqlexception:所有没有被sqlwarning或not found捕获的sqlstate代码的简写
案例:
-- 根据传入的参数uage,来查询用户表tb_user中,所有用户年龄小于等于uage的用户名(name)和专业(profession)
-- 将用户的明星和专业插入到所创建的一张新表(id,name,profession)中
-- 逻辑
-- A.声明游标,存储查询结果集
-- B.准备:创建表结构
-- C.开启游标
-- D.获取游标中的记录
-- E.插入数据到新表中
-- F.关闭游标
create procedure p14(in uage int)
begin
declare uname varchar(100);
declare upro varchar(100);
declare u_cursor cursor for select name,profession from tb_user where age <= uage;
declare exit handler for sqlstate '02000' close u_cursor; -- 声明条件处理程序,状态码02000时触发
-- declare exit handler for not found close u_cursor; -- 声明条件处理程序,状态码02000时触发
drop table if exists tb_user_pro;
create table if not exists tb_user_pro(
id int primary key auto_increment,
name varchar(100),
profession varchar(100)
);
open u_cursor;
while true do
fetch u_cursor into uname,upro;
insert into tb_user_pro values (null,uname,upro);
end while;
close u_cursor;
end;
call p14(40);
7、存储函数:
存储函数是有返回值的存储过程,存储函数的参数只能是IN类型的,具体语法如下:
create function 存储函数名称([参数列表])
returns type [characteristic...]
begin
--SQL语句
return...;
end;
characteristic说明:
deterministic:相同的输入参数总是产生相同的结果
no sql:不包含sql语句
reads sql data:包含读取数据的语句,但不包含写入数据的语句
-- 存储函数
-- 从1到n的累加
create function fun1(n int)
returns int deterministic
begin
declare total int default 0;
while n>0 do
set total := total + n;
set n := n - 1;
end while;
return total;
end;
用的不多,因为存储函数可以用存储过程替代
8、触发器:
触发器是与表有关的数据库对象,指在 insert/update/delete 之前或之后,触发并执行触发器中定义的SQL语句集合。触发器的这种特性可以协助应用在数据库端确保数据的完整性,日志记录,数据校验等操作
使用别名 OLD和 NEW 来引用触发器中发生变化的记录内容,这与其他的数据库是相似的。现在触发器还只支持行级触发,不支持语句级触发。
触发器类型 | new和old |
---|---|
insert型触发器 | new表示将要或者已经新增的数据 |
update型触发器 | old表示修改之前的数据,new表示将要或已经修改后的数据 |
delete型触发器 | old表示将要或者已经删除的数据 |
语法:
-
创建
create trigger trigger_name defore/after insert/update/delete on tbl_name for each row -- 行级触发器 begin trigger_stmt; end;
-
查看
show triggers;
-
删除
drop trigger [schema_name.]trigger_name; -- 如果没有指定schema_name,默认为当前数据库
案例:
通过触发器记录tb_user表的数据变更日志,将变更日志插入到日志表user_logs中,包含增加,修改,删除
create table user_logs(
id int(11) not null auto_increment,
operation varchar(20) not null comment '操作类型,insert/update/delete',
operate_time datetime not null comment '操作时间',
operate_id int(11) not null comment '操作的ID',
operate_params varchar(500) comment '操作参数',
primary key(`id`)
)engine=innodb default charset=utf8;
案例:
create table user_logs(
id int(11) not null auto_increment,
operation varchar(20) not null comment '操作类型,insert/update/delete',
operate_time datetime not null comment '操作时间',
operate_id int(11) not null comment '操作的ID',
operate_params varchar(500) comment '操作参数',
primary key(`id`)
)engine=innodb default charset=utf8;
-- 插入数据触发器
create trigger tb_user_insert_trigger
after insert on tb_user for each row
begin
insert into user_logs(id, operation, operate_time,operate_id,operate_params) values
(null,'insert',now(),new.id,concat('插入的数据内容为:id=',new.id,',name=',new.name,',phone=',new.phone,',email=',new.email,',profession=',new.profession));
end;
show triggers;
-- 修改数据触发器
create trigger tb_user_update_trigger
after update on tb_user for each row
begin
insert into user_logs(id, operation, operate_time,operate_id,operate_params) values
(null,'update',now(),new.id,
concat('更新之前的数据:id=',old.id,',name=',old.name,',phone=',old.phone,',email=',old.email,',profession=',old.profession,
'| 更新之后的数据:id=',new.id,',name=',new.name,',phone=',new.phone,',email=',new.email,',profession=',new.profession));
end;
-- 删除数据触发器
create trigger tb_user_update_trigger
after delete on tb_user for each row
begin
insert into user_logs(id, operation, operate_time,operate_id,operate_params) values
(null,'delete',now(),old.id,
concat('删除之前的数据:id=',old.id,',name=',old.name,',phone=',old.phone,',email=',old.email,',profession=',old.profession));
end;
小结:
-
视图
-
虚拟存在的表,不保存查询结果 ,只保存查询的SQL逻辑
-
简单、安全、数据独立
-
-
存储过程
- 事先定义并存储在数据库中的一段SQL语句的集合
- 减少网络交互,提高性能、封装重用
- 变量、if、case、参数(in/out/input)、while、repeat、loop、cursor、handler
-
存储函数
- 存储函数是有返回值的存储过程,参数类型只能为IN类型
- 存储函数可以被存储过程替代
-
触发器
- 可以在表数据进行INSERT、UPDATE、DELETE之前或之后触发
- 保证数据完整性、日志记录、数据校验
9、锁:
锁是计算机协调多个进程或线程并发访问某一资源的机制。在数据库中,除传统的计算资源(CPU、RAM、1/0)的争用以外,数据也是一种供许多用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题,锁冲突也是影响数据库并发访问性能的一个重要因素。从这个角度来说,锁对数据库而言显得尤其重要,也更加复杂。
分类:
MYSQL中的锁,按照锁的粒度分,分为以下三类:
- 全局锁:锁定数据库中的所有表
- 表级锁:每次操作锁住整张表
- 行级锁:每次操作锁住对应的行数据
全局锁:
全局锁就是对整个数据库实例加锁,加锁后整个实例就处于只读状态,后续的DML的写语句,DDL语句,已经更新操作的事务提交语句都将被阻塞
其典型的使用场景是做全库的逻辑备份,对所有的表进行锁定,从而获取一致性视图,保证数据的完整性
演示:
flush tables with read lock; -- 加锁
mysqldump -uroot -p1234 itcast > itcast.sql -- 备份
-- 连接远程需要输入以下命令,-h后面写上远程地址
mysqldump -h 192.168.200.202 -uroot -p1234 itcast > itcast.sql -- 备份
unlock tables; -- 解锁
特点:
数据库中加全局锁, 是一个比较重的操作,存在以下问题:
- 如果在主库上备份,那么在备份期间都不能执行更新,业务基本上就得停摆
- 如果在从库上备份,那么在备份期间从库不能执行主库同步过来的二进制日志(binlog),会导致主从延迟
在InnoDB引擎中,我们可以在备份时加上参数 --single-transaction参数来完成不加锁的一致性数据备份
mysqldump --single-transaction -uroot -p123456 itcast -> itcast.sql
表级锁:
每次操作锁住整张表,锁定粒度大,发生锁冲突的概率最高,并发度最低。应用在MyISAM、InnoDB、BDB等存储引擎中
对于表级锁,主要分为以下三类:
-
表锁
-
表共享读锁(read lock)
-
表独占写锁(write lock)
语法:
- 加锁:lock tables 表名 ... read/write
- 释放锁:unlock tables / 客户端断开连接
读锁不会阻塞其他客户端的读,但是会堵塞写。写锁既会阻塞其他客户端的读,又会阻塞其他客户端的写
-
-
元数据锁(meta data lock,DML)
MDL加锁过程是系统自动控制,无需显式使用,在访问一张表的时候会自动加上。MDL锁的主要作用是维护表元数据的数据一致性,在表上有活动事务的时候,不可以对元数据进行写入操作。为了避免DML和DDL冲突,保证读写的正确性
在MySQL中引入MDL,当对一张表进行增删改查的时候,加DML读锁(共享);当对表结构进行变更操作的时候,加DML写锁(排他)
查看元数据锁:
select object_type,object_schema,object_name,lock_type,lock_duration from performance_schema.metadata_locks;
-
意向锁
为了避免DML在执行时,加的行锁与表锁的冲突,在InnoDB中引入了意向锁,使得表锁不用检查每行数据是否加锁,使用意向锁来减少表锁的检查
-
意向共享锁(IS):由语句select ... lock in share mode添加
与表锁共享锁(read)兼容,与表锁排它锁(write)互斥
-
意向排他锁(IX):由insert、update、delete、select ... for update添加
与表锁共享锁(read)及排它锁(write)都互斥,意向锁之间不会互斥
通过一下SQL,查看意向锁及行锁的加锁情况:
select object_schema,object_name,index_name,lock_type,lock_mode,lock_data from performance_schema.data_locks;
-
行级锁:
行级锁,每次操作锁住对应的行数据,锁定粒度最小,发生锁冲突的概率最低,并发度最高,应用在InnoDB存储引擎中
InnoDB的数据是基于索引组织的,行锁是通过对索引上的索引项加锁来实现的,而不是对记录加的锁。对于行级锁,主要分为以下三类:
- 行锁(Record Lock):锁定单个行记录的锁,防止其他事务对此进行update和delete。在RC、RR隔离级别下都支持
- 间隙锁(Gap Lock):锁定索引记录间隙(不含该记录),确保索引记录间隙不变,防止其他事务在这个间隙进行insert,产生幻读。在PR隔离级别下都支持
- 临键锁(Next-Key Lock):行锁和间隙锁组合,同时锁住数据,并锁住数据前面的Gap。在RR隔离级别下支持。
InnoDB实现了一下两种类型的行锁:
- 共享锁(S):允许一个事务去读一行,组织其他事务获得相同数据集的排它锁
- 排他锁(X):允许获取排他锁的事务更新数据,组织其他事务获得相同数据集的共享锁和排他锁
当前锁类型\请求锁类型 | S(共享锁) | X(排他锁) |
---|---|---|
S(共享锁) | 兼容 | 冲突 |
X(排他锁) | 冲突 | 冲突 |
SQL | 行锁类型 | 说明 |
---|---|---|
insert | 排他锁 | 自动加锁 |
update | 排他锁 | 自动加锁 |
delete | 排他锁 | 自动加锁 |
select(正常) | 不加任何锁 | |
select...lock in share mode | 共享锁 | 需要手动在select之后加lock in share mode |
select ... for update | 排他锁 | 需要手动在select之后加for update |
默认情况下,InnoDB在pereatable read事务隔离级别运行,InnoDB使用next-key锁进行搜索和索引扫描,以防止幻读
- 针对唯一索引进行检索时,对已经存在的记录进行等值匹配时,将会自动优化为行锁
- InnoDB的行锁是针对于索引加的锁,不通过索引条件检索数据,那么InnoDB将对表中的所有记录加锁,此时就会升级为表锁
通过以下SQL,查看意向锁及行锁的加锁情况
select object_schema,object_name,index_name,lock_type,lock_mode,lock_data from performance_schema.data_locks;
间隙锁/临键锁-演示:
默认情况下,InnoDB在 repeatable read事务隔离级别运行,InnoDB使用next-key锁进行搜索和索引扫描,以防止幻读
- 索引上的等值查询(唯一索引),给不存在的记录加锁时,优化为间隙锁
- 索引上的等值查询(普通索引),向右遍历时最后一个值不满足查询需求时,next-key lock退化为间隙锁
- 索引上的范围查询(唯一索引)--会访问到不满足条件的第一个值为止
注意:间隙锁唯一的目的是防止其他事务插入间隙,间隙锁可以共存,一个事务采用的间隙锁不会阻止另一个事务在同一间隙上采用间隙锁
间隙锁就是在间隙上加锁
小结:
- 概述
- 在并发访问时,解决数据访问的一致性,有效性问题
- 全局锁、表级锁、行级锁
- 全局锁
- 对整个数据库实例加锁,加锁后整个实例就处于只读状态
- 性能较差,数据逻辑备份时使用
- 表级锁
- 操作锁住整张表,锁定粒度大,发生锁冲突的概率高
- 表锁、元数据锁、意向锁
- 行级锁
- 操作锁住对应的行数据,锁定粒度最小,发生锁冲突的概率最低
- 行锁、间隙锁、临键锁
10、InnoDB引擎
逻辑存储结构:
tablespace(表空间) -> segment(段) -> extent(区) -> page(页) -> row(行)
-
表空间(idb文件):一个mysql实例可以对应多个表空间,用于存储记录、索引等数据
-
段:分为数据段(leaf node segment)、索引段(non-leaf node segment)、回滚段(rollback segment)、InnoDB是索引组织表,数据段就是B+树的叶子节点,索引段即为B+树的非叶子节点。段用来管理多个extent(区)
-
区:表的单元结构,每个区的大小为1M。默认情况下,InnoDB存储引擎页大小为16K,即一个区中一共有64个连续的页
-
页:InnoDB存储引擎磁盘管理的最小单元,每个页的大小默认为16KB。为了保证页的连续性,InnoDB存储引擎每次从磁盘申请4-5个区
-
行:InnoDB存储引擎数据是按行进行存放的
Trx_id:每次对某条记录进行改动时,都会把对应的事务id赋值给trx_id隐藏列
Roll_pointer:每次对某条引记录进行改动时,都会把旧的版本写入到undo日志中,然后这个隐藏列就相当于一个指针,可以通过它来找到该记录修改前的信息
架构:
mysql5.5版本开始,默认使用InnoDB存储引擎,它擅长事务处理,具有崩溃恢复特性,在日常的开发中使用非常广泛,下面是InnoDB架构图,左侧为内存结构,右侧为磁盘结构
内存架构:
磁盘结构:
后台线程:
内存数据如何存到磁盘空间
查看InnoDB引擎状态的指令:
show engine innodb status;
事务原理:
-
事务
事务 是一组操作的集合,它是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求,即这些操作要么同时成功,要么同时失败
-
特性
- 原子性:事务是不可分割的最小操作单元,要么全部成功,要么全部失败
- 一致性: 事务完成时,必须使所有的数据都保持一致状态
- 隔离性:数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行
- 持久性:事务一旦提交或回滚,它对数据库中的数据的改变就是永久的
redo log:
undo log:
MVCC:
-
当前读
读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。对于我们日常的操作,如select ... lock in share mode(共享锁),select ... for update、update、insert、delete(排他锁)都是一种当前读
-
快照读
简单的select(不加锁)就是快照读,快照读,读取的是记录数据的可见版本,有可能是历史数据,不加锁,是非阻塞读
- read committed:每次select,都生成一个快照读
- repeatable read:开启事务后第一个select语句才是快照读的地方
- serializable:快照读会退化为当前读
-
MVCC
全称Muti-Version Concurrency Control,多版本并发控制。指维护一个数据的多个版本,使得读写操作没有冲突,快照读为MySQL实现MVCC提供了一个非阻塞读功能。MVCC的具体实现,还需要以来数据库记录中的三个隐式字段undo log日志、readView
-
记录中的隐藏字段
idb2sdi xxxx.ibd -- 查看表空间文件
-
undo log
回滚日志,在insert、update、delete的时候产生的便于数据回滚的日志
当insert的时候,产生的undo log日志只在回滚时需要,在事务提交后,可被立即删除
而update、delete的时候,产生的undo log日志不仅在回滚时需要,在快照读时也需要,不会立即被删除
-
undo log版本链
不同事务或相同事务对同一条记录进行修改,会导致该记录的undolog生成一条记录版本链表,链表的头部是最新的旧记录,链表尾部是最早的旧记录
-
readview
readview(读视图)是快照读SQL执行时MVCC提取数据的依据,记录并维护系统当前活跃的事务(未提交的)id。
readview中包含了四个核心字段:
字段 含义 m_ids 当前活跃的事务ID集合 min_trx_id 最小活跃事务ID max_trx_id 预分配事务ID,当前最大事务ID+1(因为事务ID是自增的) creator_trx_id readView创建者的事务ID 不同的隔离级别,生成的readview的时机不同:
- read committed:在事务中每一次执行快照读时生成readview
- repeatable read:仅在事务中第一次执行快照读生成readview,后续复用该readview
在RC隔离级别下,在事务中每一次执行快照读时生成readview
RR隔离级别下,仅在事务中第一次执行快照读时生成readview,后续复用该readview
小结:
-
逻辑存储结构:
表空间、段、区、页、行
-
架构
内存结构、磁盘结构
-
事务原理
原子性 -undo log
持久性 -redo log
一致性 -undo log + redo log
隔离性 -锁+MVCC
-
MVCC
记录隐藏字段、undo log版本链、readview
11、MySQL管理
系统数据库:
mysql数据库安装完成之后,自带了四个数据库,具体作用如下:
数据库 | 含义 |
---|---|
mysql | 存储mysql服务器正常运行所需的各种信息(时区、主从、用户、权限等) |
information_schema | 提供了访问数据库元数据的各种表和视图,包含数据库、表、字段类型及访问权等 |
performance_schema | 为mysql服务器运行时状态提供了一个底层监控功能,主要用于收集数据库服务器性能参数 |
sys | 包含了一些列方便DBA和开发人员利用performance_schema性能数据库进行性能调优和诊断的视图 |
常用工具:
-
mysql
-
mysqladmin
-
mysqlbinlog
-
mysqlshow
-
mysqldump
-
mysqlimport/source