并发访问
在mysql中事务是一个用于解决数据库并发访问的功能,主要运用与读写并发中。
当多个数据库客户端同时访问数据库时,有一些客户端可能对数据库进行增删改操作,有一些可能对数据库进行读取操作,那么如何能保证数据完整性和一致性呢,最方便的方法就是加锁,mysql也是一个进程,只需要对增删查改等各种操作加上锁就可以解决数据不一致的问题。
就如同进程并发时,以抢票系统为例,我们需要先判断是否还有票,这就需要查看数据库中对应的票数量是否为0,进入之后需要对这个数据进行update将其票数量值减1,但是多个进程时并发运行的,此进程通过了判断逻辑但是还没又对数据进行更新,这时另外一个抢票进程又来,上一个进程就被挂起了,新进来的进程也进入了并将票数量进行了更新,若是此时票已经为0了,那么当挂起的进程从新获得时间片之后,那么对数据进行的操作将是不合法的,因为票已经没有了。
在进程中我们一般会为这个抢票逻辑加上锁,一旦有进程进入此段代码,其他的进程都只能等待锁的释放,获得锁才能进入。这就保证了抢票这段代码的原子性。
此逻辑在mysql也是一样的,当有多个客户端同时对同一个数据库内的同一行记录进行修改时,mysql也是需要对其进行加锁,直到持有锁的将锁释放掉才能对其进行操作。
加锁与效率
读写是否需要加锁呢?如上例子,读写进行并发也是需要保证整个过程的数据的一致性和正确性的,不然读取时是有票的,但是还没进行下一步的操作,已经被其他客户端更改了数据,那么以原数据为基准进行的操作就肯定是错误的。
但是对数据库的访问可能不只一两个客户端,可能会有大量的客户端进行对数据库的访问,而且操作可能也有长操作,短操作。不能让一个客户端就完全占用了数据库而禁止其他客户端的访问。所以后来mysql加入的事务用与处理并发访问的问题,事务最开始并不存在于mysql中的,而是后续经过实际使用后,对应需求而加入的功能。
什么是事务
事务就是我们需要对数据进行的一个完整的操作的总合,如抢票,我们需要先查询票数量就是select语句,获取其数据后判断票是大于0的我们还需要对数据进行update将票--,那么这两条语句就是我们需要对这个数据的一个完整的操作,两个语句统合成一个事务。
为了处理并发访问导致的问题,事务拥有4个属性
- 原子性: 为了解决并发导致的数据不一致问题,需要对应的操作要具有原子性。mysql中将一条或多条DML语句(CURD)组成一组,这一组语句就是一个事务,这一整个事务是原子性的,从开始,到结束,在外部看来是只有两个状态,一个是什么事都没做,一个已经完成了事务中的所有操作的两个状态。
- 隔离性:数据库是允许多个事务并发进行的,那么对事务与事务之间进行隔离,以保证每个事务获取到的数据能满足一致性,防止事务的交叉执行的干扰,事务隔离分为不同的级别:读未提交(read uncommitted),读提交(read committed),可重复读(repeatable read),串行化(serializable)。
- 持久性:当事务结束后,对数据修改就是永久的,即使故障也不会丢失。
- 一致性:上面三条就是为了保证数据的一致性。事务开始与结束之后,数据库的完整性没有被破坏,事务对数据的修改是完全符合预设规则,包含资料的精确度,串联性,以及后续数据库可以自发地完成预定的工作。
在mysql中只有innodb数据库引擎是支持事务的。
MariaDB [(none)]> show engines\G

如图只有InnoDB中的Comment字段中有Supports transactions。
Savepoints是指事务保存点。
自动提交
使用innodb引擎的数据库中,当我们输入一段DML语句时其实就已经经历了一次事务了如
select * from table_name;
这条语句也是一个事务,在mysql中默认是自动对事务进行提交的
MariaDB [(none)]> show variables like 'autocommit';
这时代表事务自动提交以开启。
这个当然时可以更改的。
MariaDB [(none)]> set autocommit=0;
这是之影响当前正在运行的客户端。
现在有一张表

同时打开两个客户端,我们使用关闭了自动提交事务的客户端对此表进行插入
insert into student values(7,'alies',2ies',2);

进行了插入后我们再使用另外一个客户端对表进行查询结果如下

这里是没有id为7的一行记录的。这就是事务的原子性-两个状态,因为此事务还没有完成即还没有提交,所以,在其他事务中看起来就是什么都没做,直到我们输入
MariaDB [test_db]> commit;
这时,就能从其他客户端中看到这条记录了。