MySQL事务概述和隔离级别
- 事务
- 事务的特征
- 事务的控制语句
- 事务的生命周期
- 事务执行过程
- ACID特性
- 原子性(A)
- 一致性(C)
- 隔离性(I)
- 持久性(D)
- 隔离级别
- 不同隔离级别并发异常
- 总结
事务
事务的前提:并发连接访问。MySQL的事务就是将多条SQL语句作为整体进行执行。
事务的定义:用户定义的一系列SQL操作,这些操作要么都执行,要么都不执行,是一个不可分割的单位。
事务的本质是并发控制的单元,是用户定义的一个操作序列。这些操作要么都做,要么都不做,是一个不可分割的工作单元。
事务的目的:事务将数据库从一种一致性状态转换为另一种一致性状态;保
证系统始终处于一个完整且正确的状态。
事务的组成:事务可由一条非常简单的SQL语句组成,也可以由一组复杂的SQL语句组成。
事务的特征
在数据库提交事务时,可以确保要么所有修改都已经保存,要么所有修改都不保存。事务是访问并更新数据库各种数据项的一个程序执行单元。
在 MySQL innodb 下,单条语句都具备事务;可以通过 set autocommit = 0; 设置当前会话手动提交。
事务的控制语句
-- 显示开启事务
START TRANSACTION | BEGIN
-- 提交事务,并使得已对数据库做的所有修改持久化
COMMIT
-- 回滚事务,结束用户的事务,并撤销正在进行的所有未提交的修改
ROLLBACK
-- 创建一个保存点,一个事务可以有多个保存点
SAVEPOINT identifier
-- 删除一个保存点
RELEASE SAVEPOINT identifier
-- 事务回滚到保存点
ROLLBACK TO [SAVEPOINT] identifier
事务的生命周期
(1)begin … commit。从开始事务到提交事务。
(2)begin … rollback。从开始事务到事务回滚。
事务执行过程
#mermaid-svg-OVpA5SgtqrpBnBLP {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-OVpA5SgtqrpBnBLP .error-icon{fill:#552222;}#mermaid-svg-OVpA5SgtqrpBnBLP .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-OVpA5SgtqrpBnBLP .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-OVpA5SgtqrpBnBLP .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-OVpA5SgtqrpBnBLP .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-OVpA5SgtqrpBnBLP .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-OVpA5SgtqrpBnBLP .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-OVpA5SgtqrpBnBLP .marker{fill:#333333;stroke:#333333;}#mermaid-svg-OVpA5SgtqrpBnBLP .marker.cross{stroke:#333333;}#mermaid-svg-OVpA5SgtqrpBnBLP svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-OVpA5SgtqrpBnBLP .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-OVpA5SgtqrpBnBLP .cluster-label text{fill:#333;}#mermaid-svg-OVpA5SgtqrpBnBLP .cluster-label span{color:#333;}#mermaid-svg-OVpA5SgtqrpBnBLP .label text,#mermaid-svg-OVpA5SgtqrpBnBLP span{fill:#333;color:#333;}#mermaid-svg-OVpA5SgtqrpBnBLP .node rect,#mermaid-svg-OVpA5SgtqrpBnBLP .node circle,#mermaid-svg-OVpA5SgtqrpBnBLP .node ellipse,#mermaid-svg-OVpA5SgtqrpBnBLP .node polygon,#mermaid-svg-OVpA5SgtqrpBnBLP .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-OVpA5SgtqrpBnBLP .node .label{text-align:center;}#mermaid-svg-OVpA5SgtqrpBnBLP .node.clickable{cursor:pointer;}#mermaid-svg-OVpA5SgtqrpBnBLP .arrowheadPath{fill:#333333;}#mermaid-svg-OVpA5SgtqrpBnBLP .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-OVpA5SgtqrpBnBLP .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-OVpA5SgtqrpBnBLP .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-OVpA5SgtqrpBnBLP .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-OVpA5SgtqrpBnBLP .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-OVpA5SgtqrpBnBLP .cluster text{fill:#333;}#mermaid-svg-OVpA5SgtqrpBnBLP .cluster span{color:#333;}#mermaid-svg-OVpA5SgtqrpBnBLP div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-OVpA5SgtqrpBnBLP :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}
引擎层
server层
Y
N
写undolog,存储回滚指针和事务ID
记录所在的目标页,是否存在于内存中
更新内存
唯一索引
找到数据,判断数据冲突与否
普通索引
找到数据
更新内存
判断冲突与否,更新
唯一索引
将数据页从磁盘读入内存
普通索引
在change buffer更新记录
change buffer会异步将更新 同步到磁盘
写入redolog
写入binlog,用于主从复制
刷redolog盘,处于commit-prepare阶段
提交事务
刷binlog盘,处于commit-commit阶段
执行器
优化器
分析器
连接器
更新记录的SQL命令
ACID特性
原子性(A)
事务操作要么都做(提交),要么都不做(回滚)。事务是访问并更新数据库各种数据项的一个程序执行单元,是不可分割的工作单位。
MySQL通过 undolog 来实现回滚操作。undolog 记录的是事务每步具体操作,当回滚时,回放事务具体操作的逆运算。
事务包含的全部操作是一个不可分割的整体,要么全部执行,要么全部不执行。
假如数据库的事务执行到一半,数据库突然宕机,那么宕机前执行的SQL语句不会生效,undolog 会把它回滚到事务开始之前的数据。
undolog是会记录DML操作的日志,回滚就是依赖undolog日志,用来帮助回滚事务的。
一致性(C)
事务的前后,所有的数据都保持一个一致的状态,不能违反数据的一致性检测(完整性约束检查)。
一致性指事务将数据库从一种一致性状态转变为下一种一致性的状态,在事务执行前后,数据库完整性约束没有被破坏;一
个事务单元需要提交之后才会被其他事务可见。例如:一个表的姓名是唯一键,如果一个事务对姓名进行修改,但是在事务
提交或事务回滚后,表中的姓名变得不唯一了,这样就破坏了一致性。
一致性 有数据库完整约束和逻辑上一致性。
一致性由原子性、隔离性以及持久性共同来维护的。
隔离性(I)
描述:各个事务之间互相影响的程度。
目的:主要规定多个事务访问同一数据资源,各个事务对该数据资源访问的行为,不同的隔离性是应对不同的现象(脏读、
不可重复读、幻读)。
事务的隔离性要求每个读写事务的对象对其他事务的操作对象能相互分离,并发事务之间不会相互影响,设定了不同程度的
隔离级别,通过适度破环一致性,得以提高性能。
隔离性 通过 MVCC和 锁来实现。MVCC是多版本并发控制,主要解决一致性非锁定读,通过记录和获取行版本,而不是使用锁来限制读操作,从而实现高效并发读性能。锁用来处理并发 DML 操作。
数据库中提供粒度锁的策略,针对表(聚集索引 B+ 树)、页(聚集索引 B+ 树叶子节点)、行(叶子节点当中某一段记录行)三种粒度加锁。
持久性(D)
事务一旦完成,要将数据所做的变更记录下来,包括数据存储和多副本的网络备份。
事务提交后,事务 DML 操作将会持久化(写入 redolog 磁盘文件 哪一个页 页偏移值 具体数据);即使发生宕机等故障,数据
库也能将数据恢复。
redolog 记录的是物理日志。
隔离级别
隔离级别的目的是提升并发性能。隔离级别越高,事务之间的影响级别最低,同时效率也是最低。隔离级别中,所有写操作都要加锁,唯一不同的是读操作,不同的隔离级别读操作的加锁程度不一样。
ISO 和 ANIS SQL 标准制定了四种事务隔离级别的标准,各数据库厂商在正确性和性能之间做了妥协,并没有严格遵循这些标准。MySQL innodb默认支持的隔离级别是 REPEATABLE READ。
降低隔离级别都会影响一致性,准确的说是影响逻辑上的一致性。
(1) READ UNCOMMITTED。读未提交;该级别下读不加锁,写加排他锁,写锁在事务提交或回滚后释放锁。读未提交可能会造成逻辑上一致性的问题;比如,可能读到其他事务还没提交的数据,也就是读到其他事务的中间状态。
(2)READ COMMITTED。读已提交;这是大部分数据库采用的隔离级别。从该级别后支持 MVCC (多版本并发控制),也就是提供一致性非锁定读;此时读取操作读取历史快照数据。该隔离级别下读取历史版本的最新数据,所以读取的是已提交的数据。这种隔离级别有可能存在一个问题:两个事务,一个事务有两个select读取同一行数据,另一个事务在两个select之间修改该行数据并提交,这就造成两次读的数据不一致。
#mermaid-svg-NyHHkWcM9uIguZ3h {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-NyHHkWcM9uIguZ3h .error-icon{fill:#552222;}#mermaid-svg-NyHHkWcM9uIguZ3h .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-NyHHkWcM9uIguZ3h .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-NyHHkWcM9uIguZ3h .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-NyHHkWcM9uIguZ3h .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-NyHHkWcM9uIguZ3h .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-NyHHkWcM9uIguZ3h .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-NyHHkWcM9uIguZ3h .marker{fill:#333333;stroke:#333333;}#mermaid-svg-NyHHkWcM9uIguZ3h .marker.cross{stroke:#333333;}#mermaid-svg-NyHHkWcM9uIguZ3h svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-NyHHkWcM9uIguZ3h .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-NyHHkWcM9uIguZ3h .cluster-label text{fill:#333;}#mermaid-svg-NyHHkWcM9uIguZ3h .cluster-label span{color:#333;}#mermaid-svg-NyHHkWcM9uIguZ3h .label text,#mermaid-svg-NyHHkWcM9uIguZ3h span{fill:#333;color:#333;}#mermaid-svg-NyHHkWcM9uIguZ3h .node rect,#mermaid-svg-NyHHkWcM9uIguZ3h .node circle,#mermaid-svg-NyHHkWcM9uIguZ3h .node ellipse,#mermaid-svg-NyHHkWcM9uIguZ3h .node polygon,#mermaid-svg-NyHHkWcM9uIguZ3h .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-NyHHkWcM9uIguZ3h .node .label{text-align:center;}#mermaid-svg-NyHHkWcM9uIguZ3h .node.clickable{cursor:pointer;}#mermaid-svg-NyHHkWcM9uIguZ3h .arrowheadPath{fill:#333333;}#mermaid-svg-NyHHkWcM9uIguZ3h .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-NyHHkWcM9uIguZ3h .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-NyHHkWcM9uIguZ3h .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-NyHHkWcM9uIguZ3h .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-NyHHkWcM9uIguZ3h .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-NyHHkWcM9uIguZ3h .cluster text{fill:#333;}#mermaid-svg-NyHHkWcM9uIguZ3h .cluster span{color:#333;}#mermaid-svg-NyHHkWcM9uIguZ3h div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-NyHHkWcM9uIguZ3h :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}
事务1,begin
读数据,select * from table where id=1;
其他操作
再次读数据,select * from table where id=1;
其他操作
提交事务,commit
事务2,begin
修改数据,update table set name='new' where id=1;
提交事务,commit
(3)REPEATABLE READ。可重复读(RR);该级别下也支持 MVCC,此时读取操作读取事务开始时的版本数据。这个隔离级别可以解决READ COMMITTED逻辑上的问题,避免了两次读取的数据不一致。这个也存在一个逻辑问题,就是如果是两次范围读取数据,而第二次读取数据使用了for update(加锁),就会造成两次读的行记录不一样,即幻读。
#mermaid-svg-aumgtbw82KyS0olt {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-aumgtbw82KyS0olt .error-icon{fill:#552222;}#mermaid-svg-aumgtbw82KyS0olt .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-aumgtbw82KyS0olt .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-aumgtbw82KyS0olt .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-aumgtbw82KyS0olt .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-aumgtbw82KyS0olt .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-aumgtbw82KyS0olt .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-aumgtbw82KyS0olt .marker{fill:#333333;stroke:#333333;}#mermaid-svg-aumgtbw82KyS0olt .marker.cross{stroke:#333333;}#mermaid-svg-aumgtbw82KyS0olt svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-aumgtbw82KyS0olt .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-aumgtbw82KyS0olt .cluster-label text{fill:#333;}#mermaid-svg-aumgtbw82KyS0olt .cluster-label span{color:#333;}#mermaid-svg-aumgtbw82KyS0olt .label text,#mermaid-svg-aumgtbw82KyS0olt span{fill:#333;color:#333;}#mermaid-svg-aumgtbw82KyS0olt .node rect,#mermaid-svg-aumgtbw82KyS0olt .node circle,#mermaid-svg-aumgtbw82KyS0olt .node ellipse,#mermaid-svg-aumgtbw82KyS0olt .node polygon,#mermaid-svg-aumgtbw82KyS0olt .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-aumgtbw82KyS0olt .node .label{text-align:center;}#mermaid-svg-aumgtbw82KyS0olt .node.clickable{cursor:pointer;}#mermaid-svg-aumgtbw82KyS0olt .arrowheadPath{fill:#333333;}#mermaid-svg-aumgtbw82KyS0olt .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-aumgtbw82KyS0olt .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-aumgtbw82KyS0olt .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-aumgtbw82KyS0olt .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-aumgtbw82KyS0olt .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-aumgtbw82KyS0olt .cluster text{fill:#333;}#mermaid-svg-aumgtbw82KyS0olt .cluster span{color:#333;}#mermaid-svg-aumgtbw82KyS0olt div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-aumgtbw82KyS0olt :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}
事务1,begin
读数据,select * from table where id>10;
其他操作
再次读数据,select * from table where id>10 for update;
其他操作
提交事务,commit
事务2,begin
新增数据,insert into table (`name`) values ('new');
提交事务,commit
(4)SERIALIZABLE。可串行化;该级别下给读加了共享锁;所以事务都是串行化的执行,此时隔离级别最严苛。
命令
-- 设置隔离级别
SET [GLOBAL | SESSION] TRANSACTION ISOLATION
LEVEL REPEATABLE READ;
-- 或者采用下面的方式设置隔离级别
SET @@tx_isolation = 'REPEATABLE READ';
SET @@global.tx_isolation = 'REPEATABLE READ';
-- 查看全局隔离级别
SELECT @@global.tx_isolation;
-- 查看当前会话隔离级别
SELECT @@session.tx_isolation;
SELECT @@tx_isolation;
-- 手动给读加 S 锁
SELECT ... LOCK IN SHARE MODE;
-- 手动给读加 X 锁
SELECT ... FOR UPDATE;
-- 查看当前锁信息
SELECT * FROM information_schema.innodb_locks;
不同隔离级别并发异常
准备,建立一张数据表:
DROP TABLE IF EXISTS `account_t`;
CREATE TABLE `account_t`(`id` int(11) not NULL,`name` VARCHAR(255) DEFAULT NULL,`money` INT(11) DEFAULT 0,PRIMARY KEY (`id`),KEY `idx_name` (`name`)
)ENGINE=INNODB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8;select * from `account_t`;
INSERT into `account_t` values(1,'c',1000),(2,'b',1000),(3,'a',1000);
脏读
一个事务读到另一个未提交事务修改的数据。事务(A)可以读到另外一个事务(B)中未提交的数据;也就是事务A读到脏数据。
-- 事务1
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;BEGIN
-- 先执行查询
select money from `account_t` WHERE name='a';
-- 事务1更改数据后,再次执行查询
select money from `account_t` WHERE name='a';ROLLBACK;
-- 事务2
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;BEGIN
-- 事务1先执行查询,再修改数据
UPDATE `account_t` set money=money-100 WHERE name='a';UPDATE `account_t` set money=money+100 WHERE name='b';COMMIT;
ROLLBACK;
#mermaid-svg-837BWo9JsvN77VtP {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-837BWo9JsvN77VtP .error-icon{fill:#552222;}#mermaid-svg-837BWo9JsvN77VtP .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-837BWo9JsvN77VtP .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-837BWo9JsvN77VtP .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-837BWo9JsvN77VtP .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-837BWo9JsvN77VtP .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-837BWo9JsvN77VtP .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-837BWo9JsvN77VtP .marker{fill:#333333;stroke:#333333;}#mermaid-svg-837BWo9JsvN77VtP .marker.cross{stroke:#333333;}#mermaid-svg-837BWo9JsvN77VtP svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-837BWo9JsvN77VtP .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-837BWo9JsvN77VtP .cluster-label text{fill:#333;}#mermaid-svg-837BWo9JsvN77VtP .cluster-label span{color:#333;}#mermaid-svg-837BWo9JsvN77VtP .label text,#mermaid-svg-837BWo9JsvN77VtP span{fill:#333;color:#333;}#mermaid-svg-837BWo9JsvN77VtP .node rect,#mermaid-svg-837BWo9JsvN77VtP .node circle,#mermaid-svg-837BWo9JsvN77VtP .node ellipse,#mermaid-svg-837BWo9JsvN77VtP .node polygon,#mermaid-svg-837BWo9JsvN77VtP .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-837BWo9JsvN77VtP .node .label{text-align:center;}#mermaid-svg-837BWo9JsvN77VtP .node.clickable{cursor:pointer;}#mermaid-svg-837BWo9JsvN77VtP .arrowheadPath{fill:#333333;}#mermaid-svg-837BWo9JsvN77VtP .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-837BWo9JsvN77VtP .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-837BWo9JsvN77VtP .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-837BWo9JsvN77VtP .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-837BWo9JsvN77VtP .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-837BWo9JsvN77VtP .cluster text{fill:#333;}#mermaid-svg-837BWo9JsvN77VtP .cluster span{color:#333;}#mermaid-svg-837BWo9JsvN77VtP div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-837BWo9JsvN77VtP :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}
事务2
事务1
1
3
1
2
3
4
5
5
begin
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
select money from `account_t` WHERE name='a';
select money from `account_t` WHERE name='a';
commit
begin
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
UPDATE `account_t` set money=money-100 WHERE name='a';
commit;
在读写分离的场景下,可以将 slave 节点设置为 READ UNCOMMITTED;此时脏读不影响,在 slave 上查询并不需要特别精准的返回值。
不可重复读
事务(A) 可以读到另外一个事务(B)中提交的数据;通常发生在一个事务中两次读到的数据是不一样的情况;不可重复读在
隔离级别 READ COMMITTED 存在。
一般而言,不可重复读的问题是可以接受的,因为读到已经提交的数据,一般不会带来很大的问题,所以很多厂商(如Oracle、SQL Server)默认隔离级别就是 READ COMMITTED。
-- 事务1
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;BEGINUPDATE `account_t` set money=money-100 WHERE name='a';UPDATE `account_t` set money=money+100 WHERE name='b';COMMIT;
-- 事务2
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;BEGIN
-- 事务1更新数据并提交前执行
select money from `account_t` WHERE name='a';
-- 事务1更新数据并提交后 执行
select money from `account_t` WHERE name='a';
COMMIT;
#mermaid-svg-SZFV8Dskwgo2QZsw {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-SZFV8Dskwgo2QZsw .error-icon{fill:#552222;}#mermaid-svg-SZFV8Dskwgo2QZsw .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-SZFV8Dskwgo2QZsw .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-SZFV8Dskwgo2QZsw .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-SZFV8Dskwgo2QZsw .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-SZFV8Dskwgo2QZsw .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-SZFV8Dskwgo2QZsw .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-SZFV8Dskwgo2QZsw .marker{fill:#333333;stroke:#333333;}#mermaid-svg-SZFV8Dskwgo2QZsw .marker.cross{stroke:#333333;}#mermaid-svg-SZFV8Dskwgo2QZsw svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-SZFV8Dskwgo2QZsw .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-SZFV8Dskwgo2QZsw .cluster-label text{fill:#333;}#mermaid-svg-SZFV8Dskwgo2QZsw .cluster-label span{color:#333;}#mermaid-svg-SZFV8Dskwgo2QZsw .label text,#mermaid-svg-SZFV8Dskwgo2QZsw span{fill:#333;color:#333;}#mermaid-svg-SZFV8Dskwgo2QZsw .node rect,#mermaid-svg-SZFV8Dskwgo2QZsw .node circle,#mermaid-svg-SZFV8Dskwgo2QZsw .node ellipse,#mermaid-svg-SZFV8Dskwgo2QZsw .node polygon,#mermaid-svg-SZFV8Dskwgo2QZsw .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-SZFV8Dskwgo2QZsw .node .label{text-align:center;}#mermaid-svg-SZFV8Dskwgo2QZsw .node.clickable{cursor:pointer;}#mermaid-svg-SZFV8Dskwgo2QZsw .arrowheadPath{fill:#333333;}#mermaid-svg-SZFV8Dskwgo2QZsw .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-SZFV8Dskwgo2QZsw .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-SZFV8Dskwgo2QZsw .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-SZFV8Dskwgo2QZsw .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-SZFV8Dskwgo2QZsw .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-SZFV8Dskwgo2QZsw .cluster text{fill:#333;}#mermaid-svg-SZFV8Dskwgo2QZsw .cluster span{color:#333;}#mermaid-svg-SZFV8Dskwgo2QZsw div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-SZFV8Dskwgo2QZsw :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}
事务2
事务1
1
4
2
1
2
3
5
6
begin
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
select money from `account_t` WHERE name='a';
select money from `account_t` WHERE name='a';
commit
begin
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
commit;
UPDATE `account_t` set money=money-100 WHERE name='a';
幻读
两次读取同一个范围内的记录得到的结果集不一样。
例如:
#mermaid-svg-VtcjV2hoOTMaC7N5 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-VtcjV2hoOTMaC7N5 .error-icon{fill:#552222;}#mermaid-svg-VtcjV2hoOTMaC7N5 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-VtcjV2hoOTMaC7N5 .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-VtcjV2hoOTMaC7N5 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-VtcjV2hoOTMaC7N5 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-VtcjV2hoOTMaC7N5 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-VtcjV2hoOTMaC7N5 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-VtcjV2hoOTMaC7N5 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-VtcjV2hoOTMaC7N5 .marker.cross{stroke:#333333;}#mermaid-svg-VtcjV2hoOTMaC7N5 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-VtcjV2hoOTMaC7N5 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-VtcjV2hoOTMaC7N5 .cluster-label text{fill:#333;}#mermaid-svg-VtcjV2hoOTMaC7N5 .cluster-label span{color:#333;}#mermaid-svg-VtcjV2hoOTMaC7N5 .label text,#mermaid-svg-VtcjV2hoOTMaC7N5 span{fill:#333;color:#333;}#mermaid-svg-VtcjV2hoOTMaC7N5 .node rect,#mermaid-svg-VtcjV2hoOTMaC7N5 .node circle,#mermaid-svg-VtcjV2hoOTMaC7N5 .node ellipse,#mermaid-svg-VtcjV2hoOTMaC7N5 .node polygon,#mermaid-svg-VtcjV2hoOTMaC7N5 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-VtcjV2hoOTMaC7N5 .node .label{text-align:center;}#mermaid-svg-VtcjV2hoOTMaC7N5 .node.clickable{cursor:pointer;}#mermaid-svg-VtcjV2hoOTMaC7N5 .arrowheadPath{fill:#333333;}#mermaid-svg-VtcjV2hoOTMaC7N5 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-VtcjV2hoOTMaC7N5 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-VtcjV2hoOTMaC7N5 .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-VtcjV2hoOTMaC7N5 .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-VtcjV2hoOTMaC7N5 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-VtcjV2hoOTMaC7N5 .cluster text{fill:#333;}#mermaid-svg-VtcjV2hoOTMaC7N5 .cluster span{color:#333;}#mermaid-svg-VtcjV2hoOTMaC7N5 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-VtcjV2hoOTMaC7N5 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}
事务2
事务1
1
4
2
1
2
3
5
6
begin
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
select money from `account_t` WHERE name='a';
select money from `account_t` WHERE name='a' for update;
commit
begin
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
commit;
UPDATE `account_t` set money=money-100 WHERE name='a';
幻读在隔离级别 REPEATABLE READ 及以下存在;但是可以在 REPEATABLE READ 级别下通过读加锁(使用 next-key locking)解决。
#mermaid-svg-m5jt6stPmHAJCWig {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-m5jt6stPmHAJCWig .error-icon{fill:#552222;}#mermaid-svg-m5jt6stPmHAJCWig .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-m5jt6stPmHAJCWig .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-m5jt6stPmHAJCWig .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-m5jt6stPmHAJCWig .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-m5jt6stPmHAJCWig .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-m5jt6stPmHAJCWig .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-m5jt6stPmHAJCWig .marker{fill:#333333;stroke:#333333;}#mermaid-svg-m5jt6stPmHAJCWig .marker.cross{stroke:#333333;}#mermaid-svg-m5jt6stPmHAJCWig svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-m5jt6stPmHAJCWig .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-m5jt6stPmHAJCWig .cluster-label text{fill:#333;}#mermaid-svg-m5jt6stPmHAJCWig .cluster-label span{color:#333;}#mermaid-svg-m5jt6stPmHAJCWig .label text,#mermaid-svg-m5jt6stPmHAJCWig span{fill:#333;color:#333;}#mermaid-svg-m5jt6stPmHAJCWig .node rect,#mermaid-svg-m5jt6stPmHAJCWig .node circle,#mermaid-svg-m5jt6stPmHAJCWig .node ellipse,#mermaid-svg-m5jt6stPmHAJCWig .node polygon,#mermaid-svg-m5jt6stPmHAJCWig .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-m5jt6stPmHAJCWig .node .label{text-align:center;}#mermaid-svg-m5jt6stPmHAJCWig .node.clickable{cursor:pointer;}#mermaid-svg-m5jt6stPmHAJCWig .arrowheadPath{fill:#333333;}#mermaid-svg-m5jt6stPmHAJCWig .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-m5jt6stPmHAJCWig .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-m5jt6stPmHAJCWig .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-m5jt6stPmHAJCWig .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-m5jt6stPmHAJCWig .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-m5jt6stPmHAJCWig .cluster text{fill:#333;}#mermaid-svg-m5jt6stPmHAJCWig .cluster span{color:#333;}#mermaid-svg-m5jt6stPmHAJCWig div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-m5jt6stPmHAJCWig :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}
事务2
事务1
1
4
2
1
2
3
5
6
begin
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
select money from `account_t` WHERE name='a' for update;
select money from `account_t` WHERE name='a';
commit
begin
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
commit;
UPDATE `account_t` set money=money-100 WHERE name='a';
丢失更新
脏读、不可重复读、幻读都是一个事务写,一个事务读,由于一个事务的写导致另一个事务读到了不该读的数据。
丢失更新是两个事务都是写。丢失更新分为提交覆盖和回滚覆盖;回滚覆盖会被数据库拒绝,所以不可能产生,重点关注提交覆盖。
#mermaid-svg-DS2PLAqVdXibyj8g {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-DS2PLAqVdXibyj8g .error-icon{fill:#552222;}#mermaid-svg-DS2PLAqVdXibyj8g .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-DS2PLAqVdXibyj8g .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-DS2PLAqVdXibyj8g .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-DS2PLAqVdXibyj8g .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-DS2PLAqVdXibyj8g .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-DS2PLAqVdXibyj8g .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-DS2PLAqVdXibyj8g .marker{fill:#333333;stroke:#333333;}#mermaid-svg-DS2PLAqVdXibyj8g .marker.cross{stroke:#333333;}#mermaid-svg-DS2PLAqVdXibyj8g svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-DS2PLAqVdXibyj8g .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-DS2PLAqVdXibyj8g .cluster-label text{fill:#333;}#mermaid-svg-DS2PLAqVdXibyj8g .cluster-label span{color:#333;}#mermaid-svg-DS2PLAqVdXibyj8g .label text,#mermaid-svg-DS2PLAqVdXibyj8g span{fill:#333;color:#333;}#mermaid-svg-DS2PLAqVdXibyj8g .node rect,#mermaid-svg-DS2PLAqVdXibyj8g .node circle,#mermaid-svg-DS2PLAqVdXibyj8g .node ellipse,#mermaid-svg-DS2PLAqVdXibyj8g .node polygon,#mermaid-svg-DS2PLAqVdXibyj8g .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-DS2PLAqVdXibyj8g .node .label{text-align:center;}#mermaid-svg-DS2PLAqVdXibyj8g .node.clickable{cursor:pointer;}#mermaid-svg-DS2PLAqVdXibyj8g .arrowheadPath{fill:#333333;}#mermaid-svg-DS2PLAqVdXibyj8g .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-DS2PLAqVdXibyj8g .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-DS2PLAqVdXibyj8g .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-DS2PLAqVdXibyj8g .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-DS2PLAqVdXibyj8g .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-DS2PLAqVdXibyj8g .cluster text{fill:#333;}#mermaid-svg-DS2PLAqVdXibyj8g .cluster span{color:#333;}#mermaid-svg-DS2PLAqVdXibyj8g div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-DS2PLAqVdXibyj8g :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}
事务2
事务1
1
4
5
2
1
2
3
6
7
begin
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
select money from `account_t` WHERE name='a' ;
UPDATE `account_t` set money=1000-100 WHERE name='a';
commit
begin
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
UPDATE `account_t` set money=1000+100 WHERE name='a';
select money from `account_t` WHERE name='a' ;
commit;
区别
(1)脏读和不可重复读的区别在于,脏读是读取了另一个事务未提交的数据,而不可重复读是读取了另一个事务提交后的修改。本质上都是其他事务的修改影响了本事务的读取。
(2)不可重复读和幻读比较类似;不可重复读是两次读取同一条记录,得到不一样的结果;而幻读是两次读取同一个范围内的记录得到的结果集不一样(可能不同个数,也可能相同个数内容不一样,比如x一行后又添加新行)。
(3)不可重复读是因为其他事务进行了 update 操作,幻读是因为其他事务进行了 insert或者 delete 操作。
隔离级别
|
回滚覆盖
|
脏读
|
不可重复读
|
幻读
|
提交覆盖
|
READ UNCOMMITTED
|
NO
|
YES
|
YES
|
YES
|
YES
|
READ COMMITTED
|
NO
|
NO
|
YES
|
YES
|
YES
|
REPEATABLE REA
|
NO
|
NO
|
NO
|
YES (手动加锁)
|
YES (手动加锁)
|
SERIALIZABLE
|
NO
|
NO
|
NO
|
NO
|
NO
|
总结
- redolog在事务提交后,记录DML操作对应物理页修改的内容。
- undolog记录DML操作步骤,用于回滚(通过逆运算回滚);undolog记录了事务的操作步骤以及历史版本信息。
- 在innodb中,一条SQL语句也是一个事务,是一个单独的事务,不需要begin… commit。
- 脏读是一个事务读取到另一个未提交事务修改的数据;不可重复的是一个事务内两次读取同一数据不一样。
- 幻读是一个事务内两次读取同一范围内的记录得到的结果集不一样。
- 不可重复读和幻读比较类似;不可重复读是两次读取同一条记录,得到不一样的结果;而幻读是两次读取同一个范围内的记录得到的结果集不一样。
- 重点掌握脏读、不可重复读、幻读的区别。
MySQL事务原理之事务概述和隔离级别相关推荐
- Spring的AOP和IOC是什么?使用场景有哪些?Spring事务与数据库事务,传播行为,数据库隔离级别
Spring的AOP和IOC是什么?使用场景有哪些?Spring事务与数据库事务,传播行为,数据库隔离级别 AOP:面向切面编程. 即在一个功能模块中新增其他功能,比方说你要下楼取个快递,你同事对你说 ...
- mysql show 原理_mysql事务的实现原理
此篇文章算是对mysql事务的一个总结,基本把mysql事务相关的知识点都涵盖到了,面试问来问去无非也就是这些,在了解这些之前我们先对mysql在执行的过程中 有一个整体的认识,如下图 如上图所示, ...
- java mysql实现原理_MySQL事务实现原理
MySQL事务隔离级别的实现原理 知识储备 只有InnoDB支持事务,所以这里说的事务隔离级别是指InnoDB下的事务隔离级别 隔离级别 读未提交:一个事务可以读取到另一个事务未提交的修改.这会带来脏 ...
- 腾讯面试:MySQL事务与MVCC如何实现的隔离级别?
有情怀,有干货,微信搜索[三太子敖丙]关注这个不一样的程序员. 本文 GitHub https://github.com/JavaFamily 已收录,有一线大厂面试完整考点.资料以及我的系列文章. ...
- 正确理解MYSQL事务的可重复读RR隔离级别与锁
1,mysql默认的事务隔离级别是可重复度. 可重复度就是在一个事务中对相同数据的多次查询将返回相同结果. 比如 事务1: BEGIN; SELECT age from student where i ...
- mysql 事务原理 mvcc_MySql事务以及MVCC机制与原理
事务 事务是一组原子性sql查询语句,被当作一个工作单元.若MySQL对改事务单元内的所有sql语句都正常的执行完,则事务操作视为成功,所有的sql语句才对数据生效,若sql中任意不能执行或出错则事务 ...
- mysql 事务,锁,与四大隔离级别
概念 事务 原子性:事务必须是一个自动工作的单元,要么全部执行,要么全部不执行. 一致性:事务结束的时候,所有的内部数据都是正确的. 隔离性:并发多个事务时,各个事务不干涉内部数据,处理的都是另外一个 ...
- 同一个事务里面对同一条数据做2次修改_MySQL事务与MVCC如何实现的隔离级别
有情怀,有干货,微信搜索[三太子敖丙]关注这个不一样的程序员. 本文 GitHub https://github.com/JavaFamily 已收录,有一线大厂面试完整考点.资料以及我的系列文章. ...
- mysql 消极锁_MySQL支持的事物隔离级别以及消极锁和乐观锁原理和应用场景
在一样平常开发中,尤其是营业开发,少不了行使 Java 对数据库举行基本的增删改查等数据操作,这也是Java工程师的必备手艺之一.做好数据操作,不仅仅需要对Java语言相关框架的掌握,更需要对种种数据 ...
最新文章
- VBA最常用的基础代码、基础功能写法总结
- python list转字符串_我用python写了个自动生成给文档生成索引的脚本!懒人智慧...
- oracle怎么才能成功安装,一、oracle数据库成功安装步骤 (11gR2)
- Typora markdown公式换行等号对齐_下了31个markdown编辑器,我就不信选不出一个好用的...
- Oracle11g数据库快速安装
- YAML出现严重的反序列化漏洞,谷歌TensorFlow将采用 JSON
- 今天安家 明天开始在网络的海洋里遨游
- excel多列数据生成图分类有间隔
- JAVA 蓝桥杯 第十届 省赛 C组 之 [等差数列]
- 2019年数据库系统工程师上午真题及答案解析
- java的第一行代码
- java下载服务器资源
- 计算机圆半径计算公式,圆半径计算公式是什么?
- GIS空间服务赋能邵阳不动产登记可视化办理
- android手机变为usb无线网卡,淘汰的手机别扔,简单几步变无线网卡
- 判断API接口是否支持跨域
- radmin配置说明
- 孤独的人最怕的是爱情
- 通向架构师的道路(第二十七天)IBM网格计算与企业批处理任务架构
- 服务器c盘logs文件夹,Win10系统Logs文件夹有什么作用?Logs文件夹可以删除吗?
热门文章
- Minecraft兑换码
- Labelme 安装以及使用
- IBM Workload Scheduler 9.5 版本特性
- 根据Wind资讯统计数据
- QLExpress 规则引擎
- 2022建筑焊工(建筑特殊工种)特种作业证考试题库及在线模拟考试
- 华为---VLAN实验
- 百度Android客户端研发面试经验
- jupyter notebook 远程连接调试实录
- 在线ps怎么快速处理图片?电脑怎么批量图片处理?