>  기사  >  데이터 베이스  >  교착상태란 무엇입니까? MySQL 교착 상태에 대한 이해에 대해 이야기해 보겠습니다.

교착상태란 무엇입니까? MySQL 교착 상태에 대한 이해에 대해 이야기해 보겠습니다.

青灯夜游
青灯夜游앞으로
2022-08-24 10:12:502184검색

교착상태란 무엇인가요? 다음 기사에서는 MySQL 교착 상태를 이해하는 데 도움이 됩니다. MySQL의 교착상태에 필요한 조건과 교착상태 문제를 해결하는 방법에 대해 이야기해 볼까요? 그것이 모두에게 도움이 되기를 바랍니다.

교착상태란 무엇입니까? MySQL 교착 상태에 대한 이해에 대해 이야기해 보겠습니다.

1. 교착상태란 무엇인가요?

두 개 이상의 서로 다른 프로세스 또는 스레드에서 공통 리소스 경쟁이나 프로세스(또는 스레드) 간의 통신으로 인해 각 스레드가 중단되고 외부 요인이 없으면 서로 기다리는 현상을 말합니다. 결국 전체 시스템이 붕괴되는 원인이 됩니다.

2.Mysql에서 교착 상태가 발생하는 조건

  • 리소스 배타 조건

은 동일한 리소스, 즉 일정 시간 내에 하나의 리소스를 두고 여러 트랜잭션이 경쟁하는 경우의 상호 배타성을 말합니다. 트랜잭션에 의해 점유된 경우에만 사용되며 배타적 리소스(예: 행 잠금)라고도 할 수 있습니다.

  • 요청 및 보유 조건

은 A 잠금이 트랜잭션 a에서 획득되었지만 새로운 잠금 B 요청이 이루어졌고 잠금 B가 이미 다른 트랜잭션 b에 의해 점유되었음을 의미합니다. 시간이 지나면 트랜잭션 a는 차단되지만 획득한 잠금 A는 유지됩니다.

  • 박탈 조건 없음

은 잠금 A가 트랜잭션 a에서 획득되었음을 의미합니다. 제출하기 전에는 박탈할 수 없으며 사용 후 커밋된 다음 자체적으로 해제될 수 있습니다.

  1. 상호 잠금 획득 조건

은 교착 상태가 발생하면 상호 잠금 획득 프로세스가 있어야 함을 의미합니다. 즉, 보유 잠금 A가 잠금 B를 획득하는 트랜잭션과 동시에 트랜잭션이 잠금 B b도 잠금 A를 획득하고 있으며 이는 결국 상호 획득으로 이어지며 각 트랜잭션이 차단됩니다.

3. Mysql 클래식 교착 상태

A 계좌가 B 계좌로 50위안을 이체하면 B 계좌도 A 계좌로 30위안을 이체하는 경우가 있다고 가정해 보겠습니다. 이 과정에서 문제가 발생합니까? 교착상태 상황은 어떻습니까?

3.1 테이블 생성문

CREATE TABLE `account` (
  `id` int(11) NOT NULL COMMENT '主键',
  `user_id` varchar(56) NOT NULL COMMENT '用户id',
  `balance` float(10,2) DEFAULT NULL COMMENT '余额',
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_user_id` (`user_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='账户余额表';

3.2 관련 데이터 초기화

INSERT INTO `test`.`account` (`id`, `user_id`, `balance`) VALUES (1, 'A', 80.00);
INSERT INTO `test`.`account` (`id`, `user_id`, `balance`) VALUES (2, 'B', 60.00);

3.3 정상적인 전송 과정

교착상태 문제에 대해 이야기하기 전에 먼저 살펴보겠습니다. 먼저 일반적인 전송 프로세스를 참조하세요.
일반적인 상황에서는 사용자 A가 사용자 B에게 50위안을 전송하는데, 이는 한 번의 거래로 완료될 수 있습니다. 사용자 A의 잔액과 사용자 B의 잔액을 먼저 확보해야 합니다. 이 두 데이터는 나중에 수정해야 하기 때문에 쓰기 잠금이 필요합니다. (업데이트의 경우) 다른 트랜잭션 변경으로 인해 변경 사항이 손실되고 더티 데이터가 발생하는 것을 방지하기 위해 이를 잠급니다.
해당 sql은 다음과 같습니다.

트랜잭션을 열기 전에 mysql의 자동 커밋을 꺼야 합니다

set autocommit=0;
# 查看事务自动提交状态状态

show VARIABLES like 'autocommit';![여기에 이미지 설명 삽입](https:// img-blog.csdnimg .cn/a486a4ed5c9d4240bd115ac7b3ce5a39.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_show VARIABLES like 'autocommit';![在这里插入图片描述](https://img-blog.csdnimg.cn/a486a4ed5c9d4240bd115ac7b3ce5a39.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_

Q1NETiBA6ZqQIOmjjg==,size_20,color_FFFFFF,t_70,g_se,x_16)

Q1NETiBA6ZqQIOmjjg==,size_20,color_FFFFFF,t _70,g_se,x_16)

# 转账sql
START TRANSACTION;
# 获取A 的余额并存入A_balance变量:80
SELECT user_id,@A_balance:=balance from account where user_id = 'A' for UPDATE;
# 获取B 的余额并存入B_balance变量:60
SELECT user_id,@B_balance:=balance from account where user_id = 'B' for UPDATE;

# 修改A 的余额
UPDATE account set balance = @A_balance - 50 where user_id = 'A';
# 修改B 的余额
UPDATE account set balance = @B_balance + 50 where user_id = 'B';
COMMIT;
실행 결과 :

교착상태란 무엇입니까? MySQL 교착 상태에 대한 이해에 대해 이야기해 보겠습니다.

데이터 업데이트가 정상적으로 이루어지고 있는 것을 확인할 수 있습니다

3.4 Deadlock 전송 과정

초기화된 잔액은

교착상태란 무엇입니까? MySQL 교착 상태에 대한 이해에 대해 이야기해 보겠습니다.

높은 동시성 사용자 A가 사용자 B에게 50위안을 이체하고, 사용자 B도 사용자 A에게 30위안을 이체하는 시나리오가 있습니다.

그런 다음 Java 프로그램 작업의 프로세스와 타임라인은 다음과 같습니다.

1. 사용자 A는 사용자 B에게 50위안을 이체합니다. 그는 SQL을 실행하고 A의 잔액을 얻고 A의 조각을 잠그기 위해 프로그램에서 트랜잭션 1을 열어야 합니다. 데이터.

# 事务1
set autocommit=0;
START TRANSACTION;
# 获取A 的余额并存入A_balance变量:80
SELECT user_id,@A_balance:=balance from account where user_id = 'A' for UPDATE;
2. 사용자 B는 사용자 A에게 30위안을 전송합니다. 그는 SQL을 실행하고 B의 잔액을 얻고 B의 데이터를 잠그려면 프로그램에서 트랜잭션 2를 열어야 합니다.

# 事务2
set autocommit=0;
START TRANSACTION;
# 获取A 的余额并存入A_balance变量:60
SELECT user_id,@A_balance:=balance from account where user_id = 'B' for UPDATE;
3. 트랜잭션 1의 나머지 SQL을 실행합니다.

# 获取B 的余额并存入B_balance变量:60
SELECT user_id,@B_balance:=balance from account where user_id = 'B' for UPDATE;

# 修改A 的余额
UPDATE account set balance = @A_balance - 50 where user_id = 'A';
# 修改B 的余额
UPDATE account set balance = @B_balance + 50 where user_id = 'B';
COMMIT;

트랜잭션 1에서 B 데이터의 쓰기 잠금을 획득할 때 타임아웃이 발생한 것을 확인할 수 있습니다. 왜 이런 일이 발생합니까? 주로 2단계에서 트랜잭션 2의 데이터 B에 대한 쓰기 잠금을 획득했기 때문에 트랜잭션 1은 트랜잭션 2가 커밋하거나 롤백하기 전에는 데이터 B에 대한 쓰기 잠금을 얻지 못할 것입니다.

4. 트랜잭션 2에서 나머지 sql을 실행합니다

# 获取A 的余额并存入B_balance变量:60
SELECT user_id,@B_balance:=balance from account where user_id = 'A' for UPDATE;

# 修改B 的余额
UPDATE account set balance = @A_balance - 30 where user_id = 'B';
# 修改A 的余额
UPDATE account set balance = @B_balance + 30 where user_id = 'A';
COMMIT;

마찬가지로 트랜잭션 2에서 데이터 A의 쓰기 잠금을 획득할 때도 타임아웃이 발생했습니다. 1단계의 트랜잭션 1에서 데이터 A에 대한 쓰기 잠금을 얻었으므로 트랜잭션 2는 트랜잭션 1이 커밋하거나 롤백하기 전에는 데이터 A에 대한 쓰기 잠금을 얻지 못합니다. 🎜

5、 为什么会出现这种情况呢?

主要是因为事务1和事务2存在相互等待获取锁的过程,导致两个事务都挂起阻塞,最终抛出获取锁超时的异常。

3.5 死锁导致的问题

众所周知,数据库的连接资源是很珍贵的,如果一个连接因为事务阻塞长时间不释放,那么后面新的请求要执行的sql也会排队等待,越积越多,最终会拖垮整个应用。一旦你的应用部署在微服务体系中而又没有做熔断处理,由于整个链路被阻断,那么就会引发雪崩效应,导致很严重的生产事故。

4、如何解决死锁问题?

要想解决死锁问题,我们可以从死锁的四个必要条件入手。
由于资源独占条件和不剥夺条件是锁本质的功能体现,无法修改,所以咱们从另外两个条件尝试去解决。

4.1 打破请求和保持条件

根据上面定义可知,出现这个情况是因为事务1和事务2同时去竞争锁A和锁B,那么我们是否可以保证锁A和锁B一次只能被一个事务竞争和持有呢?
答案是肯定可以的。下面咱们通过伪代码来看看:

/**
* 事务1入参(A, B)
* 事务2入参(B, A)
**/
public void transferAccounts(String userFrom, String userTo) {
     // 获取分布式锁
     Lock lock = Redisson.getLock();
     // 开启事务
     JDBC.excute("START TRANSACTION;");
     // 执行转账sql
     JDBC.excute("# 获取A 的余额并存入A_balance变量:80\n" +
             "SELECT user_id,@A_balance:=balance from account where user_id = '" + userFrom + "' for UPDATE;\n" +
             "# 获取B 的余额并存入B_balance变量:60\n" +
             "SELECT user_id,@B_balance:=balance from account where user_id = '" + userTo + "' for UPDATE;\n" +
             "\n" +
             "# 修改A 的余额\n" +
             "UPDATE account set balance = @A_balance - 50 where user_id = '" + userFrom + "';\n" +
             "# 修改B 的余额\n" +
             "UPDATE account set balance = @B_balance + 50 where user_id = '" + userTo + "';\n");
     // 提交事务
     JDBC.excute("COMMIT;");
     // 释放锁
     lock.unLock();
}

上面的伪代码显而易见可以解决死锁问题,因为所有的事务都是通过分布式锁来串行执行的。

那么这样就真的万事大吉了吗?

在小流量情况下看起来是没问题的,但是在高并发场景下这里将成为整个服务的性能瓶颈,因为即使你部署了再多的机器,但由于分布式锁的原因,你的业务也只能串行进行,服务性能并不因为集群部署而提高并发量,完全无法满足分布式业务下快、准、稳的要求,所以咱们不妨换种方式来看看怎么解决死锁问题。

4.2 打破相互获取锁条件(推荐)

要打破这个条件其实也很简单,那就是事务再获取锁的过程中保证顺序获取即可,也就是锁A始终在锁B之前获取。
我们来看看之前的伪代码怎么优化?

/**
* 事务1入参(A, B)
* 事务2入参(B, A)
**/
public void transferAccounts(String userFrom, String userTo) {
     // 对用户A和B进行排序,让userFrom始终为用户A,userTo始终为用户B
     if (userFrom.hashCode() > userTo.hashCode()) {
         String tmp = userFrom;
         userFrom = userTo;
         userTo = tmp;
     }
     // 开启事务
     JDBC.excute("START TRANSACTION;");
     // 执行转账sql
     JDBC.excute("# 获取A 的余额并存入A_balance变量:80\n" +
             "SELECT user_id,@A_balance:=balance from account where user_id = '" + userFrom + "' for UPDATE;\n" +
             "# 获取B 的余额并存入B_balance变量:60\n" +
             "SELECT user_id,@B_balance:=balance from account where user_id = '" + userTo + "' for UPDATE;\n" +
             "\n" +
             "# 修改A 的余额\n" +
             "UPDATE account set balance = @A_balance - 50 where user_id = '" + userFrom + "';\n" +
             "# 修改B 的余额\n" +
             "UPDATE account set balance = @B_balance + 50 where user_id = '" + userTo + "';\n");
     // 提交事务
     JDBC.excute("COMMIT;");
 }

假设事务1的入参为(A, B),事务2入参为(B, A),由于我们对两个用户参数进行了排序,所以在事务1中需要先获取锁A在获取锁B,事务2也是一样要先获取锁A在获取锁B,两个事务都是顺序获取锁,所以也就打破了相互获取锁的条件,最终完美解决死锁问题。

5、总结

因为mysql在互联网中的大量使用,所以死锁问题还是经常会被问到,希望兄弟们能掌握这方面的知识,提高自己的竞争力。

【相关推荐:mysql视频教程

위 내용은 교착상태란 무엇입니까? MySQL 교착 상태에 대한 이해에 대해 이야기해 보겠습니다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 csdn.net에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제