MySQL线上死锁案例分析

news2024/11/23 19:18:51

项目场景

项目开发中有两张表:c_bill(账单表),c_bill_detail(账单明细表),他们的表结构如下(这里只保留必要信息):

CREATE TABLE `c_bill_detail` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
  `bill_detail_no` varchar(32)  NOT NULL DEFAULT '' COMMENT '对账单编号',
  `receivable_date` datetime(3) DEFAULT NULL COMMENT '应收日期',
  `order_type` varchar(20) NOT NULL DEFAULT '' COMMENT 
  `bill_no` varchar(32)  NOT NULL DEFAULT '' COMMENT '账单编号',
  `invoice_amount` decimal(12,4) NOT NULL COMMENT '开票金额',
  `active` tinyint NOT NULL DEFAULT '1' COMMENT '是否逻辑删除',
  PRIMARY KEY (`id`) USING BTREE,
  KEY `idx_bill_no` (`bill_no`) USING BTREE
) ENGINE=InnoDB COMMENT='客户账单明细';

CREATE TABLE `c_bill` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
  `bill_no` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '对账单编号',
  `should_receive_amount` decimal(12,4) NOT NULL COMMENT '应收总额',
  `actual_should_receive_amount` decimal(12,4) NOT NULL COMMENT '实际应收金额',
  `invoice_status` tinyint DEFAULT NULL COMMENT '开票状态(字典:invoice-status)',
  `invoice_amount` decimal(12,4) NOT NULL COMMENT '开票金额',
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE KEY `uk_bill_no` (`bill_no`) USING BTREE
) ENGINE=InnoDB COMMENT='客户账单';

c_bill表跟c_bill_detail表是1对多的关系,c_bill表中的invoice_amount是由c_bill_detail表中的invoice_amount统计出来的。
统计sql如下:

UPDATE c_bill
      SET invoice_amount = (SELECT ifnull(sum(invoice_amount), 0)
                            FROM c_bill_detail
                            WHERE bill_no = #{billNo}
                              AND active = 1
                              AND order_type in ('sale_order', 'supplement_order', 'subject_sale_order')),
          invoice_date   = #{invoiceDate},
          invoice_status =
              CASE
                  WHEN invoice_amount = should_receive_amount THEN 1
                  WHEN invoice_amount = 0 THEN 0
                  ELSE 2
                  END
      where bill_no = #{billNo}
        and active = 1;

业务层面,账单进行开发票操作后,会更新c_bill_detail表跟c_bill

问题描述

有一天线上出现告警:
在这里插入图片描述
从日志上看发生了死锁,通过定位代码发现跟执行以下sql有关:

UPDATE c_bill
      SET invoice_amount = (SELECT ifnull(sum(invoice_amount), 0)
                            FROM c_bill_detail
                            WHERE bill_no = #{billNo}
                              AND active = 1
                              AND order_type in ('sale_order', 'supplement_order', 'subject_sale_order')),
          invoice_date   = #{invoiceDate},
          invoice_status =
              CASE
                  WHEN invoice_amount = should_receive_amount THEN 1
                  WHEN invoice_amount = 0 THEN 0
                  ELSE 2
                  END
      where bill_no = #{billNo}
        and active = 1;

通过数据库锁分析得到如下信息:
在这里插入图片描述
从上面信息可以得到以下信息:

  • 事务1等待c_bill_detail表的S锁,该锁对应的索引名称是 PRIMARY(也就是主键索引,id)
  • 事务1持有c_bill表的X锁,该锁对应的索引名称是uk_bill_no
  • 事务2等待c_bill表的X锁,该锁对应的索引名称为uk_bill_no
  • 事务2持有c_bill_detail表额S锁,该锁对应的索引名称是PRIMARY(也就是主键索引,id)

通过上面可以看出,事务1跟事务2直接的锁进入了死循环,形成了死锁。

原因分析:

死锁数据分析

上面的途中,给出了死锁有关的两个索引:c_bill_detail表的主键索引,跟c_bill表的主键索引。c_bill表知道是执行了上面提到的统计sql,那么,c_bill_detail表是执行了什么操作呢?

首先通过审计找出当时这两个事务的操作:
在这里插入图片描述
上面是线程3213915(事务A)的有关操作,可以看到对c_bill_detail表有如下更新:

-- SQL
UPDATE
  c_bill_detail
SET
  receivable_date = '2023-11-15 00:00:00',
  invoice_status = 2,
  invoice_amount = 305412,
  updater = '管理员',
  updater_code = 'ADMINISTRATOR',
  update_time = '2023-12-08 17:47:52.382000'
WHERE id = 146947
  AND active = 1

线程3213754(事务B)操作如下
在这里插入图片描述

UPDATE
  c_bill_detail
SET
  receivable_date = '2023-11-15 00:00:00',
  invoice_status = 2,
  invoice_amount = 305412,
  updater = '管理员',
  updater_code = 'ADMINISTRATOR',
  update_time = '2023-12-08 17:47:52.381000'
WHERE id = 147471
  AND active = 1;

从上面可以看出事务A对表c_bill_detailid = 146947数据进行了更新,事务B对表c_billid=147471进行了更新。

通过审计日志还发现,事务A跟事务B也都更新了c_bill表,而且都是更新了bill_no=XSZD202309070005这一行数据。

事务A:
在这里插入图片描述

UPDATE
  c_bill
SET
  invoice_amount =
  (SELECT
    IFNULL(SUM(invoice_amount), 0)
  FROM
    c_bill_detail
  WHERE bill_no = 'XSZD202309070005'
    AND active = 1
    AND order_type IN (
      'sale_order',
      'supplement_order',
      'subject_sale_order'
    )),
  invoice_date = '2023-12-08 00:00:00',
  invoice_status =
  CASE
    WHEN invoice_amount = should_receive_amount
    THEN 1
    WHEN invoice_amount = 0
    THEN 0
    ELSE 2
  END
WHERE bill_no = 'XSZD202309070005'
  AND active = 1;

事务B:

在这里插入图片描述

-- SQL
UPDATE
  c_bill
SET
  invoice_amount =
  (SELECT
    IFNULL(SUM(invoice_amount), 0)
  FROM
    c_bill_detail
  WHERE bill_no = 'XSZD202309070005'
    AND active = 1
    AND order_type IN (
      'sale_order',
      'supplement_order',
      'subject_sale_order'
    )),
  invoice_date = '2023-12-08 00:00:00',
  invoice_status =
  CASE
    WHEN invoice_amount = should_receive_amount
    THEN 1
    WHEN invoice_amount = 0
    THEN 0
    ELSE 2
  END
WHERE bill_no = 'XSZD202309070005'
  AND active = 1;

上图可以看出,事务B最终在更新c_bill时失败回滚了(因为发生了死锁)。

通过查看数据发现,c_bill_detailid = 146947id=147471对应的bill_no都是XSZD202309070005
在这里插入图片描述
到这里,只是发现了数据上的关联,还是不知道为什么会出现死锁,下面在其他环境进行复现。

select语句添加了共享读锁

为了更好复现这个死锁情况,现将线上的sql执行顺序整理如下:

在这里插入图片描述

下面在本地数据库,选取c_bill_detailid=19380id=19381,这两条数据有相同的bill_no=XSZD202211226768

在这里插入图片描述
开启两个事务,分别按照上面表格的sql数据进行执行,同时观察锁情况:

事务A 更新id = 19380:

Database changed
mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> UPDATE
    ->   c_bill_detail
    -> SET
    ->   receivable_date = '2023-11-15 00:00:00',
    ->   invoice_status = 2,
    ->   invoice_amount = 305412,
    ->   updater = '管理员',
    ->   updater_code = 'ADMINISTRATOR',
    ->   update_time = '2023-12-08 17:47:52.382000'
    -> WHERE id = 19380
    ->   AND active = 1;
Query OK, 1 row affected (0.03 sec)
Rows matched: 1  Changed: 1  Warnings: 0

事务B更新id = 19381的记录

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> UPDATE
    ->   c_bill_detail
    -> SET
    ->   receivable_date = '2023-11-15 00:00:00',
    ->   invoice_status = 2,
    ->   invoice_amount = 305412,
    ->   updater = '管理员',
    ->   updater_code = 'ADMINISTRATOR',
    ->   update_time = '2023-12-08 17:47:52.381000'
    -> WHERE id = 19381
    ->   AND active = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

这是观察锁情况

mysql> select * from performance_schema.data_locks\G;
*************************** 1. row ***************************
  // 省略表意向锁
*************************** 2. row ***************************
               ENGINE: INNODB
       ENGINE_LOCK_ID: 139645347952208:230:8:12:139645358792496
ENGINE_TRANSACTION_ID: 65810
            THREAD_ID: 563867
             EVENT_ID: 34
        OBJECT_SCHEMA: fresh
          OBJECT_NAME: c_bill_detail
       PARTITION_NAME: NULL
    SUBPARTITION_NAME: NULL
           INDEX_NAME: PRIMARY
OBJECT_INSTANCE_BEGIN: 139645358792496
            LOCK_TYPE: RECORD
            LOCK_MODE: X,REC_NOT_GAP
          LOCK_STATUS: GRANTED
            LOCK_DATA: 19381
*************************** 3. row ***************************
       // 省略表锁
*************************** 4. row ***************************
               ENGINE: INNODB
       ENGINE_LOCK_ID: 139645347951400:230:8:9:139645358786480
ENGINE_TRANSACTION_ID: 65809
            THREAD_ID: 563866
             EVENT_ID: 34
        OBJECT_SCHEMA: fresh
          OBJECT_NAME: c_bill_detail
       PARTITION_NAME: NULL
    SUBPARTITION_NAME: NULL
           INDEX_NAME: PRIMARY
OBJECT_INSTANCE_BEGIN: 139645358786480
            LOCK_TYPE: RECORD
            LOCK_MODE: X,REC_NOT_GAP
          LOCK_STATUS: GRANTED
            LOCK_DATA: 19380
4 rows in set (0.00 sec)

从上面看出,c_bill_detail表的id=19381id=19380的数据加上了X锁,这是意料之中的。

接下来事务A执行更新c_bill

mysql> UPDATE
    ->   c_bill
    -> SET
    ->   invoice_amount =
    ->   (SELECT
    ->     IFNULL(SUM(invoice_amount), 0)
    ->   FROM
    ->     c_bill_detail
    ->   WHERE bill_no = 'XSZD202211226768'
    ->     AND active = 1
    ->     AND order_type IN (
    ->       'sale_order',
    ->       'supplement_order',
    ->       'subject_sale_order'
    ->     )),
    ->   invoice_date = '2023-12-08 00:00:00',
    ->   invoice_status =
    ->   CASE
    ->     WHEN invoice_amount = should_receive_amount
    ->     THEN 1
    ->     WHEN invoice_amount = 0
    ->     THEN 0
    ->     ELSE 2
    ->   END
    -> WHERE bill_no = 'XSZD202211226768'
    ->   AND active = 1;

此时事务A发生了阻塞
在这里插入图片描述

这时查看锁情况:

mysql> select * from performance_schema.data_locks\G;
*************************** 1. row ***************************
           // 表锁
*************************** 2. row ***************************
               ENGINE: INNODB
       ENGINE_LOCK_ID: 139645347952208:230:8:16:139645358792496
ENGINE_TRANSACTION_ID: 65820
            THREAD_ID: 563867
             EVENT_ID: 43
        OBJECT_SCHEMA: fresh
          OBJECT_NAME: c_bill_detail
       PARTITION_NAME: NULL
    SUBPARTITION_NAME: NULL
           INDEX_NAME: PRIMARY
OBJECT_INSTANCE_BEGIN: 139645358792496
            LOCK_TYPE: RECORD
            LOCK_MODE: X,REC_NOT_GAP
          LOCK_STATUS: GRANTED
            LOCK_DATA: 19381
*************************** 3. row ***************************
               //c_bill 表意向锁
*************************** 4. row ***************************
              // c_bill_detail表意向锁
*************************** 5. row ***************************
               ENGINE: INNODB
       ENGINE_LOCK_ID: 139645347951400:230:8:15:139645358786480
ENGINE_TRANSACTION_ID: 65819
            THREAD_ID: 563866
             EVENT_ID: 44
        OBJECT_SCHEMA: fresh
          OBJECT_NAME: c_bill_detail
       PARTITION_NAME: NULL
    SUBPARTITION_NAME: NULL
           INDEX_NAME: PRIMARY
OBJECT_INSTANCE_BEGIN: 139645358786480
            LOCK_TYPE: RECORD
            LOCK_MODE: X,REC_NOT_GAP
          LOCK_STATUS: GRANTED
            LOCK_DATA: 19380
*************************** 6. row ***************************
               ENGINE: INNODB
       ENGINE_LOCK_ID: 139645347951400:229:5:6:139645358786824
ENGINE_TRANSACTION_ID: 65819
            THREAD_ID: 563866
             EVENT_ID: 45
        OBJECT_SCHEMA: fresh
          OBJECT_NAME: c_bill
       PARTITION_NAME: NULL
    SUBPARTITION_NAME: NULL
           INDEX_NAME: uk_bill_no
OBJECT_INSTANCE_BEGIN: 139645358786824
            LOCK_TYPE: RECORD
            LOCK_MODE: X,REC_NOT_GAP
          LOCK_STATUS: GRANTED
            LOCK_DATA: 'XSZD202211226768', 5117
*************************** 7. row ***************************
               ENGINE: INNODB
       ENGINE_LOCK_ID: 139645347951400:229:7:6:139645358787168
ENGINE_TRANSACTION_ID: 65819
            THREAD_ID: 563866
             EVENT_ID: 45
        OBJECT_SCHEMA: fresh
          OBJECT_NAME: c_bill
       PARTITION_NAME: NULL
    SUBPARTITION_NAME: NULL
           INDEX_NAME: PRIMARY
OBJECT_INSTANCE_BEGIN: 139645358787168
            LOCK_TYPE: RECORD
            LOCK_MODE: X,REC_NOT_GAP
          LOCK_STATUS: GRANTED
            LOCK_DATA: 5117
*************************** 8. row ***************************
               ENGINE: INNODB
       ENGINE_LOCK_ID: 139645347951400:230:58:117:139645358787512
ENGINE_TRANSACTION_ID: 65819
            THREAD_ID: 563866
             EVENT_ID: 45
        OBJECT_SCHEMA: fresh
          OBJECT_NAME: c_bill_detail
       PARTITION_NAME: NULL
    SUBPARTITION_NAME: NULL
           INDEX_NAME: idx_bill_no
OBJECT_INSTANCE_BEGIN: 139645358787512
            LOCK_TYPE: RECORD
            LOCK_MODE: S
          LOCK_STATUS: GRANTED
            LOCK_DATA: 'XSZD202211226768', 19380
*************************** 9. row ***************************
               ENGINE: INNODB
       ENGINE_LOCK_ID: 139645347951400:230:58:118:139645358787512
ENGINE_TRANSACTION_ID: 65819
            THREAD_ID: 563866
             EVENT_ID: 45
        OBJECT_SCHEMA: fresh
          OBJECT_NAME: c_bill_detail
       PARTITION_NAME: NULL
    SUBPARTITION_NAME: NULL
           INDEX_NAME: idx_bill_no
OBJECT_INSTANCE_BEGIN: 139645358787512
            LOCK_TYPE: RECORD
            LOCK_MODE: S
          LOCK_STATUS: GRANTED
            LOCK_DATA: 'XSZD202211226768', 19381
*************************** 10. row ***************************
               ENGINE: INNODB
       ENGINE_LOCK_ID: 139645347951400:230:8:16:139645358787856
ENGINE_TRANSACTION_ID: 65819
            THREAD_ID: 563866
             EVENT_ID: 45
        OBJECT_SCHEMA: fresh
          OBJECT_NAME: c_bill_detail
       PARTITION_NAME: NULL
    SUBPARTITION_NAME: NULL
           INDEX_NAME: PRIMARY
OBJECT_INSTANCE_BEGIN: 139645358787856
            LOCK_TYPE: RECORD
            LOCK_MODE: S,REC_NOT_GAP
          LOCK_STATUS: WAITING
            LOCK_DATA: 19381
10 rows in set (0.00 sec)

从上面的Row10发现,事务A跟表c_bill_detail的id = 19381的记录添加了S锁,并且在锁状态为等待状态。

接着事务B也执行更新c_bill表,发现就会出现死锁的情况。
在这里插入图片描述
到这里总结上面的持锁过程:

  • 事务A先持有t_bill_detailid=19380X
  • 接着事务B持有t_bll_detailid=19381X锁,与上一把没有存在锁竞争,都能正常执行
  • 事务A更新c_bill,这时不仅给表c_bill表的bill_no=XSZD202211226768的记录加上了X锁,同时也给c_bill_detailid=19381的记录添加了S锁,并且处于等待状态。
  • 事务B更新c_bill同样会给c_bill_detailid=19380的记录添加S锁。

这里有一下几点需要注意:

  1. S锁跟X锁不兼容,会出现锁等待的情况。
  2. 普通的select语句是不加锁的,但是在update语句中进行select查询赋值,这时的select就会添加上共享锁。
  3. 共享锁主要是保证每次读取的都是最新的值(读取时不支持修改)。

以上就是生成环境形成死锁的分析过程。关于X锁跟S锁的更多说明,可以参考:Innodb中的锁

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1307355.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

中华鲲鹏 深耕湾区 华鲲振宇重磅亮相第二届数字政府建设峰会

12月8日,由国家互联网信息办公室指导,广东省人民政府、香港特别行政区政府、澳门特别行政区政府联合主办的第二届数字政府建设峰会暨“数字湾区”发展论坛在广州开幕。第十二届全国政协副主席、国家电子政务专家委员会主任王钦敏,中央宣传部副部长、中央网信办主任、国家网信办…

Profibus、Profinet、Ethernet有什么区别?

PROFINET 是一种新的以太网通讯系统,是由西门子公司和 Profibus 用户协会开发。 PROFINET 具有多制造商产品之间的通讯能力,自动化和工程模式,并针对分布式智能自动化系统进行了优化。其应用结果能够大大节省配置和调试费用。 PROFINET 系统集…

@svelte-dev/auth 一个简单好用的 Svelte 身份管理库

特性 完全的服务器端身份验证完整的TypeScript支持策略-基础身份验证轻松处理成功和失败实现自定义策略支持持久会话概述 Svelte Auth是一个完整的开源身份验证解决方案,适用于Svelte应用程序。 深受Passport.js和Remix-Auth的启发,但完全从头开始重写,以便在Web Fetch AP…

el-date-picker 限制选择范围最大为一年,设置快捷选项,设置默认时间

el-date-picker 限制选择范围最大为一年&#xff1a; 主要代码为&#xff1a;:picker-options"pickerOptions" 以及 blur"pickerBlur" <el-date-pickerv-model"transactionTime"type"daterange"style"width: 200px"size…

数字工厂管理系统建设层级分为哪几层

随着工业4.0时代的到来&#xff0c;数字工厂已成为制造业转型升级的必经之路。数字工厂管理系统作为数字工厂的核心组成部分&#xff0c;对于提高生产效率、降低成本、提升质量等方面具有重要意义。数字工厂管理系统的建设层级一般分为以下几个层次&#xff0c;本文将对其进行详…

LLM Agent发展演进历史(观看metagpt视频笔记)

LLM相关的6篇重要的论文&#xff0c;其中4篇来自谷歌&#xff0c;2篇来自openai。技术路径演进大致是&#xff1a;SSL (Self-Supervised Learning) -> SFT (Supervised FineTune) IT (Instruction Tuning) -> RLHF。 word embedding的问题&#xff1a;新词如何处理&…

【计算思维】第14届蓝桥杯省赛计算思维U10组真题

选择题 第 1 题 单选题 晶晶在注册一个学习网站时&#xff0c;需要设置密码。 网站提示&#xff1a; 密码必须由 8~16 个字符组成&#xff0c;可以包含数字、大写字母、小写字母、特殊符号这 4 种 字符类型。 包含 4 种不同类型字符的密码是强密码&#xff1b; 包含 2 种或 …

全国合作商标服大赛决赛完整规则流程

本文是全国合作商标服大赛决赛完整规则流程&#xff0c;有需要的朋友可以参考下。 一、抢答比拼 1、政策管理考核题 系统评分。抢答题共15题/条线&#xff1a;单选10题&#xff0c;多选5题&#xff0c;基础分100分 单选答对10分/答错-5分&#xff0c;多选答对20分/答错-10分…

编译原理lab3-cminus_compiler-LLVM简要熟悉

lab3实验报告&#xff0c;我的实验报告图例很少&#xff0c;这次只有两张图&#xff0c;其余的都以复制输出的形式展现出来了&#xff0c;最终提交的代码在最后 [[#你的提交|你的提交]][[#实验设计|实验设计]][[#提交一&#xff1a;手动编写.ll|提交一&#xff1a;手动编写.ll…

Redis - 做缓存时高并发问题:缓存穿透、击穿、雪崩,数据库缓存双写不一致

缓存穿透 当用户访问的数据既不在缓存也不在数据库中时&#xff0c;就会导致每个用户查询都会“穿透” 缓存“直抵”数据库。这种情况就称为缓存穿透。当高度发的访问请求到达时&#xff0c;缓存穿透不 仅增加了响应时间&#xff0c;而且还会引发对 DBMS 的高并发查询&…

frp配置内网穿透步骤

frp配置内网穿透步骤 1.环境准备1.1 云服务器1.2 frp包 2. frp安装2.1 server服务端设置2.2 客户端配置 实现目标通过云服务器ip:8080访问内网电脑启动的web项目localhost:8080 1.环境准备 1.1 云服务器 服务器安装centos7.9, 安全组入口方向开通 7500 7000 8080 8060端口 …

CSDN云账号签约流程

在电脑端上进行提现操作&#xff0c;按提示进行即可。 问题 手机端无法签约。 电脑签约

【详解优先级队列(堆)】

目录 堆的概念 堆的性质 堆的存储方式 堆的创建 堆的向下调整 向下过程(以小堆为例) 向下过程(以大堆为例) 建堆的时间复杂度O(n) 堆的插入与删除 堆的插入 向上调整建堆的时间复杂度O(nlogn) 堆的删除 常见习题 常用接口介绍 PriorityQueue的特性 Pri…

HNU计算机视觉作业一

前言 选修的是蔡mj老师的计算机视觉&#xff0c;上课还是不错的&#xff0c;但是OpenCV可能需要自己学才能完整把作业写出来。由于没有认真学&#xff0c;这门课最后混了80多分&#xff0c;所以下面作业解题过程均为自己写的&#xff0c;并不是标准答案&#xff0c;仅供参考 …

lammps编译(2Aug2023、intel2020、rtx4070ti)

说明&#xff1a; [rootnode101 ~]# cat /etc/redhat-release CentOS Linux release 7.9.2009 (Core) [rootnode101 ~]# gcc -v Using built-in specs. COLLECT_GCCgcc COLLECT_LTO_WRAPPER/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/lto-wrapper Target: x86_64-redhat-lin…

双十二哪个牌子的电视盒子好用?测评员总结电视盒子品牌排行榜

每次电商大促我会分享好物推荐&#xff0c;本期我要盘点的数码产品是电视盒子&#xff0c;电视盒子的功能和重要性大家都懂&#xff0c;但电视盒子如何选就不太了解了&#xff0c;我根据今年20多次的测评结果整理了电视盒子品牌排行榜&#xff0c;看看哪个牌子的电视盒子好用吧…

beebox靶场A3 low级别 xss通关教程(二)

六&#xff1a;xss get型 eval 通过观察我们可以发现url地址中存在一个date函数 那我们可以试一下把后面的date()函数去掉&#xff0c;直接写入一个alert(555) 发现直接弹出一个框&#xff0c;证明有xss漏洞 七&#xff1a;xss href 直接进入页面会看到是get方法&#xff0c…

HarmonyOS4.0从零开始的开发教程12给您的应用添加弹窗

HarmonyOS&#xff08;十&#xff09;给您的应用添加弹窗 概述 在我们日常使用应用的时候&#xff0c;可能会进行一些敏感的操作&#xff0c;比如删除联系人&#xff0c;这时候我们给应用添加弹窗来提示用户是否需要执行该操作&#xff0c;如下图所示&#xff1a; 弹窗是一种…

【51单片机系列】直流电机使用

本文是关于直流电机使用的相关介绍。 文章目录 一、直流电机介绍二、ULN2003芯片介绍三、在proteus中仿真实现对电机的驱动 51单片机的应用中&#xff0c;电机控制方面的应用也很多。在学习直流电机(PWM)之前&#xff0c;先使用GPIO控制电机的正反转和停止。但不能直接使用GPIO…

飞天使-linux操作的一些技巧与知识点4-ansible常用的技巧,配置等

文章目录 ansible配置文件的优先级尝试开始进行操作ansible常用模块ansible 的playbook示例安装phpplaybook中变量的引用 ansible yum install -y ansible 测试是否可用 ansible localhost -m ping /etc/ansible/ansible.cfg &#xff1a;主配置文件&#xff0c;配置 ansible…