文章目录

  • 目标
  • 死锁
    • 案例
    • 实现需求1
      • 死锁演示:排他锁的相互等待
      • 查看最近一次死锁记录
    • 并发测试
      • jmeter数据库事务并发测试
        • 步骤一 配置jmeter 连接数据库
        • 步骤二:添加线程组,添加jdbc请求和监听器
      • 执行测试以及结果
        • 察看结果树
        • 用表格察看结果
        • 聚合报告
    • 死锁问题优化
    • 优化死锁-实现需求1
      • 并发测试
    • 总结

目标

  • 了解常见死锁场景和排查解决方法

    参考:一次mysql死锁的排查过程

    【MySQL】如何阅读死锁日志

    连接数据库时allowMultiQueries=true的作用

死锁

死锁是一种不同事务无法继续进行的情况,因为每个事务都持有另一个事务所需的锁。因为两个事务都在等待资源可用,所以它们都不会释放它所持有的锁。

当事务锁定多个表中的行(通过UPDATE或SELECT…FOR UPDATE等语句)但顺序相反时,可能会发生死锁。当这样的语句锁定索引记录和间隙的范围时,也会发生死锁,因为每个事务都会由于时间问题而获得一些锁。

画外音: 可以从java死锁的场景理解。

死锁一般分为俩种场景:并发事务的排他锁的互相等待,并发事务的共享锁与排他锁排队等待。下面通过两个案例来演示:

案例

需求1:有一个现金抽奖服务,五人一个小队,小队每人都可以抽奖,但是抽到的金额,需要平分给五个队员。

需求2:每抽奖一次,平分奖金后,计算一次现在小组内每个人的奖金大于10元,就移出当前小组,不允许再参加抽奖。

测试环境: Mysq5.7 版本(华为云 1 vCPU | 2 GB )

从模拟案例来展示下常见死锁场景。

  • 初始化案例sql表和数据
CREATE TABLE `t_lockdraw_money` (`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',`user_name` varchar(64) DEFAULT NULL COMMENT '姓名',`money` int(20) DEFAULT NULL COMMENT '金额',`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',`group_id` bigint(20) DEFAULT NULL COMMENT '用户id',PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8;INSERT INTO `lock_test`.`t_lockdraw_money` (`id`, `user_name`, `money`, `create_time`, `update_time`, `group_id`) VALUES ('3', '张三', '1', '2021-11-05 03:04:05', '2021-11-09 05:47:34', '1');
INSERT INTO `lock_test`.`t_lockdraw_money` (`id`, `user_name`, `money`, `create_time`, `update_time`, `group_id`) VALUES ('4', '李四', '1', '2021-11-05 03:04:17', '2021-11-09 05:47:35', '1');
INSERT INTO `lock_test`.`t_lockdraw_money` (`id`, `user_name`, `money`, `create_time`, `update_time`, `group_id`) VALUES ('5', '王五', '1', '2021-11-05 03:04:30', '2021-11-09 05:47:37', '1');
INSERT INTO `lock_test`.`t_lockdraw_money` (`id`, `user_name`, `money`, `create_time`, `update_time`, `group_id`) VALUES ('6', '赵六', '111', '2021-11-05 03:04:50', '2021-11-05 03:04:50', '2');
INSERT INTO `lock_test`.`t_lockdraw_money` (`id`, `user_name`, `money`, `create_time`, `update_time`, `group_id`) VALUES ('7', '孙七', '35', '2021-11-05 03:05:47', '2021-11-05 03:05:47', '2');
INSERT INTO `lock_test`.`t_lockdraw_money` (`id`, `user_name`, `money`, `create_time`, `update_time`, `group_id`) VALUES ('8', '周八', '222', '2021-11-05 03:06:03', '2021-11-09 06:00:39', '2');

特别注意:由于篇目问题,此篇只演示需求1的死锁情况。

实现需求1

假设一次抽奖后,增加小组1(group_id=1)的成员金额,每人增加五元。忽略业务代码实现,假定抽象后的sql为:

START TRANSACTION;
-- 增加5元
UPDATE t_lockdraw_money
SET money = money + 5
WHEREid = 3;UPDATE t_lockdraw_money
SET money = money + 5
WHEREid = 4;UPDATE t_lockdraw_money
SET money = money + 5
WHEREid = 5;COMMIT;

这里使用了事务,保证平分金额要么成功,要么失败。前端传参(3,4,5),所以对这个三条数据进行了金额的增加。但是,由于前端传小组成员id时,不一定是(3,4,5)的顺序,可能传的是 (4,3,5) 或者 (5,3,4)等等。所以就可能产生死锁。

死锁演示:排他锁的相互等待

事务1 事务2
START TRANSACTION;
– 增加5元
UPDATE t_lockdraw_money
SET money = money + 5
WHERE
id = 3;

UPDATE t_lockdraw_money
SET money = money + 5
WHERE
id = 4;

UPDATE t_lockdraw_money
SET money = money + 5
WHERE
id = 5;

COMMIT;

START TRANSACTION;
– 增加5元
UPDATE t_lockdraw_money
SET money = money + 5
WHERE
id = 4;

UPDATE t_lockdraw_money
SET money = money + 5
WHERE
id = 3;

UPDATE t_lockdraw_money
SET money = money + 5
WHERE
id = 5;

COMMIT;

并发执行事务,使用以下sql执行顺序:

事务1 事务2
START TRANSACTION;
– 增加5元
UPDATE t_lockdraw_money SET money = money + 5 WHERE id = 3; – 更新id为3的金额增加5
– 事务1持有了id为3 的互斥记录锁(X锁)
START TRANSACTION;
– 增加5元
UPDATE t_lockdraw_money SET money = money + 5 WHERE id = 4; --更新id为4的金额增加5
– 事务2持有了id为4这一行的 互斥记录锁(X锁)
UPDATE t_lockdraw_money SET money = money + 5 WHERE id = 4; --更新id为4的金额增加5
– 事务1申请id为4这一行的互斥记录锁(X锁),发现此行已经被事务2锁定,进入等待
UPDATE t_lockdraw_money SET money = money + 5 WHERE id = 3; – 更新id为3的金额增加5
– 事务2申请id为3这一行的互斥记录锁(X 锁),发现此行已经被事务1锁定,进入等待
[Err] 1213 - Deadlock found when trying to get lock; try restarting transaction – 尝试获取锁时发现死锁;尝试重新启动事务
– mysql 检测到发生死锁,并回滚事务,打破死锁
由于事务2被mysql检测到死锁,进行回滚。现在事务1可以正常获取排他锁,继续执行剩余sql

上述两个事务执行顺序,和加锁过程分析,是因为不同事务持有了对方需要的锁,就导致了死锁。

画外音:跟java中死锁场景一样,相互等待对方持有的锁释放。

可以手动在Navicat中按照上面的执行顺序,测试下。

查看最近一次死锁记录

查看InnoDB用户事务中的最后一个死锁

mysql>show engine innodb status;

官方文档给出的文档:InnoDB 标准监视器和锁监视器输出

通过此命令查看下刚才死锁信息,对于输出的内容只关注 TRANSACTION 部分的即可,其他不再关注。

TRANSACTION 提供跟踪事务死锁的原因。

*** TRANSACTION:
TRANSACTION 11574497, ACTIVE 17 sec starting index read, thread declared inside InnoDB 5000
mysql tables in use 1, locked 1
3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1
MySQL thread id 645222, OS thread handle 139895901488896, query id 2676707 114.254.3.57 root updating
UPDATE t_lockdraw_money
SET money = money + 5
WHEREid = 3
*** HOLDS THE LOCK:
RECORD LOCKS space id 500 page no 3 n bits 80 index PRIMARY of table `lock_test`.`t_lockdraw_money` trx id 11574497 lock_mode X locks rec but not gap
Record lock, heap no 5 PHYSICAL RECORD: n_fields 8; compact format; info bits 00: len 8; hex 8000000000000004; asc         ;;1: len 6; hex 000000b09ce1; asc       ;;2: len 7; hex 33000001c528b8; asc 3    ( ;;3: len 6; hex e69d8ee59b9b; asc       ;;4: len 4; hex 80000006; asc     ;;5: len 5; hex 99ab0a3111; asc    1 ;;6: len 5; hex 99ab166477; asc    dw;;7: len 8; hex 8000000000000001; asc         ;;***  WAITING FOR THIS LOCK TO BE GRANTED, WHICH CONFLICTS WITH THE LOCK HELD BY TRANSACTION 11574496:
RECORD LOCKS space id 500 page no 3 n bits 80 index PRIMARY of table `lock_test`.`t_lockdraw_money` trx id 11574497 lock_mode X locks rec but not gap waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 8; compact format; info bits 00: len 8; hex 8000000000000003; asc         ;;1: len 6; hex 000000b09ce0; asc       ;;2: len 7; hex 32000001cc2660; asc 2    &`;;3: len 6; hex e5bca0e4b889; asc       ;;4: len 4; hex 80000006; asc     ;;5: len 5; hex 99ab0a3105; asc    1 ;;6: len 5; hex 99ab166473; asc    ds;;7: len 8; hex 8000000000000001; asc         ;;*** TRANSACTION:
TRANSACTION 11574496, ACTIVE 21 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1
MySQL thread id 645216, OS thread handle 139895778895616, query id 2676689 114.254.3.57 root updating
UPDATE t_lockdraw_money
SET money = money + 5
WHEREid = 4
*** HOLDS THE LOCK:
RECORD LOCKS space id 500 page no 3 n bits 80 index PRIMARY of table `lock_test`.`t_lockdraw_money` trx id 11574496 lock_mode X locks rec but not gap
Record lock, heap no 2 PHYSICAL RECORD: n_fields 8; compact format; info bits 00: len 8; hex 8000000000000003; asc         ;;1: len 6; hex 000000b09ce0; asc       ;;2: len 7; hex 32000001cc2660; asc 2    &`;;3: len 6; hex e5bca0e4b889; asc       ;;4: len 4; hex 80000006; asc     ;;5: len 5; hex 99ab0a3105; asc    1 ;;6: len 5; hex 99ab166473; asc    ds;;7: len 8; hex 8000000000000001; asc         ;;***  WAITING FOR THIS LOCK TO BE GRANTED, WHICH CONFLICTS WITH THE LOCK HELD BY TRANSACTION 11574497:
RECORD LOCKS space id 500 page no 3 n bits 80 index PRIMARY of table `lock_test`.`t_lockdraw_money` trx id 11574496 lock_mode X locks rec but not gap waiting
Record lock, heap no 5 PHYSICAL RECORD: n_fields 8; compact format; info bits 00: len 8; hex 8000000000000004; asc         ;;1: len 6; hex 000000b09ce1; asc       ;;2: len 7; hex 33000001c528b8; asc 3    ( ;;3: len 6; hex e69d8ee59b9b; asc       ;;4: len 4; hex 80000006; asc     ;;5: len 5; hex 99ab0a3111; asc    1 ;;6: len 5; hex 99ab166477; asc    dw;;7: len 8; hex 8000000000000001; asc         ;;*** WE ROLL BACK TRANSACTION 11574497

先拆分下日志模块:

  • *** TRANSACTION 开头后,代表一个事务

    • *** HOLDS THE LOCK: 下的内容,代表这个事务持有的锁
    • *** WAITING FOR THIS LOCK … 下的内容,代表这个事务等待的锁,并指出与那个事务持有锁冲突

先分析第一个事务:TRANSACTION 11574497 第一块内容

TRANSACTION 11574497, ACTIVE 17 sec starting index read, thread declared inside InnoDB 5000
-- 事务编号为 11574497,活跃了17秒,starting index read 表示事务状态为根据索引读取数据 thread declared inside InnoDB 说明事务已经进入innodb层。
------------------------------------------------
-- 常见的其他状态:
--fetching rows 表示事务状态在row_search_for_mysql中被设置,表示正在查找记录。
--updating or deleting 表示事务已经真正进入了Update/delete的函数逻辑(row_update_for_mysql)
--thread declared inside InnoDB 说明事务已经进入innodb层。通常而言 不在innodb层的事务大部分是会被回滚的。
------------------------------------------------
mysql tables in use 1, locked 1
-- 当前的事务使用一个表。locked 1 表示表上有一个表锁,对于DML语句为LOCK_IX
3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1
-- 3 lock struct(s) 表示该事务的锁链表的长度为3,每个链表节点代表该事务持有的一个锁结构,包括表锁,记录锁以及 autoinc 锁等 。heap size 1136 表示事务分配的锁堆内存大小,一般没有什么具体的用处。
-- 2 row lock(s) 表示当前事务持有的行记录锁/gap 锁的个数。
MySQL thread id 645222, OS thread handle 139895901488896, query id 2676707 114.254.3.57 root updating
-- MySQL thread id 645222  表示MySQL的进程ID,query id 2676707 表示SQL的id
-- 114.254.3.57 root updating 表示root@'114.254.3.57'执行的 update操作
UPDATE t_lockdraw_money
SET money = money + 5
WHEREid = 3
-- 表示事务中正在执行(等待)的SQL

HOLDS THE LOCK 部分:

*** HOLDS THE LOCK:  持有锁
RECORD LOCKS space id 500 page no 3 n bits 80 index PRIMARY of table `lock_test`.`t_lockdraw_money` trx id 11574497 lock_mode X locks rec but not gap
-- 在`lock_test`.`t_lockdraw_money` 这个表的主键索引上,事务11574497持有记录排他锁(x锁)并且不对间隙加锁(就是记录锁(X))
Record lock, heap no 5 PHYSICAL RECORD: n_fields 8; compact format; info bits 0
-- 记录锁的物理记录信息
...

WAITING FOR THIS LOCK… 部分

***  WAITING FOR THIS LOCK TO BE GRANTED, WHICH CONFLICTS WITH THE LOCK HELD BY TRANSACTION 11574497:
-- 正在等待授予此锁,这与事务11574496持有的锁冲突:
RECORD LOCKS space id 500 page no 3 n bits 80 index PRIMARY of table `lock_test`.`t_lockdraw_money` trx id 11574496 lock_mode X locks rec but not gap waiting
-- 在`lock_test`.`t_lockdraw_money` 这个表的主键索引上,事务11574496持有记录排他锁(x锁)并且不对间隙加锁(就是记录锁(X))
Record lock, heap no 5 PHYSICAL RECORD: n_fields 8; compact format; info bits 0

从上述内容可以知道,事务11574497申请 id =3 主键索引上的锁,与事务11574496持有的锁冲突。也就是事务11574496现在持有id=3主键索引上的锁。

第二个事务内容与第一个事务内容大致一样:事务11574496申请 id =4 主键索引上的锁,与事务11574497持有的锁冲突。也就是事务11574497现在持有id=4主键索引上的锁。

注意最后一句:

*** WE ROLL BACK TRANSACTION 11574497
-- 我们将回滚事务11574497

通过上述日志,可以定位死锁问题的sql,但是由于show engine innodb status输出来的死锁日志无任务事务上下文,对于复杂事务,并不能很好地诊断相关事务所持有的所有锁信息。所以对于解决死锁问题,仍需要结合业务逻辑,分析那些事务中存在这些sql,才能最终定位有问题的代码逻辑。

并发测试

在演示死锁场景时,是我们手动执行事务触发的,那么在真实生产环境,出现死锁的次数概率是什么呢?通过并发测试,观察这两个sql死锁的概率。

并发测试工具: 数据库自带有 mysqlslap 工具,或者第三方工具(jmeter)

为了更趋近与真实执行环境,我们使用jmeter 来做数据库sql并发测试,因为jmeter使用java jdbc连接数据库,执行sql测试。

对于jmeter 常见用法可以参考:jmeter

对于数据库测试,jmeter 官方文档提供的指南:建立数据库测试计划

jmeter数据库事务并发测试

测试环境: jmeter5.4 版本,Mysq5.7 版本(华为云 1 vCPU | 2 GB ),jmeter 与数据库连接为外网形式。

测试指标:受限于测试环境mysql性能限制,模拟50,75,100 数据库事务并发

关于jmeter 配置数据库具体细节,请参考之前的文章。不再过多阐述。

Mysql数据库死锁实战-Jmeter连接配置数据库[Mysql]

关于配置jmeter和mysql驱动jar,已经配置好了,可以下载使用:

mysql-connector-java-6.0.2.jar

数据库事务并发测试-死锁问题-互斥锁相互等待.jmx

步骤一 配置jmeter 连接数据库

关于jmeter的数据库配置官方文档:JDBC_Connection_Configuration

配置mysql驱动,将mysql-connector-java.jar 拷贝至jmeter bin目录下

由于需要测试数据库事务,修改了如下选项配置

jdbc:mysql://xxxx:3306/lock_test?autoReconnect=true&serverTimezone=Asia/Shanghai&allowMultiQueries=true

这里有一个max number of connection ,最大连接数默认为0,即每个sql请求,都是新一个连接。可以配置最大连接数,就跟数据库线程池的功能一致了。在测试任务开始后,会先创建这些连接,供并发测试的sql请求使用。

步骤二:添加线程组,添加jdbc请求和监听器

注意下图上所示。

画外音:query type 选 callable statment 应该是针对存储过程的,我看其他文章指出要使用这个。看了看其他的类型,感觉也不太正确。如果有那位大佬知道,可以告知下。

执行测试以及结果

为了模拟生产环境,采用数据库连接池大小 20 ,线程数50/s,75/s,100/s 并发测试

察看结果树

查看结果树能准确打印每个sql请求的请求和响应。

能看到执行的sql 语句, 和响应信息

用表格察看结果

表格形式查看,能看出那个时间执行了那些sql请求

这里能看出,执行的sql事务1和sql事务2,并不是一个一个匹配对应执行的,而是sql事务1一次性执行几次,sql事务2一次性执行几次。这也模拟了真实环境下的并发请求。

聚合报告

50并发/s

Label Samples Average Median 90% Line 95% Line 99% Line Min Max Error % Throughput Received KB/sec Sent KB/sec
sql事务1 50 44 40 66 70 93 23 93 8% 48.8/sec 3.98 0.0
sql事务2 50 39 38 45 49 97 28 97 0 50.2/sec 4.16 0.0
总体 100 41 39 64 69 93 23 97 4% 93.6/sec 7.70 0.0

75并发/s

Label # 样本 平均值 中位数 90% 百分比 95% 百分比 99% 百分比 最小值 最大值 异常% 吞吐量 接收KB/sec 发送KB/sec
sql事务1 75 46 40 69 88 101 30 121 8% 74.4/sec 6.06 0.0
sql事务2 75 42 41 50 57 61 31 68 17.33% 76.4/sec 6.10 0.0
总体 150 44 41 57 69 101 30 121 12.67% 142.9/sec 11.5 0.0

100并发/S

Label 样本 平均值 中位数 90% 百分比 95% 百分比 99% 百分比 最小值 最大值 异常% 吞吐量 接收KB/sec 发送KB/sec
sql事务1 100 59 45 104 126 166 26 176 21% 96.8/sec 7.6 0.0
sql事务2 100 49 43 57 90 171 27 182 10% 95.5/sec 7.7 0.0
总体 200 54 44 88 126 171 26 182 15.5% 183.6/sec 14.7 0.0

从上述并发测试,可以看出,随着并发的增高,出现死锁概率也会变大。可以理解,当线程并发越高,不同事务对于同一行数据,申请行锁的概率更高,不同锁之间等待时间更长,出现死锁的概率也会增加。

一些想法:从并发测试出发,真实生产环境也许对同一行数据的加锁竞争不会如此之高,但是减少死锁产生,降低事务之间锁等待时长,确实能提升服务性能和可靠性。

死锁问题优化

通过上述案例的分析,因为加锁顺序的不同,导致可能产生死锁情况。从这个角度出发,有几个优化方案:

  • 在同一个事务中,尽可能一次性锁定所有需要加锁的行

    如上述例子,执行每一条update才去申请加锁,导致不同事务都有可能申请成功对不同行的加锁。如果某一个事务一次性申请了所有需要加锁的行,其他事务申请获取锁只能等待,也就不会产生死锁了。可以使用select * for update 等等

  • 当修改一个事务中的多个表或同一个表中的不同行集时,每次都以一致的顺序执行这些操作。事务形成明确定义的队列并且不会死锁

    如上述例子,因为不同事务执行update 顺序不同,导致的死锁。那么就要求在开发过程中,尽量保证不同业务对于同一数据行的加锁顺序一致。这个要求应该比较高,那么可以尝试在更新,删除数据时,尽量都使用主键id来作为条件,并且将主键id排序,来获取一致顺序。

优化死锁-实现需求1

将逐个更新,变为一次更新,在一次操作中获取所有需要加锁的行。

START TRANSACTION;UPDATE t_lockdraw_money
SET money = CASE id
WHEN 3 THEN(money + 5)
WHEN 4 THEN(money + 5)
WHEN 5 THEN(money + 5)
END
WHEREid IN (3, 4, 5);COMMIT;

并发测试

依旧使用jmeter,维持上述配置,只修改并发测试的sql,测试100并发/s 下死锁情况。

Label 样本 平均值 中位数 90% 百分比 95% 百分比 99% 百分比 最小值 最大值 异常% 吞吐量 接收KB/sec 发送KB/sec
sql事务1 100 51 46 81 87 93 29 93 0.0 96.06/sec 5.91 0.0
sql事务2 100 47 49 56 62 66 31 66 0.0 97.6/sec 6.0 0.0
总体 200 49 48 65 81 93 29 93 0.0 184.1/sec 11.3 0.0

从聚合报告中,可以看出没有发生死锁。

总结

通过上述排他锁相互等待死锁,演示了死锁出现的原因,死锁简要排查方法,此类死锁一些优化规则,并发测试来模拟线上环境产生死锁的概率。对之后事务代码编写和死锁排查提供一点思路,如有错误,欢迎各位大佬指正。

下一篇,通过实现需求2的案例,演示共享锁与互斥锁排队等待产生死锁的情况。

听说点赞关注的人,身体健康,一夜暴富,升职加薪迎娶白富美!!!

点我领取每日福利
微信公众号:耿子blog
GitHub地址:gengzi

Mysql数据库死锁实战-死锁演示-排他锁的相互等待相关推荐

  1. mysql数据库事务隔离级别演示

    mysql数据库事务隔离级别演示 关键词: 一.基本概念 二.事务的四个特性(ACID) 三.事务的用法 3.1 相关命令 3.2 使用步骤 四.数据库的隔离级别 五.示例演示(每组事务结束手动com ...

  2. mysql数据库j电子课件,MYSQL数据库技术分享PPT演示课件

    <MYSQL数据库技术分享PPT演示课件>由会员分享,可在线阅读,更多相关<MYSQL数据库技术分享PPT演示课件(29页珍藏版)>请在人人文库网上搜索. 1.数据库技术分享, ...

  3. 使用C语言调用mysql数据库编程实战以及技巧

    今天编写使用C语言调用mysql数据库编程实战以及技巧,为其他IT同行作为参考,当然有错误可以留言,共同学习. 一.mysql数据库的C语言常用接口API 1.首先当然是链接数据库mysql_real ...

  4. 金仓数据库 MySQL 至 KingbaseES 迁移最佳实践(3. MySQL 数据库移植实战)

    3. MySQL 数据库移植实战 由于 KingbaseES 利用 KDTS-PLUS 等多种工具简化移植过程. 本节重点描述了在实际应用中移植一个 MySQL 数据库系统的完整过程,以及其中的主要移 ...

  5. mysql 的独占锁和排它锁_数据库中的共享锁与排他锁

    摘要: 能修改数据.为什么要加锁很多人都知道,锁是用来解决并发问题的,那么什么是并发问题呢?并发情况下,不加锁会有什么问题呢?拿生活中的洗手间举例子,每个洗手间都会有一个门,并且是可以上锁的,当我们进 ...

  6. MySQL数据库InnoDB存储引擎中的锁机制--转载

    原文地址:http://www.uml.org.cn/sjjm/201205302.asp 00 – 基本概念 当并发事务同时访问一个资源的时候,有可能导致数据不一致.因此需要一种致机制来将访问顺序化 ...

  7. MySQL数据库事务中的行级锁,表级锁,页级锁

    锁定用于确保事务完整性和数据库一致性. 锁定可以防止用户读取其他用户正在更改的数据,并防止多个用户同时更改相同的数据. 如果不使用锁定,数据库中的数据可能在逻辑上变得不正确,而针对这些数据进行查询可能 ...

  8. Mysql共享锁实例_mysql共享锁与排他锁用法实例分析

    本文实例讲述了mysql共享锁与排他锁用法.分享给大家供大家参考,具体如下: mysql锁机制分为表级锁和行级锁,本文就和大家分享一下我对mysql中行级锁中的共享锁与排他锁进行分享交流. 共享锁又称 ...

  9. 网上商城系统MySql数据库设计项目实战

    说明:这是一个数据库课程设计实战项目(附带代码+文档+视频讲解),如需代码+文档+视频讲解可以直接到文章最后获取. 项目背景 互联网的发展让各个产业突破传统的发展领域,产业功能不断进化,实现同一内容的 ...

  10. scrapy读取mysql数据库_python3实战scrapy获取数据保存至MySQL数据库

    python3使用scrapy获取数据然后保存至MySQL数据库,我上一篇写了如何爬取数据保存为csv文件,这一篇将会写如何将数据保存至数据库. 思路大都一样,我列一个思路: 1:获取腾讯招聘网. 2 ...

最新文章

  1. 30岁前不必在乎的28件事
  2. Android多线程----异步消息处理机制之Handler
  3. java c语言union转换_C语言联合体(union)的使用方法及其本质-union
  4. C/C++将十进制数转为二进制并输出
  5. Python: tkinter实例改名小工具
  6. 单体架构到微服务架构的带来的变化
  7. 少儿编程100讲轻松学python(四)-python如何判断是否为数字字符串
  8. ADBPGGreenplum成本优化之磁盘水位管理
  9. ssh-scan处理手记
  10. 对软件测试团队“核心价值”的思考
  11. CI-持续集成(2)-软件工业“流水线”技术实现
  12. android u盘加密代码,怎么用安卓手机给u盘加密
  13. ISO 22000:2018食品安全管理体系介绍、认证及其标准
  14. 湘源控规计算土石方流程
  15. GitHub项目徽章的添加和设置
  16. uni.app H5(微信公众号定位) uni.getLocation
  17. Hashing(哈希)
  18. 华为机试4.20:按照路径替换二叉树
  19. CMM是什么?什么是 “能力成熟度模型”?
  20. CSS外部字体引入方式

热门文章

  1. 假定1km长的CSMA/CD网络的数据率为1Gbit/s。设信号在网络上的传输速率为200000km/s.求能够使用此协议的最短帧长。
  2. html几种美丽的分割线
  3. 正确使用RecyclerView分割线
  4. 什么是智能DNS云解析?云解析如何实现智能解析效果?
  5. 二十一世纪大学英语读写教程(第二册)学习笔记(原文)——5 - Holding Onto a Dream(坚持梦想)
  6. android 单词本代码,android 背单词app源码(MVP架构)
  7. PD充电器诱骗取电方案详解5V9V12V15V20V
  8. 深入讨论机器学习 8 大回归模型的基本原理以及差异!
  9. java编写程序防止电脑屏幕休眠
  10. 网站建设流程都有哪些?