Maison  >  Article  >  base de données  >  Qu'est-ce qu'une impasse ? Parlons de la compréhension du blocage de MySQL

Qu'est-ce qu'une impasse ? Parlons de la compréhension du blocage de MySQL

青灯夜游
青灯夜游avant
2022-08-24 10:12:502226parcourir

Qu'est-ce qu'une impasse ? L'article suivant vous amènera à comprendre le blocage de MySQL. Parlons des conditions nécessaires à un blocage dans Mysql et comment résoudre le problème de blocage ? J'espère que cela aide tout le monde.

Qu'est-ce qu'une impasse ? Parlons de la compréhension du blocage de MySQL

1. Qu'est-ce qu'une impasse ?

L'impasse fait référence au fait que dans deux ou plusieurs processus ou threads différents, en raison de la concurrence pour les ressources communes ou de la communication entre les processus (ou threads), chaque thread se bloque et s'attend s'il n'y a pas de forces externes. finira par provoquer l’effondrement du système tout entier.

2. Conditions nécessaires au blocage dans Mysql

  • Condition d'exclusivité des ressources

fait référence à l'exclusivité mutuelle lorsque plusieurs transactions sont en concurrence pour la même ressource, c'est-à-dire, sur une période donnée, une ressource n'est utilisée que par Occupé par une transaction, elle peut également être appelée ressource exclusive (comme un verrou de ligne).

  • Conditions de demande et de conservation

signifie que le verrou A a été obtenu dans une transaction a, mais qu'une nouvelle demande de verrou B a été faite et que le verrou B est déjà occupé par une autre transaction b. temps, la transaction a se bloquera, mais elle conservera le verrou A qu'elle a obtenu.

  • Aucune condition de privation

fait référence à un verrou A qui a été acquis dans la transaction a. Il ne peut être privé avant d'être soumis. Il ne peut être engagé qu'après utilisation puis libéré par lui-même.

  1. Conditions d'acquisition de verrouillage mutuel

signifie que lorsqu'une impasse se produit, il doit y avoir un processus d'acquisition de verrouillage mutuel, c'est-à-dire qu'une transaction contenant un verrou A acquiert le verrou B, et en même temps, la transaction détenir le verrou B b, c'est également acquérir le verrou A, ce qui conduit finalement à une acquisition mutuelle et chaque transaction est bloquée.

3. Cas d'impasse classique de Mysql

Supposons qu'il y ait un scénario de transfert lorsque le compte A transfère 50 yuans vers le compte B, le compte B transfère également 30 yuans vers le compte A. Y aura-t-il des problèmes dans ce processus ? Qu’en est-il des situations de blocage ?

3.1 Déclaration de création de table

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 Initialiser les données associées

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 Processus de transfert normal

Avant de parler du problème de blocage, regardons-le Consultez d’abord le processus de transfert normal.
Dans des circonstances normales, l'utilisateur A transfère 50 yuans à l'utilisateur B, ce qui peut être effectué en une seule transaction. Le solde de l'utilisateur A et le solde de l'utilisateur B doivent d'abord être obtenus. Étant donné que ces deux données doivent être modifiées ultérieurement, un verrou en écriture. (pour UPDATE) les verrouille pour empêcher d'autres modifications de transaction d'entraîner la perte de nos modifications et de provoquer des données sales.
Le SQL pertinent est le suivant :

Vous devez désactiver la soumission automatique de MySQL avant d'ouvrir la transaction

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

afficher les VARIABLES comme 'autocommit' ;![Insérer la description de l'image ici](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;
Résultat après exécution :

Quest-ce quune impasse ? Parlons de la compréhension du blocage de MySQL

Vous pouvez voir que les mises à jour des données sont normales

3.4 Processus de transfert dans une impasse

Le solde initialisé est

Quest-ce quune impasse ? Parlons de la compréhension du blocage de MySQL

En supposant une concurrence élevée Il existe un scénario dans lequel l'utilisateur A transfère 50 yuans à l'utilisateur B, et l'utilisateur B transfère également 30 yuans à l'utilisateur A.

Ensuite, le processus et le calendrier de fonctionnement de notre programme Java sont les suivants :

1. L'utilisateur A transfère 50 yuans à l'utilisateur B. Il doit ouvrir la transaction 1 dans le programme pour exécuter SQL, obtenir le solde de A et verrouiller la pièce de A. de données.

# 事务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. L'utilisateur B transfère 30 yuans à l'utilisateur A. Il doit ouvrir la transaction 2 dans le programme pour exécuter SQL, obtenir le solde de B et verrouiller les données de B.

# 事务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. Exécutez le SQL restant dans la transaction 1

# 获取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;

Vous pouvez voir qu'un délai d'attente s'est produit lors de l'acquisition du verrou en écriture des données B dans la transaction 1. Pourquoi cela se produit-il ? Principalement parce que nous avons obtenu le verrou en écriture sur les données B dans la transaction 2 à l'étape 2, la transaction 1 n'obtiendra jamais le verrou en écriture sur les données B avant que la transaction 2 ne soit validée ou annulée.

4. Exécutez le SQL restant dans la transaction 2

# 获取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;

De même, un délai d'attente s'est également produit lors de l'acquisition du verrou en écriture des données A dans la transaction 2. Étant donné que le verrou en écriture sur les données A a été obtenu dans la transaction 1 à l'étape 1, la transaction 2 n'obtiendra jamais le verrou en écriture sur les données A avant que la transaction 1 ne soit validée ou annulée. 🎜

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

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer