MySQL笔记_2_基础_2

MySQL笔记_2_基础_2


MySQL 由连接池、SQL 接口、解析器、优化器、缓存、存储引擎等组成

可以分为四层:连接层、服务层、引擎层、文件系统层

1.png
MySQL笔记_基础2(1.数据库架构)

连接层

  • 最上面,是一些客户端和连接服务,不是MySQL特有的,所有基于网络的C/S的网络应用程序都应该包括连接处理、认证、安全管理等。

服务层(MySQL 的核心)

  • 管理服务和工具
  • 连接池
  • SQL 接口(查询解析)
  • 分析
  • 优化
  • 缓存
  • 跨存储引擎的功能(存储过程、触发器、视图)

引擎层

  • 存储引擎层
    • 负责存取数据,服务器通过 API 和各种存储引擎进行交互
    • 不同的存储引擎具有不同的功能,我们可以根据实际需求选择使用对应的存储引擎

文件系统层

  • 数据存储层
    • 主要是将数据存储在运行设备的文件系统之上,并完成与存储引擎的交互

以一条 SQL SELECT 语句的执行轨迹来说明客户端与 MySQL 的交互过程

2.png
MySQL笔记_基础2(2.DQL 执行流程)
  1. 通过客户端/服务器通信协议与 MySQL 建立连接
  2. 查询缓存,这是 MySQL 的一个可优化查询的地方,如果开启了 Query Cache 且在查询缓存过程中查询到完全相同的 SQL 语句,则将查询结果直接返回给客户端;如果没有开启 Query Cache 或者没有查询到完全相同的 SQL 语句则会由解析器进行语法语义解析,并生成解析树。
  3. 预处理器生成新的解析树。
  4. 查询优化器生成执行计划。
  5. 查询执行引擎执行 SQL 语句,此时查询执行引擎会根据 SQL 语句中表的存储引擎类型,以及对应的 API 接口与底层存储引擎缓存或者物理文件的交互情况,得到查询结果,由 MySQL Server 过滤后将查询结果缓存并返回给客户端。若开启了 Query Cache ,这时也会将 SQL 语句和结果完整地保存到 QueryCache 中,以后若有相同的 SQL 语句执行则直接返回结果。

  • error log
    • 错误日志
  • bin log
    • 二进制日志
    • 关于备份,增量备份,DDL,DML,DCL操作的记录
  • Relay log
    • 中继日志
  • slow log
    • 慢查询日志
    • 用于调优,记录查询时间超过指定值的查询操作
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
-- 查看错误日志文件路径
show variables like 'log_error';
+---------------+---------------------+
| Variable_name | Value |
+---------------+---------------------+
| log_error | /var/log/mysqld.log |
+---------------+---------------------+

-- 慢查询日志文件路径
show variables like 'slow_query_log_file';
+---------------------+-----------------------------------+
| Variable_name | Value |
+---------------------+-----------------------------------+
| slow_query_log_file | /var/lib/mysql/localhost-slow.log |
+---------------------+-----------------------------------+

-- bin log 日志文件 需要在 my.cnf 中配置
log-bin=/var/log/mysql-bin/bin.log
server-id=2

-- 查看 relay log 相关参数
show variables like '%relay%';

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 客户端设置,即客户端默认的连接参数
[client]
# 默认连接端口
port = 3306
# 用于本地连接的socket套接字
socket = /data/mysqldata/3306/mysql.sock
#字符编码
default-character-set = utf8mb4

# 服务端基本设置
[mysqld]
# MySQL监听端口
port = 3306
# 为MySQL客户端程序和服务器之间的本地通讯指定一个套接字文件
socket = /data/mysqldata/3306/mysql.sock
# pid文件所在目录
pid-file = /data/mysqldata/3306/mysql.pid
# 使用该目录作为根目录(安装目录)
basedir = /usr/local/mysql-5.7.11
# 数据文件存放的目录
datadir = /data/mysqldata/3307/data
# MySQL存放临时文件的目录
tmpdir = /data/mysqldata/3307/tmp
# 服务端默认编码(数据库级别)
character_set_server = utf8mb4

1
2
3
4
5
6
7
-- 查看数据文件的位置
show variables like '%dir%';
+-----------------------------------------+----------------------------+
| Variable_name | Value |
+-----------------------------------------+----------------------------+
| datadir | /var/lib/mysql/ |
+-----------------------------------------+----------------------------+
  • .frm 文件
    • 每一个表都有一个以表名命名的 .frm 文件,与表相关的元数据(meta)信息都存放在此文件中,包括表结构的定义信息等。
  • .MYD 文件
    • myisam 存储引擎专用,存放 myisam 表的数据(data)
    • 每一个 myisam 表都会有一个 .MYD 文件与之呼应,同样存放在所属数据库的目录下
  • .MYI 文件
    • 也是 myisam 存储引擎专用,存放 myisam 表的索引相关信息
    • 每一个 myisam 表对应一个 .MYI 文件,其存放的位置和 .frm 及 .MYD 一样
  • .ibd 文件
    • 存放 innoDB 的数据文件(包括索引)。
  • db.opt 文件
    • 此文件在每一个自建的库里都会有,记录这个库的默认使用的字符集和校验规。


  • 完全备份
    • 将数据库的全部信息进行备份,包括数据库的数据文件、日志文件,还需要备份文件的存储位置以及数据库中的全部对象和相关信息。
  • 差异备份
    • 备份从最近的完全备份后对数据所做的修改,备份完全备份后变化了的数据文件、日志文件以及数据库中其他被修改的内容。
  • 增量备份
    • 增量备份是指在一次全备份或上一次增量备份后,以后每次的备份只需备份与前一次相比增加或者被修改的文件。
3.png
MySQL笔记_基础2(3.完全备份、差异备份、增量备份)
完全备份 差异备份 增量备份
备份方法 备份所有文件 一次全备份后
备份与全备份差异的部分
一次全备份后
备份与上次备份的差异部分
备份速度 最慢 较快 最快
恢复速度 最快 较快 最慢
存储空间占用 最多 较多 最少
优点 最快的恢复速度
只需要上一次完全备份就能恢复
相比增量,更快也更简单
恢复只需要最近一次的完全备份

最后一次的差异备份
备份速度快
较少的空间需求,没有重复的备份文件
缺点 最多的空间需求
大量重复的备份
较慢的备份速度
仍然会存在许多重复的备份文件
最慢的恢复速度
恢复需要最近一次完全备份

全部增量备份

  • 冷备份
    • 冷备份指的是当数据库进行备份时,数据库不能进行读写操作,即数据库要下线
    • 优点:
      • 是操作比较方便的备份方法(只需拷贝文件)
      • 低度维护,高度安全。
    • 缺点:
      • 在实施备份的全过程中,数据库不能作其它工作。
      • 若磁盘空间有限,只能拷贝到磁带等其它外部存储设备上,速度比较慢慢。
      • 不能按表或按用户恢复。

  • 热备份
    • 热备份是在数据库运行的情况下,备份数据库操作的 sql 语句,当数据库发生问题时,可以重新执行一遍备份的 sql 语句。
    • 优点:
      • 可在表空间或数据文件级备份,备份时间短。
      • 备份时数据库仍可使用。
      • 可达到秒级恢复(恢复到某一时间点上)。
    • 缺点:
      • 不能出错,否则后果严重。
      • 因难维护,所以要特别仔细小心

先确定数据文件所在目录

1
2
SHOW VARIABLES LIKE '%dir%';
-- 默认是在 datadir 的所在位置,即 /var/lib/mysql/

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# 停止 MySQL 服务
service mysqld stop

# 直接备份数据文件
cd /var/lib/ # 进入其上级目录
tar jcvf /root/backup.tar.bz2 mysql/ # 打包压缩到 root目录下


# 删除原目录,模拟灾难(数据丢失)
rm -rf /var/lib/mysql/


# 恢复数据
# 解压
tar jxvf backup.tar.bz2 mysql/
# 把备份的文件移动到/var/lib/里面去替代原来的mysql
mv /root/mysql/ /var/lib/

# 启动数据库服务,验证备份、恢复效果
service mysqld start

mysqldump 备份工具,是MySQL数据库用来备份和数据转移的一个工具,适用于数据量比较小的场景(几个G)。

热备份可以对多个库进行备份,也可以对单张表或者某几张表进行备份。

1
2
3
mkdir databackup
cd databackup
mysqldump -uroot -p mydatabase > mydatabase.sql

模拟数据丢失:

1
2
DROP DATABASE mydatabase;
CREATE DATABASE mydatabase CHARACTER SET 'utf8';

恢复数据:

1
2
cd databackup
mysql -uroot -p mydatabase < mydatabase.sql

也可以直接在 MySQL-Shell 中恢复

1
2
USE mydatabase;
source ~/databackup/mydatabase.sql;

1
2
3
4
5
# 备份并压缩:
mysqldump -uroot -p mydatabase | gzip > mydatabase.sql.gz

# 恢复数据:
gunzip < mydatabase.sql.gz | mysql -uroot -p mydatabase

1
mysqldump -uroot -p mydatabase table1 table2 > backupTable.sql

模拟数据丢失:

1
2
DROP TABLE table1;
DROP TABLE table2;

恢复数据:

1
mysql -uroot -p mydatabase < backupTable.sql

  • 备份
    1. 选中 “目标数据库”
    2. 右键,“备份/导出”
    3. “备份数据库……”
    4. 指定导出位置
  • 恢复
    1. 选中 “数据库节点”
    2. 右键,“执行SQL脚本”


​ MySql默认使用的都是 root 用户,超级管理员,拥有全部的权限。除了root用户以外,我们还可以通过DCL语言来定义一些权限较小的用户, 分配不同的权限来管理和维护数据库。

  • 创建用户

    • 1
      
      CREATE USER '用户名'@'主机名' IDENTIFIED BY '密码';
      
  • 用户授权

    • 1
      
      GRANT 权限 1, 权限 2... ON 数据库名.表名 TO '用户名'@'主机名';
      
  • 查看权限

    • 1
      
      SHOW GRANTS FOR '用户名'@'主机名';
      
  • 删除用户

    • 1
      
      DROP USER '用户名'@'主机名';
      
  • 查询用户

    • 1
      
      SELECT * FROM USER;
      

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
/*
	DCL 创建用户
	语法结构
		create user '用户名'@'主机名' identified by '密码';
*/

-- 创建 admin1 用户,只能在 localhost 这个服务器登录 mysql 服务器,密码为 123456
CREATE USER 'admin1'@'localhost' IDENTIFIED BY '123456';

-- 创建 admin2 用户可以在任何电脑上登录 mysql 服务器,密码为 123456
CREATE USER 'admin2'@'%'  IDENTIFIED BY '123456'; -- %表示在任意电脑都可以登录

/*
	用户的授权
	语法格式
		grant 权限1,权限2 ... on 数据库名.表 to '用户名'@'主机名'
*/
--  给 admin1 用户分配对 db4 数据库中 products 表的 操作权限:查询 
GRANT SELECT ON db4.`products` TO 'admin1'@'localhost';

-- 给 admin2 用户分配所有权限,对所有数据库的所有表
GRANT ALL ON *.* TO 'admin2'@'%';

-- 查询 商品表
SELECT * FROM products;

-- 插入商品数据
-- INSERT command denied to user 'admin1'@'localhost' for table 'products'
-- admin1用户只有查询权限
INSERT INTO products VALUES('p010','小鸟伏特加',3000,1,NULL);


/*
	查看用户权限
	语法格式
		show grants for '用户名'@'主机名';
*/

-- 查看root用户的权限
SHOW GRANTS FOR 'root'@'localhost';

-- 查看 admin1 用户的权限
SHOW GRANTS FOR 'admin1'@'localhost';

-- 删除用户
DROP USER 'admin1'@'localhost';

-- 查询用户
SELECT * FROM USER;


  • 在数据库表中,对字段建立索引可以大大提高查询速度。通过善用这些索引,可以令 MySQL 的查询和运行更加高效。
  • 拿汉语字典的目录页(索引)打比方,我们可以按拼音、笔画、偏旁部首等排序的目录(索引)快速查找到需要的字
  • MySql 将一个表的索引都保存在同一个索引文件中,如果对中数据进行增删改操作,MySql 都会自动的更新索引

MySql 将一个表的索引都保存在同一个索引文件中,如果对中数据进行增删改操作,MySql 都会自动的更新索引.

4.png
MySQL笔记_基础2(4.索引文件)

  • 参考主键约束

  • 主键是一种唯一性索引,每个表只能有一个主键,用于标识数据表中的每一条记录

  • 创建表的时候直接添加主键索引(最常用)

    • 1
      2
      3
      4
      
      CREATE TABLE 表名(
          -- 添加主键 (主键是唯一性索引,不能为null,不能重复,)
          字段名 类型 PRIMARY KEY,
      );
      
  • 修改表结构,添加主键索引

    • 1
      
      ALTER TABLE 表名 ADD PRIMARY KEY (列名)
      

1
2
3
4
5
6
7
8
9
CREATE DATABASE db4 CHARACTER SET utf8;

CREATE TABLE demo01(
    did INT,
    dname VARCHAR(20),
    hobby VARCHAR(30)
);

ALTER TABLE demo01 ADD PRIMARY KEY (did);

  • 参考唯一约束

  • 唯一索引指的是 索引列的所有值都只能出现一次, 必须唯一

  • 唯一索引可以保证数据记录的唯一性。事实上,在许多场合,人们创建唯一索引的目的往往不是为了提高访问速度,而只是为了避免数据出现重复。

  • 创建表的时候直接添加主键索引

    • 1
      2
      3
      4
      5
      
      CREATE TABLE 表名(
          列名 类型(长度),
          -- 添加唯一索引
          UNIQUE [索引名称] (列名)
      );
      
  • 使用 create 语句创建:在已有的表上创建索引

    • 1
      
      create unique index 索引名 on 表名(列名(长度))
      
  • 修改表结构添加索引

    • 1
      
      ALTER TABLE 表名 ADD UNIQUE ( 列名 )
      

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
-- 唯一索引的创建 
create unique index 索引名 on 表名(列名(长度))
-- 为demo01表的 hobby字段添加唯一索引
-- 唯一索引保证了数据的唯一性, 同时也提升了查询效率

CREATE UNIQUE INDEX ind_hobby ON demo01(hobby);
-- 添加唯一索引的列,列中每条记录的值都只能出现一次
INSERT INTO demo01 VALUES(1,'tom','篮球');
INSERT INTO demo01 VALUES(2,'jack','篮球');
-- 报错:Duplicate entry '篮球' for key 'ind_hobby'

  • 最常见的索引,作用就是加快对数据的访问速度

  • 普通索引(由关键字 KEY 或 INDEX 定义的索引)的唯一任务是加快对数据的访问速度。因此,应该只为那些最经常出现在查询条件(WHERE column = )或排序条件(ORDER BY column)中的数据列创建索引。

  • 使用 create index 语句创建:在已有的表上创建索引

    • 1
      
      create index 索引名 on 表名(列名[长度])
      
  • 修改表结构添加索引

    • 1
      
      ALTER TABLE 表名 ADD INDEX 索引名 (列名)
      

1
2
3
4
5
6
7
/*
	普通索引的创建
		1.create index 索引名 on 表名(列名[长度])
		2.ALTER TABLE 表名 ADD INDEX 索引名 (列名)
*/
-- 为 demo01表中的 dname字段添加普通索引
ALTER TABLE demo01 ADD INDEX ind_dname(dname);

  • 由于索引会占用一定的磁盘空间,因此,为了避免影响数据库的性能,应该及时删除不再使用的索引

    • 1
      
      ALTER TABLE table_name DROP INDEX index_name;
      

1
2
3
4
5
6
/*
	删除索引
		ALTER  TABLE 表名  DROP INDEX 索引名;
*/
-- 删除dname字段上的索引
ALTER TABLE demo01 DROP INDEX ind_dname;

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
-- 导入测试索引.sql 文件

-- test_index 表中有 500万条数据
SELECT COUNT(*) FROM test_index;

-- 通过id查询一条数据 2毫秒
SELECT * FROM test_index WHERE id = 100001;

-- 通过 dname字段查询 耗时2秒左右
SELECT * FROM test_index WHERE dname = 'name5200';

-- 执行分组查询 dname没有添加索引 36秒
SELECT * FROM test_index GROUP BY dname;

-- dname字段添加索引
ALTER TABLE test_index ADD INDEX dname_indx(dname);

-- 执行分组查询  5豪秒
SELECT * FROM test_index GROUP BY dname;

  • 添加索引首先应考虑在 where 及 order by 涉及的列上建立索引。
  • 索引的优点
    • 大大的提高查询速度
    • 可以显著的减少查询中分组和排序的时间。
    • 通过创建唯一索引保证数据的唯一性
  • 索引的缺点
    • 创建索引和维护索引需要时间,而且数据量越大时间越长
    • 当对表中的数据进行增加,修改,删除的时候,索引也要同时进行维护,降低了数据的维护速度
    • 索引文件需要占据磁盘空间

  • 视图是一种虚拟表。
  • 视图建立在已有表的基础上,视图赖以建立的这些表称为基表。
  • 向视图提供数据内容的语句为 SELECT 语句,可以将视图理解为存储起来的 SELECT 语句
  • 视图向用户提供基表数据的另一种表现形式

  • 权限控制时可以使用
    • 比如,某几个列可以运行用户查询,其他列不允许,可以开通视图查询特定的列,起到权限控制的作用
  • 简化复杂的多表查询
    • 视图本身就是一条查询 SQL ,我们可以将一次复杂的查询构建成一张视图,用户只要查询视图就可以获取想要得到的信息(不需要再编写复杂的 SQL )视图主要就是为了简化多表的查询
  • 可以把常用的查询,多次使用的子查询建立视图,以提高效率

  • 创建视图

    • 1
      2
      3
      4
      5
      
      create view 视图名 [column_list] as select语句;
      view: 表示视图
      -- column_list: 可选参数,表示属性清单,指定视图中各个属性的名称,默认情况下,与SELECT语句中查询的属性相同
      as : 表示视图要执行的操作
      select 语句: 向视图提供数据内容
      
    •  1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      
      -- 创建视图
      -- 1.查询所有商品和商品对应分类的信息
      SELECT * FROM products p LEFT JOIN category c ON p.`category_id` = c.`cid`;
      
      -- 2.根据上面的查询语句 构建一张视图
      CREATE VIEW products_category_view AS
      SELECT * FROM products p LEFT JOIN category c ON p.`category_id` = c.`cid`;
      
      -- 操作视图 就相当于操作一张 只读表
      SELECT * FROM products_category_view;
      
  • 查询视图

    • 当做一张只读的表操作就可以

    •  1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      
      -- 使用视图进行查询操作
      
      -- 查询各个分类下的商品平均价格
      /*
      	1.查询哪些表 分类表 商品表
      	2.查询条件是什么  分组操作
      	3.要查询的字段	平均价格,分类名
      	4.多表的连接条件 category_id = cid
      */
      
      -- 使用多表方式查询
      SELECT 
      	c.`cname`,
      	AVG(p.`price`)
      FROM products p LEFT JOIN category c ON p.`category_id` = c.`cid`
      GROUP BY c.`cname`;
      
      
      -- 通过视图查询
      SELECT 
      	pc.`cname`,
      	AVG(pc.`price`)
      FROM products_category_view pc GROUP BY pc.`cname`;
      
      
      -- 查询鞋服分类下最贵的商品的全部信息
      -- 多表查询
      -- 1.查询鞋服分类中 最高的商品价格
      SELECT 
      	MAX(p.`price`)
      FROM products p LEFT JOIN category c ON p.`category_id` = c.`cid` 
      WHERE c.`cname` = '鞋服';
      
      -- 2.进行子查询 将上面的查询结果作为条件
      SELECT 
      	*
      FROM products p LEFT JOIN category c 
      ON p.`category_id` = c.`cid`
      WHERE c.`cname` = '鞋服' AND p.`price` = 
      (
      	SELECT 
      		MAX(p.`price`)
      	FROM products p LEFT JOIN category c ON p.`category_id` = c.`cid` 
      	WHERE c.`cname` = '鞋服'
      );
      
      -- 通过视图查询
      SELECT 
      	*
      FROM products_category_view pc 
      WHERE pc.`cname` = '鞋服' AND pc.`price` = 
      (
      -- 子查询 求出鞋服分类下的最高价格
      SELECT MAX(pc.`price`) FROM products_category_view pc 
      WHERE pc.`cname` = '鞋服' ); 
      

  • 视图是建立在表的基础上,表存储数据库中的数据,而视图只是做一个数据的展示
  • 通过视图不能改变表中数据(一般情况下视图中的数据都是表中的列,经过计算得到的结果,不允许更新)
  • 删除视图,表不受影响,而删除表,视图不再起作用

  • MySQL 5.0 版本开始支持存储过程。
  • 存储过程(Stored Procedure)是一种在数据库中存储复杂程序,以便外部程序调用的一种数据库对象。存储过程是为了完成特定功能的 SQL 语句集,经编译创建并保存在数据库中,用户可通过指定存储过程的名字并给定参数(需要时)来调用执行。
  • 简单理解:存储过程其实就是一堆 SQL 语句的合并。中间加入了一些逻辑控制。

  • 优点:
    • 存储过程一旦调试完成后,就可以稳定运行(前提是,业务需求要相对稳定,没有变化)
    • 存储过程减少业务系统与数据库的交互,降低耦合,数据库交互更加快捷(应用服务器,与数据库服务器不在同一个地区)
  • 缺点:
    • MySQL 的存储过程与 Oracle 的相比较弱,所以较少使用,并且互联网行业需求变化较快也是原因之一
    • 尽量在简单的逻辑中使用,存储过程移植十分困难,数据库集群环境,保证各个库之间存储过程变更一致也十分困难。
    • 阿里的代码规范里,明确禁止使用存储过程,存储过程维护起来比较麻烦;

  • 创建方式1

    • 1
      2
      3
      4
      5
      6
      7
      8
      
      DELIMITER $$
      -- 声明语句结束符,可以自定义 一般使用$$CREATE PROCEDURE 过程名称()
      -- 声明存储过程
      BEGIN
      -- 开始编写存储过程
      -- 要执行的操作
      END $$
      -- 存储过程结束
      
  • 创建方式2

    • 1
      2
      3
      
      DELIMITER $$CREATE PROCEDURE 存储过程名称(IN 参数名 参数类型)BEGIN
      -- 要执行的操作
      END $$
      
  • 创建方式3

    • 1
      2
      3
      4
      5
      6
      
      DELIMITER $$
      CREATE PROCEDURE 存储过程名称(IN 参数名 参数类型, OUT 变量名 数据类型)
      BEGIN
          -- 要执行的操作
          SET @变量名=
      END $$
      
  • 调用

    • 1
      2
      
      CALL 存储过程名称;
      CALL 存储过程名称(参数值);
      

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
/*
	存储过程其实就是一堆 SQL 语句的合并。中间加入了一些逻辑控制。
	
	存储过程的优缺点
		优点
			1.调试完成就可以稳定运行 (在业务需求相对稳定情况)
			2.存储过程可以减少 业务系统与数据库的交互
		
		缺点
			1.互联网项目中 较少使用存储过程,因为 业务需求变化太快
			2.存储过程的移植十分困难.
			
*/
# 商品表
CREATE TABLE goods(
  gid INT,
  NAME VARCHAR(20),
  num INT  -- 库存
);

#订单表
CREATE TABLE orders(
  oid INT,
  gid INT,
  price INT -- 订单价格
);

# 向商品表中添加3条数据
INSERT INTO goods VALUES(1,'奶茶',20);
INSERT INTO goods VALUES(2,'绿茶',100);
INSERT INTO goods VALUES(3,'花茶',25);

/*
	创建存储过程方式1:
		
	语法格式
		delimiter $$ -- 声明语句的结束符号 自定义 ||
		create procedure 存储过程名称() -- 声明存储过程
		begin   -- 开始编写存储过程
		
			-- 要执行的SQL
			
		end $$ -- 存储过程结束
*/

-- 编写存储过程, 查询所有商品数据
DELIMITER $$
CREATE PROCEDURE goods_proc()
BEGIN 
	-- 查询商品数据
	SELECT * FROM goods;
END $$

-- 调用存储过程 call
CALL goods_proc;


/*
	存储过程创建方式2: 创建一个接收参数的存储过程
	
	语法格式
		create procedure 存储过程名(IN 参数名 参数类型)
*/

-- 创建存储过程 接收一个商品id, 根据id删除数据
DELIMITER $$
CREATE PROCEDURE goods_proc02(IN goods_id INT)
BEGIN 
	-- 根据id删除商品数据
	DELETE FROM goods WHERE gid = goods_id;
END $$

-- 调用存储过程 传递参数
CALL goods_proc02(1);

/*
	存储过程创建方式3 获取存储过程的返回值
	
	1.变量的赋值
		SET @变量名 = 值
	2.OUT 输出参数
		OUT 变量名 数据类型
*/

-- 向订单表 插入一条数据, 返回1,表示插入成功
DELIMITER $$
CREATE PROCEDURE orders_proc(IN o_oid INT,IN o_gid INT ,IN o_price INT,OUT out_num INT)
BEGIN 
	-- 执行插入操作
	INSERT INTO orders VALUES(o_oid,o_gid,o_price);
	-- 设置 out_num 值为1
	SET @out_num = 1;
	
	-- 返回 out_num
	SELECT @out_num;
END $$

-- 调用存储过程 获取返回值
CALL orders_proc(1,2,50,@out_num);

​ 触发器(trigger)是 MySQL 提供给程序员和数据分析员来保证数据完整性的一种方法,它是与表事件相关的特殊的存储过程,它的执行不是由程序调用,也不是手工启动,而是由事件来触发,比如对一个表进行操作(insert,delete, update)时就会激活它执行。——百度百科

​ 简单理解:当我们执行一条 sql 语句的时候,这条 sql 语句的执行会自动去触发执行其他的 sql 语句。

  • 监视地点(table)
  • 监视事件(insert/update/delete)
  • 触发时间(before/after)
  • 触发事件(insert/update/delete)
1
2
3
4
5
6
7
8
9
delimiter $ -- 将Mysql的结束符号从 ; 改为 $,避免执行出现错误
CREATE TRIGGER Trigger_Name -- 触发器名,在一个数据库中触发器名是唯一的
before/afterinsert/update/delete -- 触发的时机 和 监视的事件
on table_Name -- 触发器所在的表
for each row -- 固定写法 叫做行触发器, 每一行受影响,触发事件都执行
begin
    -- begin和end之间写触发事件
end
$ -- 结束标记

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/*
	触发器
		当我们执行一条sql语句的时候,这条sql语句的执行会自动去触发执行其他的sql语句。
	
	触发器创建的四个要素
		1. 监视地点(table) 
		2. 监视事件(insert/update/delete)  
		3. 触发时间(before/after)  
		4. 触发事件(insert/update/delete) 
	
	创建触发器
	语法结构
		delimiter $ -- 自定义结束符号
		create trigger  触发器名
		after/before(insert/update/delete) -- 触发的时机 和监视的事件
		on tableName -- 触发器所在表
		for each row -- 固定写法 表示行触发器
		begin
			-- 被触发的事件
		end $
*/
-- 向商品表中插入数据
INSERT INTO goods VALUES(4,'book',40);


-- 需求: 在下订单的时候,对应的商品的库存量要相应的减少,卖出商品之后减少库存量
/*
	监视的表 orders
	监视的事件 insert
	触发的时间 after
	触发的事件 update
*/
-- 1.修改结束符号
DELIMITER $
-- 2.创建触发器
CREATE TRIGGER t1
-- 3.设置触发的时间 以及监视的事件 监视的表
AFTER INSERT ON orders
-- 4.行触发器
FOR EACH ROW
-- 5.触发后要执行的操作
BEGIN
	-- 执行修改库存的操作 订单+1 库存-1
	UPDATE goods SET num = num - 1 WHERE gid = 4;
END $

-- 向orders表插入一个订单
INSERT INTO orders VALUES(1,4,25);


​ 事务是一个整体,由一条或者多条 SQL 语句组成,这些 SQL 语句要么都执行成功,要么都执行失败,只要有一条 SQL 出现异常,整个操作就会回滚,整个业务执行失败。

​ 使用事务,可以避免某些异常情况的发生:

5.jpg
MySQL笔记_基础2(5.事务概述)

  • START TRANSACTION; 或者 BEGIN;

    • 这个语句显式地标记一个事务的起始点。
  • COMMIT

    • 表示提交事务,即提交事务的所有操作,具体地说,就是将事务中所有对数据库的更新都写到磁盘上的物理数据库中,事务正常结束。
  • ROLLBACK

    • 表示撤销事务,即在事务运行的过程中发生了某种故障,事务不能继续执行,系统将事务中对数据库的所有已完成的操作全部撤销,回滚到事务开始时的状态
  • 6.png
    MySQL笔记_基础2(6.事务流程)
  • 如果事务中 SQL 语句没有问题,commit 提交事务,会对数据库数据的数据进行改变。

  • 如果事务中 SQL 语句有问题,rollback 回滚事务,会回退到开启事务时的状态。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
-- 创建账户表
CREATE TABLE account(
    -- 主键
    id INT PRIMARY KEY AUTO_INCREMENT,
    -- 姓名
    NAME VARCHAR(10),
    -- 余额
    money DOUBLE
);
    
-- 添加两个用户
INSERT INTO account (NAME, money) VALUES ('tom', 1000), ('jack', 1000);

-- 模拟一次转账操作
-- tom 账户转出, -500元
UPDATE account SET money = money - 500 WHERE NAME = 'tom';
-- jack 账户转入 + 500元
UPDATE account SET money = money + 500 WHERE NAME = 'jack';

-- 假设转出这步出错了,而转入这步正常执行了,就会出问题
-- 而启用事务管理,就能避免这种情况

/*
MySql 事务操作
手动提交事务
1.开启事务 start transaction; 或者 begin;
2.提交事务 commit;
3.回滚事务 rollback;

自动提交事务
MySql 默认的提交方式 自动提交事务
每执行一条 DML语句 都是一个单独的事务
*/

-- 手动提交事务演示
-- 成功案例
start transaction;
-- 开启事务

update account set money = money - 500 where name = 'tom';
update account set money = money + 500 where name = 'jack';
commit;
-- 提交事务

-- 失败案例
start transaction;
-- 开启事务
INSERT INTO account VALUES(NULL,'张百万',3000);
INSERT INTO account VALUES(NULL,'有财',3500);
-- 直接关闭窗口,模拟系统崩溃,数据没有发生改变
-- 或者由程序检测到出错,执行回滚操作
rollback;

  • 默认状态下,MySQL开启了自动提交事务
  • 每一条 DML(增删改)语句作为一个单独的事务
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
--  登录mysql,查看autocommit状态。
SHOW VARIABLES LIKE 'autocommit';

-- 把 autocommit 改成 off;
SET @@autocommit=off;

-- 执行修改操作

-- 选择数据库
use db2;

-- 修改数据
update account set money = money - 500 where name = 'jack';

-- 手动提交
commit;

  • 原子性
    • 每个事务都是一个整体,不可以再拆分,事务中的所有 SQL
    • 要么都执行成功,要么都执行失败
  • 一致性
    • 事务在执行之前,数据库的状态,与事务执行之后的状态要保持一致
  • 隔离性
    • 事务与事务之间不应该相互影响,执行时要保证隔离状态
  • 持久性
    • 一旦事务执行成功,对数据的修改是持久的

  • 各个事务之间是隔离,相互独立。
  • 但是一个数据库可能拥有多个访问客户端,这些客户端都可以并发方式访问数据库,数据库的相同数据可能被多个事务同时访问,如果多个事务对数据库中的同一批数据进行并发访问的时候,就可能引发一些问题:
    • 脏读(Dirty Read)
      • 一个事务读到了另一个未提交事务修改过的数据
      • 7.png
        MySQL笔记_基础2(7.脏读)
    • 不可重复读(Non-Repeatable Read)
      • 一个事务只能读到另一个已经提交的事务修改过的数据,并且其他事务每对该数据进行一次修改并提交后,该事务都能查询得到最新值
      • 8.png
        MySQL笔记_基础2(8.不可重复读)
    • 幻读(Phantom):
      • 一个事务先根据某些条件查询出一些记录,之后另一个事务又向表中插入了符合这些条件的记录,原先的事务再次按照该条件查询时,能把另一个事务插入的记录也读出来。
      • 9.png
        MySQL笔记_基础2(9.幻读)
  • 可以通过设置不同的隔离级别来解决对应的问题
    • read uncommitted,读未提交
      • 可以防止:无
    • read committed,读已提交( Oracle、MS_SQL_Server 的默认隔离级别)
      • 可以防止:脏读
    • repeatable read,可重复读( MySql 的默认隔离级别)
      • 可以防止:脏读、不可重复读
    • serializable,串行化(各事务排队执行,失去了并发的高效率优势)
      • 可以防止:脏读、不可重复读、幻读
    • 注意:隔离级别从小到大,安全性越来越高,但是效率越来越低,根据不同的情况选择对应的隔离级别

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
/*
	查看隔离级别
	select @@tx_isolation;
	
	设置隔离级别
	set global transaction isolation level 级别名称;
	read uncommitted 读未提交
	read committed   读已提交
	repeatable read  可重复读
	serializable     串行化

*/
-- 查看隔离级别	MySql默认隔离级别  repeatable read
select @@tx_isolation;

-- 设置隔离级别为 读已提交
set global transaction isolation level read committed ;

-- 设置完隔离级别后,要重新连接数据库

10.png
MySQL笔记_基础2(10.脏读演示)
11.png
MySQL笔记_基础2(11.脏读解决)

12.png
MySQL笔记_基础2(12.不可重复读演示)
13.png
MySQL笔记_基础2(13.不可重复读问题场景)
14.png
MySQL笔记_基础2(14.不可重复读解决)

15.png
MySQL笔记_基础2(15.幻读演示)

教学视频中能直观地看到,一个线程会等待另一个线程 commit; 之后,再执行命令

相关内容