MySQL

1、什么是数据库

数据集合,数据是有一定规律、结构

数据库分类

​ 关系型数据库:数据库中存储数据以表为单位

​ 非关系型数据库:通常用来解决特定的数据:数据缓存,高并发访问。存储形式有很多种:Redis数据库 通过键值对的形式存储数据

主流数据库介绍:

​ MySQL:开源免费

​ Oracle: 闭源、最贵、性能最高,市场排行第二

​ SQLServer:微软,市场排行第三,主要应用.NET(C#)开发的网站

​ DB2:IBM,主要应用银行等国有企业中

​ sqlite:轻量级,只有几十K 一般应用在嵌入式和移动设备中

开源和闭源区别:

​ 开源:开源代码,盈利方式靠卖业务,赚钱慢少,用户群大,大牛程序员会无偿帮忙升级维护

​ 闭源:不开放代码,盈利方式卖产品+服务,赚钱快,大牛们各种攻击找漏洞

2、基础命令

登录命令 mysql -u root -p

  • 展示数据库(查看mysql目录下的所有数据库)

    • show databases;
  • 新建数据库

    • create database 数据库名;
  • 使用当前数据库

    • use 数据库名;
  • 展示数据库表

    • show tables;
  • 展示数据库表信息

    • desc 表名;
    • show create table 数据库名;

3、SQL

3.1、什么是sql

​ 结构化查询语句

3.2、sql的分类

  1. DDL(数据库定义语言) 定义数据库对象:库、表、列、索引、约束

    • create、drop、alter、rename
  2. DML(数据库操作语言):操作数据库的数据

    • insert、delete、update、select
  3. DCL(数据库控制语句):设置访问权限、安全级别

    • grant(授权) revoke(回收特权)
  4. TC( 事务控制):控制事务回滚,保存节点以及提交

    • commit、rollback、savepoint
1
2
3
4
5
6
7
8
9
#建表语句

create table 表名(

id int primary key,

name varchar(255)

);

3.3、列类型

  • 整型:int、Integer、bigint等

  • oracle中数字类型设置成number类型,mysql中没有number类型

  • int类型默认长度11,如果超出长度报错

  • 浮点型:float,double,decimal(可以手动设置精度)

  • 字符型:char,varchar

  • char是定长的字符串,长度100,无论使用长度是多还是少,所占内存都是100

  • varchar为自己定义一个长度,开辟的内存100但是实际所占内存按照实际存入数据大小

  • 时间类型:date、time、timestamp(时间戳)

3.4、删除语句

  • DROP:drop table 表名;删除表结构以及数据

  • TRUNCATE:truncate table 表名;仅删除数据

  • 友情提示:尽量谨慎使用,在没有备份的时候,千万别用

3.5、修改语句(ALTER)

修改列:modify 仅能修改列类型

1
alter table 表名 modify 字段名 字段类型

增加列:ADD

1
alter table 表名 add age int;

删除列:drop

1
alter table 表名 drop 字段名;

修改列名以及列类型:change

1
alter table 表名 change 要修改的字段名 新的字段名 字段类型;

4、重命名:RENAME

1
RENAME TABLE 表名 to 新表名

3.6、约束

1、什么是约束

​ 强制执行在表上的一种规则

2、约束的作用

​ 保证数据库的完整性;在表拥有依赖的情况下,保证数据安全

3、约束的分类

​ 非空约束、唯一约束、检查约束、主键约束、外键约束

​ 根据作用于分成表级约束以及列级约束

4、约束的创建

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
非空约束:not null
create table 表名(
id int not null
);
被约束的字段不能为空

唯一性约束:unique
create table 表名(
id int unique
);
被约束的字段是唯一值
insert into test(1001,'张三');
insert into test(1001,'李四');

检查约束:check(mysql会忽略)
create table 表名(
id int primary key,
name varchar(20),
age int check(age>18)
);
insert into test values(1001,'张三',15);
insert into test values(1002,'李四',15);
主键约束(自增):即时表级约束也是列级别的约束。非空且唯一,且代表创建了一个索引
一个表中只能有一个主键,可以有多个主键约束

关键字primary key,constraint
外键约束:拥有主从关系,将某表主键设置成另一个表的外键,主键表为主表,外键表为外表

当删除主表数据库的时候,要确保从表中被占有的数据已经删除,当增加或者删除的时候,要确保主表中已经有该数据或者已删除该数据

5、约束的修改

​ 约束的增加:

​ 增加一个非空约束:

1
alter table 表名 modify/add id int not null

​ 删除一个非空约束:

1
alter table 表名 modify id

​ 增加唯一性约束:

1
alter table 表名 constraint unique(字段)

​ 增加外键约束:

1
alter table 表名 ADD constraint  foreign KEY 约束名(字段名) references 主表名(字段名)

6、设置主从表表级联动

1
alter table [表名] ADD constraint  foregin key 约束(字段名) references 主表名(字段名)

set null:当外键值被删除之后,设置为空

on action/restrict:和外键默认效果相同 可不写

cascade:主从表联动

7、自增,默认值

auto_increment,default

放置在字段类型后面

1
create table user(id int auto_increment,name varchar(20) default 'sss');

Mysql单表操作

表数据的CRUD操作

一、DML语句

1.1、插入语法

一、语法

1
2
3
4
#方式一
INSERT INTO 表名(字段名,...) VALUES(值...,...)
#方式二
INSERT INTO 表名 SET 字段名=值,字段名=值...

二、特点

  1. 要求值得类型和字段的类型要一致或兼容

  2. 字段的个数和顺序不一定与原始表中字段个数和顺序一直,但是必须保证值和字段一一对应

  3. 假如表中有可以null的字段,注意可通过2种方式进入插入null值

    1. 字段和值都可以忽略
    2. 字段可以写上,值使用null
  4. 字段和值得个数必须一致

  5. 字段名可以忽略,默认所有列

  6. 方式一,支持一次插入多行

    1
    INSERT INTO 表名[(字段名,...)] VALUES(值...,...),(值...,...),(值...,...)...
  7. 支持子查询

    1
    INSERT INTO 表名 查询语句

三、演示

1.1、方式一:插入数据

1
INSERT INTO 表名[(字段名,...)] VALUES(值...,...),(值...,...),(值...,...)...

1.2、方式二:插入数据

1
INSERT INTO dept dept_id = 1,dept_name = 'test',dept_descript = 'test';

1.2、修改语句

一、语法

1
2
3
#更新
UPDATE 表名 SET 字段 = 值,....WHERE 查询条件;
#无查询条件默认更新全部

二、演示

1、单表更新
1
UPDATE emp SET age = 19 WHERE emp_name = "张三";

1.3、删除语句

一、语法

1
2
3
#删除
DELETE FROM 表名 WHERE 查询条件;
truncate table 表名 ;#删除表中的所有记录

二、演示

1
DELETE FROM emp WHERE emp_name LIKE '李%';

二、DQL语句

2.1、基础查询

一、语法

1
SELECT 查询列表 FROM 表名;

二、特点

1、查询列表是可以字段、常量、函数、表达式

2、查询结果是一个 虚拟表

三、例子

1、查询单个字段

1
SELECT 字段名 FROM 表名;

2、查询多个字段

1
SELECT 字段名,字段名 FROM 表名;

3、查询所有字段

1
SELECT * FROM 表名;

4、起别名

1
SELECT 字段名 AS "别名" FROM 表名;

注意:别名可以使用单引号、双引号,当只有一个单词时候,可以忽略引号,当有多个单词且有空格或者特殊符号,不能省略 AS 可以 省略

5、查询常量

1
SELECT 常量;

注意:字符型和日期型的常量值必须用单引号,数值型不需要

6、去重复

1
SELECT DISTINCT 字段名 FROM 表名;

2.2、条件查询

一、语法

1
SELECT 查询列表 FROM 表名 WHERE 筛选条件;

二、分类

1、条件运算符:>、>=、 <、 <=、 =、 <=>等于、 !=、 <>不等于

2、逻辑运算符:and、or、not

3、模糊运算符

  • like:**%**任意多个字段 **_**任意单个字符
  • between and
  • not between and
  • in
  • is null
  • is not null

三、演示

1、查询工资>12000的员工的信息

2、查询工资>=14000的员工

3、查询工资<12000的员工的信息

4、查询<=14000的员工

5、查询员工编号 = 1001的员工信息

6、查询员工编号<=>1004的员工信息

注意:只能判断普通类型的数值,而<=>不仅可以判断普通类型值 还可以判断null

1
2
3
4
5
6
SELECT
*
FROM
emp
WHERE
emp_id <=> 1004

7、查询员工编号!=1001的员工信息

8、查询员工编号<>1001的员工信息

———————————————————————————————————————————————————

9、查询工资>12000 &&工资<18000的员工信息

1
2
3
4
5
6
SELECT
*
FROM
emp
WHERE
salary > 1200 AND salary < 18000

10、查询工资<=12000 ||工资>=18000的员工信息

11、查询工资 不是 <=12000 ||工资>=18000的员工信息

1
2
3
4
5
6
SELECT
*
FROM
emp
WHERE NOT
salary > 1200 OR salary < 18000

12、查询员工第一个字符是王、第四个字符为子的员工信息

1
2
3
4
5
6
SELECT
*
FROM
emp
WHERE
emp_name LIKE '王__子%'

13、查询员工编号在1000-2000之间的员工

1
2
3
4
5
6
SELECT
*
FROM
emp
WHERE
emp_id BETWEEN 1000 AND 2000

14、查询员工编号不在1000-2000之间的员工

15、查询员工的老板编号 是 2002,2001的一个员工的名字和员工编号

1
2
3
4
5
6
SELECT
*
FROM
emp
WHERE
boos_id IN(2002,2001);

注意:in列表的值类型必须一致或者兼容,in列表中不支持支持通配符%和_

注意:=、!=不能用来判断NULL、而<=>、is null、is not null 可以来判断null

2.3、排序查询

一、语法

1
2
3
4
5
6
SELECT
查询列表
FROM

WHERE 筛选条件
ORDER BY 排序列表 asc/desc

二、注意

  1. 排序列表可以是单个字段、多个字段、别名、函数、表达式

  2. asc代表升序,desc代表的是降序,如果不写,默认asc

  3. order by 的位置一般放在查询语句的最后面(除limit语句之外)

三、例子

  1. 按单个字段排序:查询员工的信息,要求工资降序

  2. 按多个字段排序:查询员工的信息,要求先按工资的降序,再按照员工编号升序

2.4、单行函数

一、语法

1
SELECT 函数名(实参列表) FROM 表

二、分类

1、字符函数

  • concat:连接字符
  • substr:截取子串
  • replace:替换字符
  • upper:变大写
  • lower:变小写
  • lpad:左填充
  • rpad:右填充
  • length:获取字节长度
  • trim:去除前后空格
  • instr:获取子串第一次出现的索引(注意:MYSQL的索引从1开始)

2、数学函数

  • round:四舍五入
  • ceil:向上取整
  • floor:向下取整
  • mod:取模运算
  • truncate:保留小数位数,不进行四舍五入
  • rand:获取随机数,返回0-1之间的小数

3、日期函数

  • now:返回当前日期+时间
  • curdate:返回当前日期
  • curtime:返回当前日期
  • year:返回年
  • month:返回月
  • day:返回日
  • hour:小时
  • minute:分钟
  • second:秒
  • monthname:以英文形式返回月
  • datediff:返回2个日期相差的天数
  • date_format:将日期转化成字符
  • str_to_date:将字符转成日期
1
2
3
4
5
6
7
8
9
10
#格式符
%Y:四位的年份
%y:二位的年份
%m:二位数的月份(01,02.....)
%c:一位的月份(1,2.....)
%d:日(01,....30,31)
%H:小时(24小时制)
%h:小时(12小时)
%i:分钟(00....59)
%s:秒(00....59)

4、控制函数

  • if 判断函数
  • case:分支函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
1、IF(条件判断,表达式1,表达式2):条件成立,返回表达1,否则返回表达2

2、case的格式一:
CASE 变量或者字段或者表达式
WHEN 常量1 THEN 值1
WHEN 常量2 THEN 值2
....
ELSE 值 n
END;

3、case的格式二:
CASE
WHEN 条件1 THEN 值1
WHEN 条件2 THEN 值2
....
ELSE 值 n
END;

5、其他函数

  • version:当前数据库的版本
  • database:当前打开的数据库
  • user:当前登录的用户
  • password(“字符”):返回该字符的密码格式
  • md5(“加密”):返回该字符的md5加密形式

三、演示

1、concat

1
SELECT concat('hello','World') AS out_put

2、substr

1
2
3
4
#截取从指定的索引后面所有字符
SELECT SUBSTR('HelloWorld',7) AS out_put
#截取从指定索引处指定字符长度的字符
SELECT SUBSTR('HelloWorld',7,1) AS out_put

3、replace

1
SELECT REPLACE('HelloWorld','World','W') AS out_put

4、round

1
2
3
4
#默认的四舍五入
SELECT ROUND(-1.55) AS out_put
#指定保留的小数位数
SELECT ROUND(-1.556,2) AS out_put

注意:四舍五入和符号无关

5、rand

6、返回当前的时间

1
SELECT NOW() AS out_put

7、日期函数

1
2
SELECT YEAR(NOW())年;
SELECT MONTH(NOW())月;

8、计算日期天数:datediff

1
SELECT DATEDIFF('2021-5-1','2021-4-18')AS out_put

9、日期格式化date_format

1
SELECT DATE_FORMAT(NOW(),'%Y年%m月%d日')  AS out_put

10、字符串转日期

11、if

1
SELECT IF(10 < 5,'大','小' ) AS out_put

12、case的格式一

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/*
查询员工的工资 ,要求
boos_id = 2001 显示工资为1.1倍
boos_id = 2002 显示工资为1.2倍
boos_id = 3001 显示工资为1.3倍
其他部门 显示的工资为原始工资
*/
SELECT
salary 原始工资,
emp_name,
CASE
boos_id
WHEN 2001 THEN salary * 1.1
WHEN 2002 THEN salary * 1.2
WHEN 3001 THEN salary * 1.3
ELSE salary
END AS 新工资
FROM
emp

13、case的格式二

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/*
查询员工的工资情况
如果工资 > 20000 显示A级别
如果工资 > 15000 显示B级别
如果工资 > 10000 显示C级别
否则,显示D级别
*/
SELECT
salary,
CASE
WHEN salary > 20000 THEN 'A'
WHEN salary > 15000 THEN 'B'
WHEN salary > 10000 THEN 'C'
ELSE 'D'
END AS 工资级别
FROM
emp

14、password(‘字符’)

15、md5(’字符’)

2.5、聚合函数

一、语法

1
SELECT 函数名(实参列表) FROM 表

二、分类

  1. SUM()返回某个列之和
  2. AVG()返回某列的平均值
  3. MAX()返回某列的最大值
  4. MIN()返回某列的最小值
  5. COUNT()返回某列的行数

注意:

  • sum、avg 一般用于处理数值型,max、min、count可以处理任何类型
  • 以上分组函数都忽略null值
  • 可以和distinct搭配去重运算 select sum(distinct 字段) from 表
  • 一般使用COUNT(*)用于统计行数
  • 和分组函数一同查询字段要求 group by 后的字段

三、演示

1、SUM

1
SELECT SUM(salary) FROM emp

2、AVG

1
SELECT AVG(salary) FROM emp

3、MAX

1
SELECT MAX(salary) FROM emp

4、MIN

1
SELECT MIN(salary) FROM emp

5、COUNT

1
SELECT COUNT(salary) FROM emp

2.6、分组查询

一、语法

1
2
3
4
5
6
7
8
SELECT
查询列表
FROM

WHERE 筛选条件
GROUP BY 分组的字段
having 分组后的筛选
order by 排序的字段

二、特点

1、和分组函数一同查询的字段必须GROUP BY后出现的字段

2、筛选分为2类:分组前的筛选和分组后的筛选

针对的表 语句位置 连接的关键字
分组前的原始表 GROUP BY前 where
分组后的结果集 GROUP BY后 havaing

3、分组可以按照单个字段也可以按照多个字段

4、分组可以搭配的排序

三、演示

1、查询每个部门的员工的平均工资

1
2
3
4
5
6
7
SELECT
AVG(salary),
dept_id
FROM
emp
GROUP BY
dept_id

Mysql多表查询操作

一、含义

多表查询又称为连接查询

二、分类

1、按照年代分类

  • sql92标准:支持内连接
  • sql99标准:支持内连接、部分外连接(左连接、右连接)、交叉连接

2、按照功能分类

  • 内连接
    • 等值连接
    • 非等值连接
    • 自连接
  • 外连接
    • 左外连接
    • 右外连接
    • 全外连接
  • 交叉连接

2.1、sql92

2.1、sql92标准:等值连接

1
2
3
4
5
6
7
8
#查询员工的名字和对应的部门名
SELECT
emp_name,
dept_name
FROM
emp e,
dept d
WHERE e.dept_id = d.dept_id

2.2、sql92标准:非等值连接(不需要字段与字段匹配)

1
2
3
SELECT *
FROM emp,dept
WHERE emp.age = 18

2.2、sql99标准

按照功能:内连接、外连接、交叉连接

一、语法

连接类型 JOIN 表 ON

1
2
3
4
5
6
7
8
SELECT 
查询列表
FROM 表1 别名1
连接类型 JOIN 表2 别名2 ON 连接条件
WHERE 分组前的筛选
GROUP BY 分组列表
HAVING 分组后的筛选
ORDER BY 排序列表

二、连接类型

1、内连接:inner

2、外连接

  • 左外连接:left [outer] 左边的是主表
  • 右外连接 right [outer] 右边的是主表
  • 全外连接(了解):full 两边都是主表 mysql不支持全外连接 Oracle支持

3、交叉连接:cross 笛卡尔乘积

三、演示

1、内连接:等值连接 where inner join

1
SELECT * FROM emp e INNER JOIN dept d ON e.dept_id = d.dept_id

2、内连接:非等值连接

3、内连接:自连接

1
2
3
4
5
6
7
#就是以自己这个表为参照表,查询一些特殊的数据
#查询所有员工以及上级的信息
SELECT e1.emp_name '下属名',e2.emp_name '上司名'
FROM emp e1,emp e2
WHERE e1.boos_id = e2.emp_id

SELECT e1.emp_name as '下属名',e2.emp_name as '领导' FROM emp e1 JOIN emp e2 ON e1.boos_id = e2.emp_id

4、外连接:左外连接

1
2
3
#查询所有部门底下的工作人员 left join on
SELECT * FROM dept d LEFT JOIN emp e ON d.dept_id = e.dept_id
#左连接 左表的记录会全部表示出来,而右表只会显示符合搜索条件的记录。右边记录不足的地方均为NULL

5、外连接:右外连接

6、交叉查询

1
2
3
4
5
#他们都返回被连接的2个表所有数据行的笛卡尔积
#返回到数据行数等于第一个表中符合查询条件的数据行数乘以第二个表中符合查询条件的数据的函数
SELECT b.*,bo.*
FROM emp b
CROSS JOIN dept bo

7、全连接

1
2
3
4
#union 全连接 就是左连接+右连接 去重
SELECT * FROM dept d LEFT JOIN emp e ON d.dept_id = e.dept_id
UNION
SELECT * FROM dept d RIGHT JOIN emp e ON d.dept_id = e.dept_id

四、子查询

一、含义

子查询(内查询) 一个查询语句嵌套到另一个select查询语句中的子句。外面的语句insert、delete、update、select等。一般select作为外面语句比较多。

二、分类

1、按照出现的位置

2、按照结果集划分

三、特点

1、子查询妨碍小括号里面

2、子查询一般放在条件查询的右侧

3、子查询的执行优先于主查询执行(主查询的条件用到了子查询的结果)

4、标量子查询 一般搭配操作符 >、>=。。。。

5、列子查询 一般配合多行操作符 in notin any some all exits

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/*子查询可以出现的位置
select(1) from(2) where(3) having(4)
(1)表示一个字段
(2)表示一个表
(3)表示一个条件
(4)表示一个范围
*/
#查询每一个部门的最高薪资的员工信息
SELECT e1.dept_id as '部门号',e1.salary
FROM emp e1,
(select dept_id,max(salary)as maxSal from emp group by dept_id) as e3
WHERE e1.dept_id = e3.dept_id AND e1.salary = e3.maxSal
ORDER BY e1.dept_id



SELECT * FROM emp e WHERE e.dept_id in(SELECT d.dept_id FROM dept d)

1、SELECT后面

1
2
3
4
5
6
7
8
9
10
11
#查询每隔部门的员工个数
SELECT
d.*,(
SELECT
COUNT(*)
FROM
emp e
WHERE e.dept_id = d.dept_id
) 个数
FROM
dept d

2、from后面

1
2
SELECT * FROM(SELECT emp_name,emp_id FROM emp) T
WHERE T.emp_name = '张三'

3、where后面

1
2
3
4
5
6
SELECT
emp_name
salary
FROM
emp
WHERE salary = (SELECT MIN(salary) FROM emp)

4、列子查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#查询所有是领导的员工的姓名 DISTINCT用于返回唯一不同的值

SELECT
emp_name
FROM
emp
WHERE emp_id IN(
SELECT DISTINCT boos_id FROM emp
)

#返回boosid为3001 工资低于员工的信息

SELECT
emp_id,
emp_name,
salary
FROM
emp
WHERE salary < ANY(
SELECT DISTINCT salary FROM emp WHERE boos_id = 3001
) AND boos_id <> 3001

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#	行子查询 查询员工编号最大 并且工资最高的员工
SELECT
*
FROM
emp
WHERE(emp_id,salary) = (
SELECT MAX(emp_id),MAX(salary) FROM emp
)

#查询又员工的部门名 EXISTS后面
SELECT
dept_name
FROM
dept d
WHERE EXISTS(
SELECT * FROM emp e
WHERE e.dept_id = d.dept_id
)
1
2
3
4
5
6
7
8
9
10
11
12
#关联查询 查询国内男性的信息以及国外男性的信息

SELECT id,cname FROM t_ca WHERE csex = '男'
UNION ALL
SELECT t_id,tname FROM t_ua WHERE csex = 'male'
/*
1、要查询的结果来自多个表 且多个表没有直接连接关系,但是查询的信息一直 可以使用联合查询
2、要求多条查询语句的查询列数是一直
3、要求多条查询语句查询的每一列的类型和顺序最好一直
4、union关键字默认去重 使用UNION ALL 可以包含重复项
*/

Mysql事务

二叉树

特点:

  1. 每个节点包含一个元素以及n个子树0<=n<=2
  2. 左树和右树是有顺序,次序不能任意颠倒,左树的值小于父节点,右边树的值大于父节点

视图

数据库当中的虚拟的表

作用:

  1. 简单。看到的都是需要的。视图不仅可以简化用户对数据的理解,也可以简化他们的操作,那些被经常使用的查询可以被定义为视图,但是视图并不在数据库中以存储的数据值形式存在
  2. 安全性
  3. 逻辑数据独立性,屏蔽真实的表结构变化带来的影响

事务

访问并可能操作数据库的一个数据库序列。这些操作要么全部执行,要么全部不执行。只是一个不可分割的工作单位。

比如:我们去银行转账

  1. 从第一个账户划出钱

  2. 将钱存入第二个账户

整个交易过程,看错一个事务,成功则全部成功,失败则需全部撤销。避免产生数据不一致问题

mysql默认自动提交事务

在 MySQL 中只有使用了 Innodb 数据库引擎的数据库或表才支持事务

事务的四大特性(ACID)

1、原子性

  • 事务开始后的所有操作,要么全做要么全部不做。不可能停留在中间环节,事务执行过程中出错,回滚到事务开始前的状态,所有的操作都没有发现。也就是说事务是一个整体,就想原子,是物质构成的基本单位。
  • 一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。

2、一致性

  • 数据库一个一致性状态改变另一个一致状态,事务执行之前和执行之后必须处于一致状态。
  • 在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作。

3、隔离性

  • 同一时间,只允许一个事务请求同一个数据。A取钱的过程结束前,B不能向这个A进行转账
  • 数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。

4、持久性

  • 事务完成后,事务对数据库的所有更新将被保持到数据库,不能回滚。
  • 事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。

事务的并发问题

  1. 脏读:事务A读取事务B更新的数据,然后B回滚操作,那么A读取的数据是脏读数据

  2. 不可重复读:事务A多次读取同一个数据,事务B在事务A多次读取的过程中,对数据更新并提交,导致A多次读取同一个数据的时候,结果不一致

  3. 幻读:adminA将数据库中所有的学生的成绩从具体分改成ABCD等级,但是adminB就在这个时候插入了具体的分数记录,当系统admin A改完结束后发现有一条数据没改过来,感觉是幻觉。

事务的隔离级别

隔离级别 脏读(Dirty Read) 不可重复读(NonRepeatable Read) 幻读(Phantom Read)
未提交读(Read uncommitted) 可能 可能 可能
已提交读(Read committed) 不可能 可能 可能
可重复读(Repeatable read) 不可能 不可能 可能
可串行化(Serializable ) 不可能 不可能 不可能

InnoDB默认是可重复读级别的

  • 脏读: 脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。
  • 不可重复读:是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。
  • 幻读:第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样,幻读是数据行记录变多了或者少了。

简单点总结下他们的区别:脏读是指读取了未修改完的记录,不可重复读指因为被其它事务修改了记录导致某事务两次读取记录不一致,而幻读是指因为其它事务对表做了增删导致某事务两次读取的表记录数不一致问题。

(1)read uncommitted 未提交读 所有事务都可以看到没有提交事务的数据。

eg: 公司发工资了,领导把5000元打到A的账号(正常工资2千)上,但是该事务并未提交,而A正好去查看账户,发现工资已经到账,是5000元整,非常高兴。但是领导随后发现给A的工资发多了,于是迅速回滚了事务,修改金额后,将事务提交,最后A实际的工资只有2000元,A空欢喜一场。

剖析:脏读:“事务A:leader给A发工资”,“事务B:A查询工资账户”,事务B读取了事务A尚 uncommitted 的数据。当隔离级别设置为Read uncommitted时,就可能出现脏读,如何避免脏读,请看下一个隔离级别。

(2)read committed 提交读     事务成功提交后才可以被查询到。  

eg: A拿着工资卡去消费,出门前查到卡里是2000元,凑巧紧接着她的老婆作为急用把A工资卡的2000元转到另一账户, 当A准备付款时,却发现工资卡已经没有钱,扣款失败……  

剖析:两个并发的事务,“事务A:A消费”,“事务B:A的老婆网上转账”,事务A事先读取了数据,紧接着事务B更新了数据,并提交了事务,而事务A再次读取该数据时,数据已经发生了改变。

当隔离级别设置为Read committed时,避免了脏读,但是可能会造成不可重复读。大多数数据库的默认级别就是Read committed,比如Sql Server , Oracle。如何解决不可重复读这一问题,请看下一个隔离级别。

(3)repeatable 重复读        同一个事务内多次查询却返回了不同的数据值,即可能将未提交的记录查询出来,而出现幻读。注:Mysql的默认隔离级别就是Repeatable read。  

前言:当隔离级别设置为Repeatable read 时,可以避免不可重复读。当A拿着工资卡去消费时,一旦系统开始读取工资卡信息(即事务开始),A的老婆就不可能对该记录进行修改,也就是A的老婆不能在此时转账。      虽然Repeatable read避免了不可重复读,但还有可能出现幻读 。  

eg1: A的老婆在银行工作,她可以很方便的查看A的信用卡消费记录。月末了,她正在查询A当月的消费情况 (select sum(amount) from transaction where month = ‘本月’)为80元,而A此时正好在某收银台买单,消费1000元,即新增了一条1000元的消费记录(insert transaction … ),并提交了事务,随后A的老婆将A当月信用卡消费的明细打印到A4纸上,却发现消费总额为1080元,A的老婆很诧异,以为出现了幻觉,幻读就这样产生了。

eg2: 在事务A中,读取到张三的工资为5000,操作没有完成,事务还没提交。与此同时,事务B把张三的工资改为8000,并提交了事务。随后,在事务A中,再次读取张三的工资,此时工资变为8000。在一个事务中前后两次读取的结果并不致,导致了不可重复读。(大部分数据库缺省的事物隔离级别都不会出现这种状况)

(4)Serializable可串行化

强制的进行排序,在每个读读数据行上添加共享锁。会导致大量超时现象和锁竞争。这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。除了防止脏读,不可重复读外,还避免了幻读。

附录:

★  脏读    : 读取了前一事务 未提交 的数据 ;

  不可重复读 : 读取了前一事务 提交 的数据;

★ 幻读 与 不可重复读    

common  :都是读取了另一条已经提交的事务(这点与脏读不同);

differences

    不可重复读 :查询的都是同一个数据项

    幻读   :针对的是一批数据整体(比如数据的个数)

不可重复读eg:  当隔离级别设置为Repeatable read 时,可以避免不可重复读

  eg2: 在事务A中,读取到张三的工资为5000,操作没有完成,事务还没提交。

    与此同时,事务B把张三的工资改为8000,并提交了事务。

    随后,在事务A中,再次读取张三的工资,此时工资变为8000。在一个事务中前后两次读取的结果并不致,导致了不可重复读。(大部分数据库缺省的事物隔离级别都不会出现这种状况)

幻读eg:

  eg1: 目前工资为5000的员工有10人,事务A读取所有工资为5000的人数为10人。

    此时,事务B插入一条工资也为5000的记录,并且commit了。

    这时,事务A再次读取工资为5000的员工,记录为11人。此时产生了幻读。

    (大部分数据库缺省的事物隔离级别都会出现这种状况,此种事物隔离级别将带来表级锁)

  eg2: A将db中all学生的score从数字分数改变为ABCDE等级,但是B就在此时插入了一条具体的分数,当A改完后发现还有一条记录没有改过来,就好像发生了幻觉一样.这就叫幻读。