SQL-事务与并发问题

        在数据库管理系统中,事务是一个重要的概念,它确保了一组数据库操作要么全部成功,要么全部失败,从而维护数据的完整性和一致性。随着多个用户同时访问数据库,事务的并发处理变得尤为重要。

1. 事务的定义

事务是指一组数据库操作,这些操作被视为一个单一的逻辑单元。事务的主要目标是确保数据在并发环境下的完整性和一致性。事务的四大特性被称为ACID特性:

  • 原子性 (Atomicity):事务中的所有操作要么全部成功,要么全部失败。如果事务中的任何操作失败,整个事务将被回滚。
  • 一致性 (Consistency):事务必须使数据库从一个一致性状态转换到另一个一致性状态。即使在发生错误或系统崩溃的情况下,数据库也必须保持一致性。
  • 隔离性 (Isolation):多个并发事务的执行互不干扰,每个事务的执行结果在提交之前对其他事务是不可见的。
  • 持久性 (Durability):一旦事务被提交,其结果是永久性的,即使系统崩溃也不会丢失。

2. 数据准备

        接下来,我们将通过一个简单的转账示例来演示事务的使用。首先,我们创建一个客户表并插入一些初始数据。

create table account (
    id int auto_increment primary key comment '主键id',
    name varchar(10) comment '姓名',
    money int comment '余额'
) comment '客户表';

insert into account (id, name, money) values 
(null, '张三', 2000),
(null, '李四', 2000);

3. 转账操作示例

        在这个示例中,我们将实现张三向李四转账1000元的操作。我们将使用事务来确保转账的原子性。

3.1 查看自动提交状态

在进行事务操作之前,我们可以查看当前的自动提交状态:

select @@autocommit;  -- 查看当前事务自动提交状态,默认为1

3.2 开始事务

我们将手动控制事务的开始和结束:

start transaction;  -- 开始事务

3.3 执行转账操作

1.查询张三的余额:

select money from account where name = '张三';  -- 查询张三的余额

2.扣款操作:

update account set money = money - 1000 where name = '张三' and money >= 1000;  -- 扣款1000

 3.增加李四余额:

update account set money = money + 1000 where name = '李四';  -- 李四余额增加1000

3.4 提交或回滚事务

最后,我们需要提交事务以保存更改,或者在出现错误时回滚事务:

commit;  -- 提交事务
-- rollback;  -- 如果发生错误,回滚事务

4.脏读,不可重复读,幻读

1.脏读是指一个事务可以读取另一个事务未提交的修改。这可能导致读取到不一致的数据。

比如事务B读取了事务A尚未提交的数据

2.不可重复读是指在同一个事务中,多次读取同一数据的结果可能不同。这通常发生在一个事务读取数据后,另一个事务对该数据进行了修改。

比如事务A两次读取结果不一致,因为事务B对同一id提交了数据

3.幻读是指在一个事务中,读取到的结果集在同一事务内的后续查询中发生变化。这通常发生在一个事务读取了一组数据,而另一个事务插入了新数据。

比如事务A查询时没有数据,插入数据时却发现已经有数据存在了,导致插入数据失败

 

 5. 事务的隔离级别

        在并发环境中,多个事务可能会同时访问相同的数据,导致数据不一致的问题。SQL提供了四种事务隔离级别来控制这种并发访问的行为:

  • 读未提交 (Read Uncommitted):一个事务可以读取另一个事务未提交的修改。这可能导致“脏读”现象。
  • 读提交 (Read Committed):一个事务只能读取已提交的修改。这样可以避免脏读,但可能会导致“不可重复读”现象。
  • 可重复读 (Repeatable Read):在一个事务内,多次读取同一数据的结果是相同的。可以避免脏读和不可重复读,但可能会导致“幻读”现象。
  • 串行化 (Serializable):最高的隔离级别,所有事务串行执行,完全避免并发问题,但性能较低。

MySQL中默认是 Repeatable Read,而在 Oracle中默认是Read Committed

 

5.1 查看和设置事务隔离级别

我们可以通过以下SQL语句查看和设置当前会话的事务隔离级别:

示例:

select @@transaction_isolation;  -- 查看当前事务隔离级别

set session transaction isolation level read committed;  -- 设置当前会话的事务隔离级别为读提交
set global transaction isolation level repeatable read;  -- 设置全局事务隔离级别为可重复读

6. 并发问题示例

为了更好地理解事务的并发问题,我们可以考虑以下场景:

6.1 脏读示例

-- 事务A
start transaction;
update account set money = money - 1000 where name = '张三';

-- 事务B
start transaction;
select money from account where name = '张三';  -- 读取到未提交的值

在这个例子中,事务B在事务A提交之前读取了张三的余额,可能会导致事务B看到的余额不准确。

6.2 不可重复读示例

-- 事务A
start transaction;
select money from account where name = '张三';  -- 第一次读取

-- 事务B
start transaction;
update account set money = money - 500 where name = '张三';  -- 更新张三的余额
commit;

-- 事务A
select money from account where name = '张三';  -- 第二次读取,结果可能不同

        在这个例子中,事务A在第一次读取后,事务B对张三的余额进行了更新,导致事务A在第二次读取时看到的结果与第一次不同。

6.3 幻读示例

-- 事务A
start transaction;
select * from account where money > 1000;  -- 第一次读取

-- 事务B
start transaction;
insert into account (name, money) values ('王五', 1500);  -- 插入新记录
commit;

-- 事务A
select * from account where money > 1000;  -- 第二次读取,结果集可能不同

        在这个例子中,事务A在第一次查询时只返回了张三和李四的记录,但在事务B插入新记录后,事务A在第二次查询时可能会看到新的记录。

7. 总结

        事务是确保数据库操作一致性和完整性的关键机制。在并发环境中,事务的隔离级别能够有效控制并发访问带来的数据不一致问题。理解事务的ACID特性和不同的隔离级别将有助于开发者设计出更可靠的数据库应用。希望这篇文章能对你有所帮助。

最后点赞支持一下吧,好让我有继续创作的动力

  • 6
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值