Heim  >  Artikel  >  Datenbank  >  Was ist ein Deadlock? Lassen Sie uns über das Verständnis des MySQL-Deadlocks sprechen

Was ist ein Deadlock? Lassen Sie uns über das Verständnis des MySQL-Deadlocks sprechen

青灯夜游
青灯夜游nach vorne
2022-08-24 10:12:502215Durchsuche

Was ist ein Deadlock? Der folgende Artikel führt Sie zum Verständnis des MySQL-Deadlocks. Lassen Sie uns über die notwendigen Bedingungen für einen Deadlock in MySQL sprechen und wie man das Deadlock-Problem löst. Ich hoffe, es hilft allen.

Was ist ein Deadlock? Lassen Sie uns über das Verständnis des MySQL-Deadlocks sprechen

1. Was ist ein Deadlock?

Deadlock bezieht sich auf die Tatsache, dass in zwei oder mehr unterschiedlichen Prozessen oder Threads aufgrund der Konkurrenz um gemeinsame Ressourcen oder der Kommunikation zwischen Prozessen (oder Threads) jeder Thread hängt und auf den anderen wartet, wenn keine externen Kräfte vorhanden sind letztendlich zum Zusammenbruch des gesamten Systems führen. 2. Notwendige Bedingungen für einen Deadlock in Mysql Wird nur von einer Transaktion verwendet und kann auch als exklusive Ressource (z. B. Zeilensperre) bezeichnet werden.

Anforderungs- und Aufbewahrungsbedingungen

  • bedeutet, dass Sperre A in einer Transaktion a erhalten wurde, aber eine neue Anforderung für Sperre B gestellt wurde und die Sperre B bereits durch eine andere Transaktion b belegt ist Gleichzeitig wird die Transaktion a blockiert, die erhaltene Sperre A bleibt jedoch erhalten.

Keine Entzugsbedingung
  • bedeutet, dass die Sperre A vor der Übermittlung nicht entzogen werden kann. Sie kann erst nach der Verwendung festgeschrieben und dann selbst freigegeben werden.

Bedingungen für den gegenseitigen Sperrenerwerb
  • bedeutet, dass beim Auftreten eines Deadlocks ein gegenseitiger Sperrenerwerbsprozess stattfinden muss, d. h. eine Transaktion, die Sperre A hält, erwirbt Sperre B und gleichzeitig die Transaktion Das Halten von Sperre B b erwirbt auch Sperre A, was schließlich zum gegenseitigen Erwerb führt und alle Transaktionen blockiert.

3. Mysql klassischer Deadlock-Fall

  1. Angenommen, es gibt ein Überweisungsszenario: Wenn Konto A 50 Yuan auf Konto B überweist, überweist Konto B auch 30 Yuan auf Konto A. Wird es bei diesem Vorgang Probleme geben? Was ist mit Deadlock-Situationen? 3.1 Anweisung zur Tabellenerstellung

    Bevor wir über das Deadlock-Problem sprechen, werfen wir einen Blick darauf Sehen Sie sich zunächst den normalen Übertragungsvorgang an.
  2. Unter normalen Umständen überweist Benutzer A 50 Yuan an Benutzer B, was innerhalb einer Transaktion abgeschlossen werden kann. Da diese beiden Daten später geändert werden müssen, muss der Kontostand von Benutzer A und der Kontostand von Benutzer B abgerufen werden (für UPDATE) sperrt sie, um zu verhindern, dass andere Transaktionsänderungen dazu führen, dass unsere Änderungen verloren gehen und schmutzige Daten entstehen.
Das relevante SQL lautet wie folgt:

Sie müssen die automatische Übermittlung von MySQL deaktivieren, bevor Sie die Transaktion öffnen
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='账户余额表';

VARIABLEN wie „autocommit“ anzeigen;![Bildbeschreibung hier einfügen](https:// img-blog.csdnimg.cn/a486a4ed5c9d4240bd115ac7b3ce5a39.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA6ZqQIOmjjg==,size_20,color_FF FFFF,t_70,g_se ,x_16)

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);
Ergebnis nach der Ausführung:

Sie können sehen, dass Datenaktualisierungen normal sind Hohe Parallelität vorausgesetzt Es gibt ein Szenario, in dem Benutzer A 50 Yuan an Benutzer B überweist und Benutzer B ebenfalls 30 Yuan an Benutzer A überweist.

Dann sind der Prozess und der Zeitplan unseres Java-Programms wie folgt:

1. Benutzer A überweist 50 Yuan an Benutzer B. Er muss Transaktion 1 im Programm öffnen, um SQL auszuführen, den Kontostand von A zu erhalten und den Anteil von A zu sperren von Daten.

set autocommit=0;
# 查看事务自动提交状态状态
2. Benutzer B überweist 30 Yuan an Benutzer A. Er muss Transaktion 2 im Programm öffnen, um SQL auszuführen, den Kontostand von B zu erhalten und die Daten von B zu sperren.

# 转账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;
3. Führen Sie die verbleibende SQL in Transaktion 1 aus.

# 事务1
set autocommit=0;
START TRANSACTION;
# 获取A 的余额并存入A_balance变量:80
SELECT user_id,@A_balance:=balance from account where user_id = 'A' for UPDATE;

Sie können sehen, dass beim Erwerb der Schreibsperre für B-Daten in Transaktion 1 eine Zeitüberschreitung aufgetreten ist. Warum passiert das? Hauptsächlich, weil wir in Schritt 2 die Schreibsperre für Daten B in Transaktion 2 erhalten haben, sodass Transaktion 1 niemals die Schreibsperre für Daten B erhält, bevor Transaktion 2 festgeschrieben oder zurückgesetzt wird. 4. Führen Sie die verbleibende SQL in Transaktion 2 aus. Da die Schreibsperre für Daten A in Transaktion 1 in Schritt 1 erhalten wurde, erhält Transaktion 2 niemals die Schreibsperre für Daten A, bevor Transaktion 1 festgeschrieben oder zurückgesetzt wird.

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视频教程

Das obige ist der detaillierte Inhalt vonWas ist ein Deadlock? Lassen Sie uns über das Verständnis des MySQL-Deadlocks sprechen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:csdn.net. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen