一文读懂MySQL数据库
1. MySQL简介
1.1 数据库介绍
数据库(Database,DB)从本质上讲就是一个文件系统,它能够讲数据有组织地集合砸一起,按照一定的规则长期存储到计算机的磁盘中,并且能够供多个用户共享和使用,同时,用户能够对数据库中的数据进行插入、删除、修改和查询操作
数据库管理系统(Database Management System,DBMS)从本质上讲就是一个为管理数据库中的数据而设计的一套管理系统。它依托数据库,对外提供统一管理数据库中数据的功能或接口,能够有效地对数据库的安全、认证、数据备份、数据恢复、数据传输等进行统一的管理。同时,数据库管理系统能够根据所依托的数据库模型对数据库进行相应的分类。
大多数的数据库都是通过数据库管理系统对数据库中的数据进行管理和维护的
1.2 数据库分类
关系型数据库:关系型(二位关系表,一张表由多列和行组成)
Oracle:是Oracle公司的数据库产品
MySql:最早属于瑞典的MysqlAB公司的,后被Sun公司收购,Sun在2009年4月20号被Oracle收购
SQLServer:微软旗下的数据库产品
Access:微软旗下的数据库产品
DB2:IBM公司旗下的数据库产品
postgreSQL:免费的一款数据库产品
SQLite3:嵌入式的数据库
非关系型数据库:
MongoDB:是一个面向文档的开源NoSQL数据库,MongoDB使用JSON之类的文档来存储任何数据。它是用c++写的
Cassandra:是Facebook为收件箱搜索开发的。Cassandra是一个用于处理大量结构化数据的分布式数据存储系统
Redis:是最著名的键值存执。Redis是用c语言编写的。它是更具BSD授权的
HBase:是谷歌为BigTable数据库设计的分布式非关系数据库
1.3 数据库安装与配置
本次安装在Ubuntu16系统, 默认安装mysql的版本是5.7
如果是Ubuntu20,安装mysql的版本是8.0
mysql官方文档: https://dev.mysql.com/doc/refman/5.7/en/
1.3.1 安装命令
sudo apt-get install mysql-server -y
安装过程中,弹出界面,要求输入root用户的口令
输入新口令后,再次弹出确认口令的输入
1.3.2 进入mysql
mysql -u(用户) -p(密码)
在mysql中,输入exit
退出
1.3.3 启动或关闭mysql服务
查看当前mysql服务的运行状态:
sudo systemctl status mysql
停止mysql服务:
sudo systemctl stop mysql
启动mysql服务:
sudo systemctl start mysql
重启mysql服务:
sudo systemctl restart mysql
ps查看mysql服务是否运行:
ps -ef|grep mysql
1.3.4 解决mysql中文乱码问题
mysql的配置文件:
/etc/mysql/mysql.conf.d/mysqld.cnf 服务器的配置文件
/etc/mysql/conf.d/mysql.cnf 客户端的配置文件
在服务器的配置文件的[mysqld]下,新增一行,内容如下:
character-set-server=utf8
在客户端配置文件的[mysql]下新增一行,内容如下:
default-character-set=utf8
【注】修改了配置文件,必须重启mysql服务
在mysql客户端中,验证当前的字符集是否修改为utf8
show variables like 'character%'
2. MySQL数据库操作指令
2.1 SQL的分类
DDL(Data Definition Language):数据定义语言,用来定义数据库对象:数据库、表、视图、索引、存储过程、触发器、函数等;
DML(Data Manipulation Language):数据操作语言,用来定义数据库记录(数据);
DCL(Data Control Language):数据控制语言,用来定义访问权限和安全级别;
DQL(重要)(Data Query Language):数据查询语言,用来查询记录(数据)。
TCL (Transaction Control Language): 事务控制语言,设置事务的隔离级别。
2.2 MySQL操作数据库
create database [if not exists] <数据库名>; 创建数据库
use <database>; 打开已存在的数据库
show databases; 查看所有数据库
drop database if exists <数据库名>; 删除数据库
show create 类型 对象名; 查看创建数据库或其它对象的sql语句
3. 数据库的DDL操作
在执行数据库的ddl操作之前,必须先打开数据库
3.1 创建表
create table 表名(字段1 字段类型,字段2 字段类型,...字段n 字段类型);
将sql语句写在外部的文件中,在mysql客户端上,可以通过source sql文件路径
快速执行sql文件中的语句
3.2 mysql的数据类型
int、integer:整型
double、float、decimal:浮点型
char:固定长度字符串类型
varchar:可变长度字符串类型,字符的最大长度默认为255
text:字符串文本类型,不限制字符内容的大小
clob:字符类型,比text表示的内容要大
blob:字节类型,存储是字节数据(二进制数据)
date:日期类型,格式为:yyyy-MM-dd
time:时间类型,格式为:hh:mm:ss
timestamp:时间戳类型 yyyy-MM-dd hh:mm:ss会自动赋值
datetime:日期时间类型 yyyy-MM-dd hh:mm:ss
3.3 查看所有表及表结构
查看当前数据库下的所有表
show tables;
查看某一张表的结构
describe <表名>;
desc <表名>;
3.4 修改表结构
rename table <原表名> to <新表名>; 修改表名
alter table <表名> add [column] 字段名 数据类型[(长度)][约束语句][FIRST|AFTER col_name]; 添加字段
alter table <表名> modify <字段名> 数据类型 [FIRST|AFTER col_name]; 修改字段
alter table <表名> change <原字段名> <新字段名> 数据类型; 修改字段名
alter table <表名> drop [column] 字段名; 删除字段
create table <表名> like <已存在的表名>; 备份表
drop table [if exists] <表名>; 删除表
4. 数据库的DML操作
4.1 insert操作
如果是char、varchar、date、time、timestamp类型的字段值,则需要使用单引号
''
语法1:单条记录插入
insert into <表名>[(字段1,字段2,...)]
values(值1,值2,...);
在插入数据时,如果只有表名时,插入数据的字段顺序按desc 表名
查看的字段顺序
语法2:批量数据插入
insert into <表名>[(字段1,字段2,...)] values
(值1,值2,...),(值1,值2,...),(值1,值2,...);
插入数据成功之后,可以查询表的数据:
select * from 表名;
4.2 复制表数据
方式1:使用现有表的数据创建新表
create table <新表名>
select *|字段1,字段2,.... from 已存在的表名;
方式2:先创建表结构,再插入数据
create table <新表名> like <已存在的表名>;
insert into <新表名>
select * from <已存在的表名>;
4.3 update语句
用于更新(变更)已现表中的数据
语法:
update <表名>
set 字段名=新值 [,字段2=新值2,..字段n=新值n]
[where 条件表达式];
新值:可以是常数(数值、字符串),字段名(代表是某一行的字段的值)
条件表达式:字段名或常数 运算符 常量或字段名
关系运算符:>,<,>=,<=,!=,=
逻辑运算符:and与,or或,not取非
is运算符:is null(是null),is not null(非null)
like运算符:%任意零或多个任意字符,_任意一个字符
4.4 delete语句
用于删除表中的数据
语法:
delete from <表名>
[where 条件表达式];
5. 数据库的DQL操作
DQL主要是SQL的查询语句
简化sql查询语法:
select [distnct] *|字段1或表达式1 [[as] 别名], ...
from 表名1 [[as] 表别名 [, 表名2 ...]]
[ [[natural] left|right] join 表名2 [别名] [on (条件1 [, 条件2...])] ]
[where 条件表达式]
[group by 分组表达式]
[having 分组语句中字段的条件表达式]
[order by 排序语句]
[limit 分页语句]
[for update]
5.1 条件查询
条件表达式:字段名或常数 运算符 常数或字段名
- 关系运算符:>,<,>=,<=,!=,=,<>(不等于)
- 逻辑运算符:and与,or或,not取非
- is运算符:is null(是null),is not null(非null)
- like运算符:
%
任意零或多个任意字符,_
任意一个字符 - in运算符:in(值1,值2,...)包含在()中所有可能
- between-and运算符:between 开始值 and 结束值
5.2 排序与分页查询
排序:order by 字段名或表达式[asc|desc] asc默认(从小到大),desc降序(从大到小)
分页:limit n 只显示前n条数据;limit offset,size从行号offset开始,显示size行数
5.3 聚合分组查询
5.3.1 聚合函数
聚合函数是用来做纵向运算的函数:mysql的函数调用时,不区分大小写的
COUNT():统计指定列不为NULL的记录行数
MAX():计算指定列的最大值,如果指定列是字符串类型,那么使用字符串排序运算
MIN():计算指定列的最小值,如果指定列是字符串类型,那么使用字符串排序运算
SUM():计算指定列的数值和,如果指定列类型不是数值类型,那么计算结果为0
AVG():计算指定列的平均值,如果指定列类型不是数值类型,那么计算结果为0
5.3.2 分组语句
分组语句的作用是依据给定的字段,按相同字段的值划分区域,对区域中的数据可以进行聚合操作计算
分组语句:group by 字段或表达式[,字段2或表达式2,...]
如果select子句中存在聚合函数时,未使用聚合函数的字段必须放在分组子句中,聚合函数遇到null值时,跳过
5.3.3 having子句
having子句是条件语句,主要负责对分组聚合的函数值进行条件处理
另外,having也可当作where子句使用
having和order by子句中可以使用列或表达式的别名
5.4 join子句
join子句主要完成表与表之间的连接(联接)
join连接表的方式:
-
内连接(等值条件连接)两个表的交集的数据(依赖连接条件,相同的字段值)
join 表名2 on 连接条件
-
左外连接(除了内连接的数据之外,包含左表的其它数据)
left join 表名2 on 连接条件
-
右外连接(除了内连接的数据之外,包含右表的其它数据)
right join 表名2 on 连接条件
-
自然连接(两个表中找到相同的唯一列的值进行等值连接)
natural [left|right] join 表名2
5.5 union子句
union子句是将两个及以上的sql查询的结果拼接(按行)在一起,第一个查询的字段名为拼接之后的所有结果的字段名
要求:所有的查询的字段个数保持一致
union自动去重行数据,如果想保持相同行的数据,则需要使用union all
5.6 子查询
子查询语句主要用于另一个查询句中,作为条件(单值、单行单列)、连接表
6. 数据约束和域完整性
数据约束(参考完整性)用于数据取值的一种约束
6.1 数据约束的类型
1)主键约束:primary key
约束列值在整个列域(整列,即一列中所有行的数据)中是唯一的,不重复的且非空
2)唯一约束:unique,约束列值在整列域中唯一且不可重复
3)外键约束:foreign key,列值的范围取值外表中主键的值
4)非空约束:not null,列值不能为null
5)自增:auto_increment,同主键一起使用,要求主键字段的数据类型为intege或int
新插入一行时,自增字段会自动加1
6)默认值:default,插入数据时,如果有默认值的列没有给定数据时,自动采用默认值
7)检查约束:check(表达式) 当数据插入到表时,会检查数据是否符合check中表达式的条件
6.2 创建表时添加约束
- 在字段数据类型后面 加约束
- 在最后一个字段后,声明约束
外键约束的on delete和on update
constrains [约束名] foreign key (字段名)
references 外表名 (外表字段名)
[on delete restrict|cascade|set null|no action|set default ]
[on update restrict|cascade]
默认为restrict,表示外键值存在时,主表中主键所在行数据不能被删除
cascade,表示主表中的主键所在行删除,则将外键值所在行也会级联删除
set null,表示主表中的主键所在行删除,则将外键值设置为null
6.3 修改表时添加约束
语法:
添加主键约束:alter table 表名 add [constraint [约束名]] primary key(列名,...)
如果没有声明主键的约束名,即为列名
添加唯一约束:alter table 表名 add [constraint [约束名]] unique(列名,...)
添加非空|自增 约束:alter table 表名 modify 列名 数据类型 not null|auto_increment
添加外键约束:alter table 表名 add [constraint [约束名]] foreign key (列名)
reference 外表名 (外表字段名)
[on delete restrict|cascade|set null|no aciton|set default]
[on update restrict|cascade]
6.4 删除表中的约束
删除主键约束:alter table 表名 drop primary key
【注意】如果主键字段是自增的,则先删除自增,再删除主键约束;如果主键约束存在某个表的外键约束时,则先删除外键约束
删除唯一约束:alter table 表名 drop index 列名
删除非空|自增|默认值约束:alter table 表名 modify 列名 数据类型 null
删除外键约束:alter table 表名 drop foreign key 索引名
7. MySQL进阶
7.1 view试图
视图是一张虚拟表,也是一个查询记录
视图是用于存储SQL查询语句,视图也可以作为子查询使用,目的是快速便捷地查询数据。
分类:简单(单一表的)视图、复杂(多表连接)视图
【注意】简单视图支持insert、delete、update等语句,而复杂的视图不支持DML操作
语法:
create [or replace] view <视图名> as select 查询语句;
drop view [if exists] <视图名>;
7.2 索引
7.2.1 索引概念
查看当前mysql数据库支持的引擎
show engines;
【注】mysql常见的数据库引擎:CSV,MEMERY,innoDB,MuLSAM
索引:是一种存储结构(默认采用B树),用于存储表中(1或多)列值的数据结构
索引的作用:加快查询速度
索引的分类:主键索引、唯一索引、普通索引、组合索引、全文索引、分区索引等。
主键索引:由主键约束添加时,自动创建的
唯一索引:唯一约束会自动创建唯一索引,当然也可以自己创建
索引的存储结构: b树(BTREE) (InnoDB, MyISAM, Memory), HASH (Memory, NDB)
7.2.2 索引的创建与删除
语法:
创建:
CREATE [UNIQUE | FULLTEXT | SPATIAL] INDEX index_name
[ USING {BTREE | HASH} ]
ON tb1_name(key_part,...);
删除:
DROP INDEX index_name ON tb1_name;
检查查询sql语句中是否使用索引:
explain sql查询语句;
一张表有20个字段,问可以创建多少个索引?
问答1: 原则上一张表上没有限制创建索引的个数, 但索引创建的多了,会影响DML语句执行的效率。作为查询条件,使用最为频繁的字段都会创建索引。
问答2: 不考虑DML性能问题,单说可以创建多少个索引,除主键约束或索引包含的字段之外,每一个字段都可以创建一个普通的索引,也可以为多个字段创建组合索引。假设,主键字段只有一个,那么至少 可以创建19个普通索引, 还可以2个字段组创建等...
7.3 触发器
触发器:在某一个DML操作(事件)发生时,DBMS系统自动触发事先定义的操作。定义触发器,即定义事件发生前后的操作
7.3.1 创建触发器
-- delimiter 声明sql语句结束的符号
delimiter &&
create trigger <触发器名称>
{before|after} {insert |update|delete}
on <表名> for each row
begin
-- sql语句
end &&
delimiter;
触发器的body(begin...end)中可以使用NEW
(新的一行数据的游标)和OLD
(更新或删除时的一行的数据游标)
7.3.2 删除触发器
drop trigger [if exists] <触发器名>;
7.4 常用函数
MySQL中提供的函数可用于select子句,where子句,insert/update/delete子句,order by,group by都可以使用
函数的特点:传入参数,并返回结果,函数的结果可以看成是一个常数
7.4.1 数值相关
round(字段或常数 [, 小数位置]) 四舍五入
ceil(字段或常数) 上行取整
floor(字段或常数) 下行取整
abs(字段或常数) 绝对值
pow(字段或常数,次幂数) 求次幂
sqrt(字段或常数) 求平方根
7.4.2 字符相关
CHAR_LENGTH() 字符个数
LENGTH() 字节个数
CONCAT(str1,str2,...) 字符串拼接
CONCAT_ws(sparator,str1,str2,...) 带分隔符的字符串拼接
REPLACE(str1,old_str,new_str)替换str1中old_str的内容为new_str
TRIM()删除两边空白
LOWER()小写字母转换
UPPER()大写字母转换
REVERSE()反转字符
HEX()将字符串或字节数据转化为十六进制的字符串
UNHEX()将十六进制反转化原字节或字符串
uuid()作为字段类型为字符类型时,通过使用uuid()的值作为主键值
uuid()是全球唯一的标识符
不可逆向的加密:md5,sha1
aes|des_encrypt(str,key)加密函数
aes|des_decrypt(key_str,key)解密函数
7.4.3 日期相关
CURDATE()/CURRENT_DATE(),CURRENT_DATE 当前日期
CURTIME()/CURRENT_TIME(),CURRENT_TIME 当前时间
now() /CURRENT_TIMESTAMP(),CURRENT_TIMESTAMP 当前日期时间
date(str) 提取str中的日期内容
time(str) 提取str中的时间内容
year(date|str) 提取日期中的年
month(date|str) 提取日期中的月
day(date|str)/DAYOFMONTH() 提取日期中的日
DAYOFYEAR() 一年中第几天
HOUR(date|time|str) 提取提取时间中的小时
MINUTE(date|time|str) 提取时间中的分钟
SECOND(date|time|str) 提取时间中的秒
week(date) 提取一年中的第几周
STR_TO_DATE(str,format) 将特定格式的日期字符串转化为日期
DATE_FORMAT(date,format) 将日期按format格式转化为字符串
DATEDIFF(date1,date2) 计算两个日期之间的相差的天数
TIMEDIFF(time1,time2) 计算两个时间之间的相差的描述
DATE_ADD(date,INTERVAL n unit) 为一个日期添加n的单位数
DATE_SUB(date,INTERVAL n unit) 为一个日期减少n的单位数
unit: YEAR,MONTH,DAY,HOUR,MINUTE,SECOND,DAY_MINUTE,YEAR_MINUTE...
日期格式:
%S,%s 两位数字形式的秒(00,01...)
%I,%i 两位数字形式的分
%H 两位数字的小时,24小时
%h 两位数字的小时,12小时
%k 数字形式的小时,24小时(0,1,2,...)
%i 数字形式的小时,12小时
%T 24小时的时间形式(hh:mm:ss)
%r 12小时的时间形式(hh:mm:ss AM 或hh:mm:ss PM)
%p AM或PM
%W 一周中每一天的名称(Sunday,...)
%a 一周中每一天名称的缩写(Sun,...)
%d 两位数字表示月中的天数(00,01,...)
%e 数字形式表示月中的天数(1,2,...)
%D 英文后缀表示月中的天数(1st,...)
%w 艺术字形式表示周中的天数(0 = Sunday, 1=Monday, …, 6=Saturday)
%j 以三位数字表示年中的天数(001,002,...366)
%U 周(0,1,...),其中Sunday为周中的第一天
%u 周,其中Monday为周中的第一天
%M 月名(January,February,...)
%b 缩写的月名(Jan,Feb,...)
%m 两位数字表示的月份(01,...12)
%c 数字表示的月份(1,2,...12)
%Y 四位数字表示的年份
%y 两位数字表示的年份
%% 直接值“%”
--将日期转化为xxxx年x月x日
select date_format(now(),'%Y年%m月%d日');
7.4.4 流程控制相关
1. if函数
IF(expr1,expr2,expr3) 如果expr1为真,返回expr2,反之返回expr3
IFNULL(expr1,expr2) 如果expr1非空,则返回expr1,反之返回expr2
NULLIF(expr1,expr2) 如果expr1和expr2相等,返回null,反之返回expr1
2. case表达式
常用于select子句
--等值条件
CASE value WHEN compare_value THEN result [WHEN compare_value THEN result ...] [ELSE result] END
---非等值条件
CASE WHEN condition THEN result [WHEN condition THEN result ...] [ELSE result] END
8. C操作MySQL数据库
Linux下的MySQL数据库的操作需要安装c的mysql操作库:
Ubuntu16/18:
sudo apt-get install libmysqld-dev
Ubuntu20:
sudo apt-get install libmysqlclient-dev
8.1 api
引入头文件
#include <mysql/mysql.h>
编译时要加
-lmysqlclient
8.1.1 初始化
获取或初始化mysql结构
MYSQL *mysql_init(MYSQL *mysql);
功能:分配或初始化与mysql_real_connect()相适应的MYSQL对象
返回值:如果无足够内存以分配新的对象,返回NULL
8.1.2 建立连接
mysql_real_connect()尝试与运行在主机上的MySQL数据库引擎建立连接
NYSQL * mysql_real_connect(MYSQL *mysql,
const char *host,
const char *user,
const char *passwd,
const char *db,
unsigned int port,
const char *unix_socket,
unsigned long client_flag);
参数:
- mysql:MySQL句柄
- host:主机名或ip地址,如果为"NULL"或"localhost","127.0.0.1",连接将被视为与本地主机的连接
- user:MySQL登陆的用户名
- passwd:MySQL登陆的密码
- db:数据库名称
- port:MySQL服务器监听客户端连接到来的端口号(默认:3306)
- unix_socket:如果unix_socket不是NULL,该字符描述了应使用的套接字或命名管道(默认为NULL)
- client_flag:通常为0,可以更改其值,以允许特定功能(详情见手册)
返回值:
- 如果连接成功,返回MYSQL*连接句柄,返回值与第1个参数的值相同
- 如果连接失败,返回NULL
如果安装mysql数据库时,没有提示设置root口令,则需要进入mysql的客户端(Shell)中
方案1:修改root的口令
mysql> use mysql;
mysql> alter user 'root'@'localhost' identified with mysql_native_password by 'root';
方案2:
mysql> use mysql;
mysql> create user 'disen'@'%' identified with mysql_native_password by 'disen';
mysql> grant all on *.* to 'disen'@'%';
mysql> flush privileges;
8.1.3 执行语句
执行由“query”指向的SQL查询,它应是字符串长度字节“long”。正常情况下,字符串必须包含1条SQL语句,而且不应为语句添加终结分号(;)或“\g”。如果允许多语句执行,字符串可包含由分号隔开的多条语句
int mysql_real_query(MYSQL *mysql,const char * query,unsigned long length);
参数:
- mysql:句柄
- query:需要执行的sql语句
- length:需要执行的语句的长度
返回值:成功0,失败非0
8.1.4 获取语句执行的结果
对于成功检索了数据的每个查询(SELECT、SHOW、DESCRIBE、EXPLAIN、CHECK TABLE等),必须调用mysql_store_result()或mysql_use_result()。对于其他查询,不需要调用mysql_store_result()或mysql_use_result,但是如果在任何情况下均调用了mysql_store_result(),它也不会导致任何伤害或性能降低
MYSQL_RES *mysql_store_result(MYSQL *mysql);
8.1.5 获取结果集的列数
unsigned int mysql_num_fields(MYSQL_RES *result);
8.1.6 获取结果集的行数
my_ulonglong mysql_num_rows(MYSQL_RES *result);
8.1.7 获取结果集的列名称
返回采用MYSQL_FILED结构的结果集的列,重复调用该函数,以检索关于结果集中所有列的信息。未剩余字段时,mysql_fetch_field()返回NULL
MYSQL_FILED * mysql_fetch_field(MYSQL_RES *result);
8.1.8 获取结果集的每列数据
检索结果集的下一行。在mysql_store_result()之后使用时,如果没有要检索的行,mysql_fetch_row()返回NULL
MYSQL_ROW mysql_fetch_row(MYSQL_RES *result);
8.1.9 释放结果集
释放由mysql_store_result()、mysql_use_result()、mysql_list_dbs()等为结果集分配的内存。完成对结果集的操作后,必须调用mysql_free_result()释放结果集使用的内存
void mysql_free_result(MYSQL_RES *result);
8.1.10 释放MYSQL句柄
关闭前面打开的连接。如果句柄是由mysql_init()或mysql_connect()自动分配的,mysql_close()还将接触分配由mysql指向的连接句柄
void mysql_close(MYSQL *mysql);
8.2 事务相关的函数
事务:做一项任务需要很多个步骤完成,每一个步骤都属于某一任务,此任务中的所有步骤要么全成功,要么全失败,称之为任务的开始和结束是在一个事务中完成的
事务四个特性:ACID
-
Atomicity(原子性):事务中的所有任务要么全成功,要么全失败
-
Consistency(一致性):事务的开始和结束,数据保持一致的
-
Isolation(隔离性):事务和事务之间事先相互的隔离的,即事务之间互不影响
事务的隔离性具有四个事务的隔离级别:
-
读未提交:事务可以读取另一个事务未提交的数据
【脏读】【不可重复读】【幻读】
-
读已提交:事务中可以读取另一个事务已提交的数据
【不可重复读】【幻读】
-
可重复读:事务中可以多次读取数据,与事务开启之前的事务是保持一致的。
【幻读】
-
串行化:事务的操作一个接一个的操作,类似于多任务的同步
在mysql中设置事务的隔离级别:
set session transaction isolation level read uncommitted; set session transaction isolation level read committed; set session transaction isolation level repeatable read; set session transaction isolation level serializable;
-
-
Durability(持久化):数据提交之后,则数据会持久性地存储
mysql_autocommit(MYSQL *socket); // 自动提交事务 mysql_commit(MYSQL *socket); // 提交事务 mysql_rollback(MYSQL *socket); // 回滚事务
【注】
- 脏读:A事务正在修改数据但未提交,此时B事务去读取此条数据,B事务读取的是未提交的数据,A事务回滚。
- 不可重复读: A事务中两次查询同一数据的内容不同,B事务间在A事务两次读取之间更改了此条数据。
- 幻读:在同一事务中两次相同查询数据的条数不一致,例如第一次查询查到5条数据,第二次查到8条数据,这是因为在两次查询的间隙,另一个事务插入了3条数据。