关于事务

我们在数据库中需要执行一个系列的操作的时候,要保证这个系列执行的连续性和完整性,要么整个系列的执行都成功,要么就全部失败(只要有一个步骤失败,其他均回滚到之前的状态),

保证不会存在一部分成功一部分失败的情况。这就是我们事务的职责。下面举个分苹果的例子:

A同学有3个苹果,B同学有2个苹果,如果A同学给一个苹果给B同学,那么A同学只剩下2个苹果,而B同学有了3个。步骤如下

1 update tname  set apples=apples-1 where name = "A";
2 update tname set apples=apples+1 where name = "B";  

当然,这是理想情况下的结果。有可能会出错:A同学减去了一个苹果,然后执行B同学的加苹果的时候,系统宕掉了,B同学没有加成功,A同学的库存中无厘头少了一个苹果。

这时候我们就需要事务支持了,有事务的情况下,就有两种状态,要么都成功,要么都失败。

操作成功:A同学减去一个苹果,B同学增加一个苹果,最终A 2个,B 3个。

操作失败:A同学依旧是3个苹果,B同学依旧是2个苹果。

事务有如下特性(参考官方描述):

1、在 MySQL 中只有使用了 Innodb 数据库引擎的数据库或表才支持事务。
2、事务处理可以用来维护数据库的完整性,保证成批的 SQL 语句要么全部执行,要么全部不执行。
3、事务用来管理 insert,update,delete 语句。

事务的四个特性(ACID)

一般来说,衡量事务必须满足四个特性:ACID,即 原子性(Atomicity,或称不可分割性)、一致性(Consistency)、隔离性(Isolation,又称独立性)、持久性(Durability)。

原子性(Atomicity):一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。

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

隔离性(Isolation):数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable),下面会详细说明。

持久性(Durability):事务处理结束后,对数据的修改就是永久的,会持久化到硬盘上,即便系统故障也不会丢失。

显示和隐式事务

事务分为显示事务和隐式事务,

隐式事务:事务自动开启、提交或回滚,比如insert、update、delete语句,事务的开启、提交或回滚由mysql内部自动控制的

显示事务:事务需要手动开启、提交或回滚,由开发者自己控制。

自动提交

MySQL中事务默认是隐式事务(即自动提交(autocommit)模式为 ON),执行insert、update、delete操作的时候,数据库自动开启事务、提交或回滚事务。如下所示:

1 mysql> show variables like 'autocommit';
2 +---------------+-------+
3 | Variable_name | Value |
4 +---------------+-------+
5 | autocommit | ON |
6 +---------------+-------+
7 1 row in set 

在自动提交模式下,如果没有start transaction显式地开始一个事务,那么每个sql语句都会被当做一个事务执行提交操作。

通过如下方式,可以关闭autocommit;需要注意的是,autocommit参数是针对连接的,在一个连接中修改了参数,不会对其他连接产生影响。如果你新开一个命令窗口,会恢复到默认值。

 1 mysql> set autocommit = 0;
2 Query OK, 0 rows affected
3
4 mysql> show variables like 'autocommit';
5 +---------------+-------+
6 | Variable_name | Value |
7 +---------------+-------+
8 | autocommit | OFF |
9 +---------------+-------+
10 1 row in set 

如果关闭了autocommit,则所有的sql语句都在一个事务中,直到执行了commit或rollback,该事务结束,同时开始了另外一个事务。

特殊操作

在MySQL中,存在一些特殊的命令,如果在事务中执行了这些命令,会马上强制执行commit提交事务;比如DDL语句(create table/drop table/alter table)。

不过,常用的select、insert、update和delete命令,都不会强制提交事务。

手动操作事务的两种方式

改变默认事务提交策略

SET AUTOCOMMIT=0 禁止自动提交

SET AUTOCOMMIT=1 开启自动提交

这个跟上面的脚本大概一致,先设置为禁止自动提交事务,这样执行的修改并没有真正的到数据库中,等commit 或者 rollback来最终确认,再手动进行事务操作。

1 --1、设置不自动提交事务
2 set autocommit=0;
3 --2、手动进行事务操作
4 commit;|rollback; 

提交示例:

 1 mysql> set autocommit = 0;
2 Query OK, 0 rows affected
3
4 mysql> insert into classes values(5,'初三五班');
5 Query OK, 1 row affected
6
7 mysql> commit; --提交操作
8 Query OK, 0 rows affected
9
10 mysql> select * from classes;
11 +---------+-----------+
12 | classid | classname |
13 +---------+-----------+
14 | 1 | 初三一班 |
15 | 2 | 初三二班 |
16 | 3 | 初三三班 |
17 | 4 | 初三四班 |
18 | 5 | 初三五班 |
19 +---------+-----------+
20 5 rows in set 

回滚示例:

 1 mysql> set autocommit = 0;
2 Query OK, 0 rows affected
3
4 mysql> insert into classes values(6,'初三六班');
5 Query OK, 1 row affected
6
7 mysql> rollback; --回滚操作
8 Query OK, 0 rows affected
9
10 mysql> select * from classes;
11 +---------+-----------+
12 | classid | classname |
13 +---------+-----------+
14 | 1 | 初三一班 |
15 | 2 | 初三二班 |
16 | 3 | 初三三班 |
17 | 4 | 初三四班 |
18 | 5 | 初三五班 |
19 +---------+-----------+
20 5 rows in set 

常规启用事务

START|BEGIN 开始一个事务

ROLLBACK 事务回滚

COMMIT 事务确认

这是典型的MySQL事务操作,其中start transaction标识事务开始,commit提交事务,将执行结果写入到数据库。如果sql语句执行出现问题,会调用rollback,回滚所有已经执行成功的sql语句。

当然,也可以在事务中直接使用rollback语句进行回滚。:

1 start transaction; --1、开启事务
2 /* 1条或者n条待执行的语句 */
3 commit;|rollback; --2、手动进行事务操作 

提交示例:

 1 mysql> start transaction;
2 Query OK, 0 rows affected
3
4 mysql> insert into classes values(6,'初三六班');
5 Query OK, 1 row affected
6
7 mysql> commit;
8 Query OK, 0 rows affected
9
10 mysql> select * from classes;
11
12 +---------+-----------+
13 | classid | classname |
14 +---------+-----------+
15 | 1 | 初三一班 |
16 | 2 | 初三二班 |
17 | 3 | 初三三班 |
18 | 4 | 初三四班 |
19 | 5 | 初三五班 |
20 | 6 | 初三六班 |
21 +---------+-----------+
22 6 rows in set 

回滚示例:

 1 mysql> start transaction;
2 Query OK, 0 rows affected
3
4 mysql> insert into classes values(7,'初三七班');
5 Query OK, 1 row affected
6
7 mysql> rollback;
8 Query OK, 0 rows affected
9
10 mysql> select * from classes;
11 +---------+-----------+
12 | classid | classname |
13 +---------+-----------+
14 | 1 | 初三一班 |
15 | 2 | 初三二班 |
16 | 3 | 初三三班 |
17 | 4 | 初三四班 |
18 | 5 | 初三五班 |
19 | 6 | 初三六班 |
20 +---------+-----------+
21 6 rows in set 

事务保存点的使用

事务保存点(savepoint),指的是对事务执行过程中做位置保存(类似我们打游戏时的存盘点),如果你写了一大堆的语句,但是有部分是你不想回滚的,想保留修改的状态,但是部分是你想回滚的。

这时候使用savepoint是个不错的方法。

 1 mysql> start transaction; --开启事务
2 Query OK, 0 rows affected
3
4 mysql> insert into classes values(7,'初三七班');
5 Query OK, 1 row affected
6
7 mysql> savepoint point1; --注意:这边设置了一个存盘点
8 Query OK, 0 rows affected
9
10 mysql> insert into classes values(8,'初三八班');
11 Query OK, 1 row affected
12
13 mysql> rollback to point1; --记住这个语法,回滚到存盘点,存盘点之后的语句就丢弃了
14 Query OK, 0 rows affected
15
16 mysql> commit;
17 Query OK, 0 rows affected
18
19 mysql> select * from classes; --最后输出,确实只有存盘点之前的成功了
20 +---------+-----------+
21 | classid | classname |
22 +---------+-----------+
23 | 1 | 初三一班 |
24 | 2 | 初三二班 |
25 | 3 | 初三三班 |
26 | 4 | 初三四班 |
27 | 5 | 初三五班 |
28 | 6 | 初三六班 |
29 | 7 | 初三七班 |
30 +---------+-----------+
31 7 rows in set 

这边需要注意 savepoint 和 rollback to savepoint 的配合使用。

只读事务的使用

表示在事务中执行的是一些只读操作,如查询,但是不会做insert、update、delete操作,数据库内部对只读事务可能会有一些性能上的优化。

1 start transaction read only; 

再只读操作的事务中进行增、删、改操作会报错,如下:

 1 mysql> start transaction read only;
2 Query OK, 0 rows affected
3
4 mysql> select * from classes;
5 +---------+-----------+
6 | classid | classname |
7 +---------+-----------+
8 | 1 | 初三一班 |
9 | 2 | 初三二班 |
10 | 3 | 初三三班 |
11 | 4 | 初三四班 |
12 | 5 | 初三五班 |
13 | 6 | 初三六班 |
14 | 7 | 初三七班 |
15 +---------+-----------+
16 7 rows in set
17
18 mysql> insert into classes values(8,'初三八班');
19 1792 - Cannot execute statement in a READ ONLY transaction. --这边报出异常

事务的脏读、幻读、不可重复读

脏读:读取未提交数据

脏读就是指当一个事务A正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务B也访问这个数据,然后使用了这个脏数据。举个例子

时间顺序 A事务 B事务

T1

开始事务  

T2

  开始事务

T3

查询A同学有2个苹果  

T4

给A同学增加一个苹果(未提交)

 

T5

  查询A同学有3个苹果(读脏数据)
T6 添加苹果操作出现错误,回滚回2个苹果  
 T7 提交事务  

不可重复读:前后多次读取数据不一致

不可重复读指的是在事务A中先后多次读取同一个数据,读取的结果不一样,因为另外一个事务也访问该同一数据,并且可能修改这个数据,这种现象称为不可重复读。

脏读与不可重复读的区别在于:前者读到的是其他事务未提交的数据,后者读到的是其他事务已提交的数据。

时间顺序 A事务 B事务

T1

开始事务  

T2

  开始事务

T3

   查询A同学有2个苹果

T4

给A同学增加一个苹果(未提交)

 

T5

 提交事务  
T6    查询A同学有3个苹果(不可重复读)

按照正确逻辑,事务B前后两次读取到的数据应该一致,这边一次读到的是2个,一次读到的是3个。

幻读:前后多次读取,数据总量不一致

在事务A中按照某个条件先后两次查询数据库,两次查询结果的条数不同,这种现象称为幻读。不可重复读与幻读的区别可以通俗的理解为:前者是数据变了,后者是数据的行数变了。

时间顺序 A事务 B事务

T1

开始事务  开始事务

T2

  第一次查询库存数据有2条

T3

给A同学增加一个苹果(增加了一条库存数据)  

T4

提交事务

 

T5

  第二次查询库存数据有3条

按照正确逻辑,按照正确逻辑,事务B前后两次读取到的数据总量应该一致

不可重复读和幻读的区别

(1)不可重复读是读取了其他事务更改的数据,针对update操作

解决:使用行级锁,锁定该行,事务A多次读取操作完成后才释放该锁,这个时候才允许其他事务更改刚才的数据。

(2)幻读是读取了其他事务新增的数据,针对insert与delete操作

解决:使用表级锁,锁定整张表,事务A多次读取数据总量之后才释放该锁,这个时候才允许其他事务新增数据。

幻读和不可重复读都是指的一个事务范围内的操作受到其他事务的影响了。只不过幻读是重点在插入和删除,不可重复读重点在修改

事务的隔离级别

SQL标准中事务的隔离性(Isolation)定义了四种隔离级别,并规定了每种隔离级别下上述几个(脏读、不可重复读、幻读)问题是否存在。

一般来说,隔离级别越低,系统开销越低,可支持的并发越高,但隔离性也越差。隔离级别与读问题的关系如下:

隔离级别的分类

注意:幻读只会在 可重复读 级别中才会出现,其他级别下不存在。

隔离级别 脏读 不可重复读 幻读

读未提交:Read Uncommitted

x

读已提交:Read Committed

× x

可重复读:Repeatable Read

×  x

串行化:Serializable

×

 × ×

查看|修改 隔离级别

查看当前的隔离级别,默认应该都是可重复读(Repeatable Read):

1 mysql> show variables like 'transaction_isolation';
2 +-----------------------+-----------------+
3 | Variable_name | Value |
4 +-----------------------+-----------------+
5 | transaction_isolation | REPEATABLE-READ |
6 +-----------------------+-----------------+
7 1 row in set

修改隔离级别:

找到MySQL安装目录中的my.init文件,会看到当前的隔离级别:REPEATABLE-READ。

1 # 隔离级别设置,READ-UNCOMMITTED读未提交,READ-COMMITTED读已提交,REPEATABLE-READ可重复读,SERIALIZABLE串行
2 transaction-isolation=REPEATABLE-READ

修改后重启MySQL即可,下面的各项操作都是在修改了对应的隔离级别之后的操作。

READ-UNCOMMITTED:读未提交

事物A和事物B,事物B未提交的数据,事物A可以读取到,这里读取到的数据叫做“脏数据”,这种隔离级别最低,一般理论上存在,数据库隔离级别大都高于该级别。

举个例子:学校新增加了7班和8班,教务主任进去录入了8班的班级信息,但是该事务并未提交,而8班班主任正好去查看班级信息,发现是存在的,摩拳擦掌准备录入这个班级的学生信息。可是教务主任发现班级信息写错了,应该先录入7班,于是迅速回滚了该事务。

最后8班的信息消失了。出现上述情况,就是我们所说的脏读 ,两个并发的事务,“事务A:查询班级信息”、“事务B:录入班级信息”,事务A读取了事务B尚未提交的数据。

数据基础:

 1 mysql> select * from classes;
2 +---------+-----------+
3 | classid | classname |
4 +---------+-----------+
5 | 1 | 初三一班 |
6 | 2 | 初三二班 |
7 | 3 | 初三三班 |
8 | 4 | 初三四班 |
9 | 5 | 初三五班 |
10 | 6 | 初三六班 |
11 | 7 | 初三七班 |
12 +---------+-----------+
13 7 rows in set
时间顺序 事务A 事务B
T1 start transaction;  
T2 select * from classes;  
T3   start transaction;
T4   insert into classes values(8,'初三八班');
T5   select * from classes;
T6 select * from classes;  
T7   rollback;
T8 commit;  

说明:

事务A-T2:只有7条数据,事务A-T6:有8条数据,事务B-T6并未提交,此时事务A已经看到了事务B插入的数据,出现了脏读

事务A-T2:只有7条数据,T6-A:有8条数据,查询到的结果不一样,出现不可重复读

结论:读未提交情况下,可以读取到其他事务还未提交的数据,多次读取结果不一样,出现了脏读、不可重复读

READ-COMMITTED:读已提交

事物A和事物B,事物B提交完的数据,事物A才能读取到

这种隔离级别高于读未提交:即对方事物提交之后的数据,我当前事物才能读取到

这种隔离级别可以避免“脏数据” ,但会导致“不可重复读取”

数据基础跟上面一样

时间顺序 事务A 事务B
T1 start transaction;  
T2 select * from classes;  
T3   start transaction; 
T4   insert into classes values(8,'初三八班');  
T5 select * from classes;  
T6   commit;
T7 select * from classes;  

说明:

事务A-T5:只有7条数据,A看不到B新增的第8条数据,说明没有脏读

事务A-T5:只有7条数据,事务A-T7:读到了8条数据,此时事务B已经提交了事务,事务A读取到了事务B提交的数据,说明可以读取到已提交的数据

事务A-T5 和 事务A-T7:两次读取的数据结果不一样,说明不可重复读

结论:读已提交情况下,无法读取到其他事务还未提交的数据,可以读取到其他事务已经提交的数据,多次读取结果不一样,未出现脏读,出现了读已提交、不可重复读。

REPEATABLE-READ:可重复读

可重复读是MySQL默认事务隔离级别

事务A和事务B,事务B提交之后的数据,事务A读取不到,即对方提交之后的数据,还是读取不到

事务B是可重复读取数据,隔离级别高于读已提交,这种隔离级别可以避免“不可重复读取”,达到可重复读取,但是可能导致“幻读”

数据基础:

 1 mysql> select * from classes;
2 +---------+-----------+
3 | classid | classname |
4 +---------+-----------+
5 | 1 | 初三一班 |
6 | 2 | 初三二班 |
7 | 3 | 初三三班 |
8 | 4 | 初三四班 |
9 | 5 | 初三五班 |
10 | 6 | 初三六班 |
11 | 7 | 初三七班 |
12 | 8 | 初三八班 |
13 +---------+-----------+
14 8 rows in set
时间顺序 事务A 事务B
T1 start transaction;  
T2   start transaction;
T3   insert into classes values(9,'初三九班');
T4 select * from classes;  
T5   commit;
T6   select * from classes;
T7 select * from classes;  
T8 commit;  
T9 select * from classes;  

说明:

事务A-T4、事务A-T7:读到的是八条数据,事务B-T6:有数据,A事务下读不到B事务产生的数据,说明没有脏读

事务A-T7:读到的是9条数据,这时事务B已经commit,事务A看不到事务B已提交的数据,A事务下两次读的结果一样,说明可重复读

事务A-T9:读到的是9条数据。

结论:可重复读情况下,未出现脏读,未读取到其他事务已提交的数据,多次读取结果一致,即可重复读。

SERIALIZABLE:串行

SERIALIZABLE会让并发的事务串行执行。事务A和事务B,事务A在操作数据库时,事务B只能排队等待,这种隔离级别很少使用,吞吐量太低,用户体验差

这种级别可以避免“幻读”,每一次读取的都是数据库中真实存在数据,事务A与事务B串行,而不并发

数据基础同上

时间 窗口A 窗口B
T1 start transaction;  
T2 select * from classes;  
T3   start transaction;
T4   insert into classes values(9,'初三九班');
T5 select * from classes;  
T6 commit;  
T7   commit;

按时间顺序运行上面的命令,会发现事务B-T4这样会被阻塞,直到事务A执行完毕T6步骤。

可以看出来,事务只能串行执行了。串行情况下不存在脏读、不可重复读、幻读的问题了。

隔离级别选择注意点

1、读已提交(READ-COMMITTED)通常用的比较多,也是MySQL默认选项。

2、具体选择哪种需要结合具体的业务来选择,隔离级别越高,并发性也低,比如最高级别SERIALIZABLE会让事物串行执行,并发操作变成串行了,会导致系统性能直接降低

事务典型用法

 1 DROP PROCEDURE IF EXISTS t_test;
2 DELIMITER //
3 CREATE PROCEDURE t_test()
4 BEGIN
5 DECLARE t_error INTEGER;
6 DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET t_error = 1;
7 START TRANSACTION;
8 insert into students(studentname,score,classid) VALUE('A',99,3);
9 insert into students(studentname,score,classid) VALUE('A','lala',3); --这边执行错误,两个语句都会被回滚
10 IF t_error = 1 THEN
11 ROLLBACK; -- 有错误回滚
12 ELSE
13 COMMIT; --没错误提交
14 END IF;
15 END //DELIMITER;
16 CALL t_test();

这个是典型的用法,score是decimal类型,这两个语句都会被回滚。

MVCC了解

RR解决脏读、不可重复读、幻读等问题,使用的是MVCC(Multi-Version Concurrency Control)协议,即多版本的并发控制协议。

有多个请求来读取表中的数据时可以不采取任何操作,但是多个请求里有读请求,又有修改请求时必须有一种措施来进行并发控制。不然很有可能会造成不一致。

读写锁,解决上述问题很简单,只需用两种锁的组合来对读写请求进行控制即可,这两种锁被称为:

1、共享锁(shared lock),又叫做 读锁

读锁是可以共享的,或者说多个读请求可以共享一把锁读数据,不会造成阻塞。

2、排他锁(exclusive lock),又叫做 写锁

写锁会排斥其他所有获取锁的请求,一直阻塞,直到写入完成释放锁。

通过读写锁,可以做到读读可以并行,但是不能做到写读,写写并行。这边了解一下,后续专门一篇来说明MVCC的原理和实现机制分析。

总结

1、认识ACID(原子性、一致性、隔离性、持久性)特性及其实现原理

2、了解事务的脏读、幻读、不可重复读

3、了解事务的隔离级别以及原理

MySQL全面瓦解14:事务的更多相关文章

  1. [MySQL Reference Manual]14 InnoDB存储引擎

    14 InnoDB存储引擎 14 InnoDB存储引擎 14.1 InnoDB说明 14.1.1 InnoDB作为默认存储引擎 14.1.1.1 存储引擎的趋势 14.1.1.2 InnoDB变成默认 ...

  2. MySQL/MariaDB数据库的事务和隔离级别

      MySQL/MariaDB数据库的事务和隔离级别 作者:尹正杰  版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.事务概述 1>.事务Transactions 一组原子性的SQL语句 ...

  3. MySQL全面瓦解13:系统函数相关

    概述 提到MySQL的系统函数,我们前面有使用过聚合函数,其实只是其中一小部分.MySQL提供很多功能强大.方便易用的函数,使用这些函数,可以极大地提高用户对于数据库的管理效率,并更加灵活地满足不同用 ...

  4. MySQL全面瓦解23:MySQL索引实现和使用

    MySQL索引实现 上一篇我们详细了解了B+树的实现原理(传送门).我们知道,MySQL内部索引是由不同的引擎实现的,主要包含InnoDB和MyISAM这两种,并且这两种引擎中的索引都是使用b+树的结 ...

  5. MySQL全面瓦解24:构建高性能索引(策略篇)

    学习如果构建高性能的索引之前,我们先来了解下之前的知识,以下两篇是基础原理,了解之后,对面后续索引构建的原则和优化方法会有更清晰的理解: MySQL全面瓦解22:索引的介绍和原理分析 MySQL全面瓦 ...

  6. MySQL全面瓦解25:构建高性能索引(案例分析篇)

    回顾一下上面几篇索引相关的文章: MySQL全面瓦解22:索引的介绍和原理分析 MySQL全面瓦解23:MySQL索引实现和使用 MySQL全面瓦解24:构建高性能索引(策略篇) 索引的十大原则 1. ...

  7. CentOS 6.4下编译安装MySQL 5.6.14

    概述: CentOS 6.4下通过yum安装的MySQL是5.1版的,比较老,所以就想通过源代码安装高版本的5.6.14. 正文: 一:卸载旧版本 使用下面的命令检查是否安装有MySQL Server ...

  8. spring mvc 4.3.2 + mybatis 3.4.1 + mysql 5.7.14 +shiro 幼儿园收费系统 之 登录

    如标题,用spring mvc 4.3.2+mybatis 3.4.1 + mysql 5.7.14 +shiro 开发了一个用于幼儿园的管理系统. 功能模块 包括 账号,角色,权限管理. 幼儿档案管 ...

  9. Mysql引起的spring事务失效

    老项目加新功能,导致出现service调用service的情况..一共2张表有数据的添加删除.然后测试了一下事务,表A和表B,我在表B中抛了异常,但结果发现,表B回滚正常,但是表A并没有回滚.显示事务 ...

随机推荐

  1. xctf攻防世界——crackme writeup

    感谢xctf提供学习平台 https://adworld.xctf.org.cn crackme有壳,脱壳部分见文章: https://www.cnblogs.com/hongren/p/126332 ...

  2. node.js报错:Cannot find module 'xxx'的解决办法

    从别处拷贝一份node.js项目,如图 控制台启动 nodemon index.js 后报错:Cannot find module xxx.删除node_modules,在启动时仍提示Cannot f ...

  3. 在FL Studio中如何更好地为人声加上混响(进阶教程)

    为人声加上混响是我们在处理人声过程中必不可少的一步.然而,除了直接在人声混音轨道加上混响插件进行调节以外,这里还有更为细节的做法可以达到更好的效果. 步骤一:使用均衡器 在为人声加上混响之前,我们应该 ...

  4. 「CSP-S 2020」动物园

    description luogu loj(暂无数据) solution 这道题作为T2,对选手们考试开始后先通看一遍所有题目的好习惯,以及判断究竟谁才是真正的签到题的重要能力进行了较好的锻炼, 特别 ...

  5. JUC并发工具包之CyclicBarrier & CountDownLatch的异同

    1.介绍 本文我们将比较一下CyclicBarrier和CountDownLatch并了解两者的相似与不同. 2.两者是什么 当谈到并发,将这两者概念化的去解释两者是做什么的,这其实是一件很有挑战的事 ...

  6. vulnhub: DC 3

    通过nmap扫描,只开放了80端口,并且该web服务是基于Joomla搭建: root@kali:~# nmap -A 192.168.74.140 Starting Nmap 7.80 ( http ...

  7. leetcode 108 和leetcode 109

    //感想:有时候啊,对于一道题目,如果知道那个点在哪,就会非常简单,比如说这两题,将有序的数组转换为二叉搜索树, 有几个点: 1.二叉搜索树:对于某个节点,它的左节点小于它,它的右节点大于它,这是二叉 ...

  8. VMware与Device/Credential Guard不兼容问题

    启动虚拟机vmware突然报不兼容错误 解决方法: 1首先打开控制面板>程序>启动或关闭Windows功能, 取消Hyper-v的勾选 2.在往下划,关闭Windows沙盒的勾选沙盒和虚拟 ...

  9. docker中启动容器提示端口被占用

    docker中启动容器提示端口被占用,但是 docker ps -a 查不到信息 1.查询端口被占用的id netstat -ntpl |grep 3306 2.杀掉该id kill -9 如果kil ...

  10. 1. 揭秘Spring类型转换 - 框架设计的基石

    仰不愧天,俯不愧人,内不愧心.关注公众号[BAT的乌托邦],有Spring技术栈.MyBatis.JVM.中间件等小而美的原创专栏供以免费学习.分享.成长,拒绝浅尝辄止.本文已被 https://ww ...