mysql 100个线程并发修改数据库某行记录,加锁不加锁结果分析

文章探讨了MySQL为何需要行锁和表锁机制,即使有了事务ACID特性。通过实验,作者验证了在并发环境下,仅依赖事务无法避免数据丢失,需要配合锁来确保数据一致性。测试中,100个线程并发对数据库某行记录进行累加操作,展示了加锁的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近看到了mysql有行锁和表锁两个概念,越想越疑惑。为什么mysql要提供锁机制,而且这种机制不是一个摆设,还有很多人在用。在现代数据库里几乎有事务机制,acid的机制应该能解决并发调度的问题了,为什么还要主动加锁呢

后来看到一篇文章,“防止更新丢失,并不能单靠数据库事务控制器来解决,需要应用程序对要更新的数据加必要的锁来解决”。瞬间,世界观都崩塌了。非常不敢相信,于是自己写了代码检验一下。数据库表是这样的。用count字段来做100次累加。

import java.sql.Connection;

import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.concurrent.CountDownLatch;

public class LostUpdate implements Runnable {

    private CountDownLatch coLatch;

    public LostUpdate(CountDownLatch countDown) {
        this.coLatch = countDown;
    }

    @Override
    public void run() {
        // TODO Auto-generat
<think>嗯,用户想了解数据库层面的加锁,特别是悲观的实现方法。首先,我得回忆一下数据库的基本概念。悲观,顾名思义,就是假设并发冲突很可能发生,所以在操作数据时直接加锁,防止其他事务干扰。 用户提到在查询数据记录,防止其他线程修改。那具体怎么实现呢?首先,我需要想到同的数据库可能有同的语法,比如MySQL和Oracle的机制可能同。常见的实现方式应该是使用SELECT ... FOR UPDATE这样的语句,这在很多数据库中都适用。 接下来,要分步骤说明实现方法。第一步是在事务中执查询,因为通常在事务中有效,所以需要先开启事务。然后,在查询语句后面加上FOR UPDATE子句,这样会在查询时对记录加排他。其他事务如果尝试修改加锁这些记录,会被阻塞,直到当前事务提交或回滚。 还需要注意的粒度,比如和表级的区别。使用FOR UPDATE通常是,但如果没有索引可能导致表,所以确保WHERE件使用索引字段很重要。此外,要提醒用户及时提交事务,避免长时间持有导致性能问题。 可能用户的应用场景是什么呢?比如电商系统中的库存扣减,或者订单处理,这时候悲观能有效防止超卖。但也要考虑到悲观的缺点,比如降低并发性,可能增加死的风险,所以需要权衡使用。 另外,是否需要对比乐观?用户可能也听说过乐观,但这里问题集中在悲观的实现,所以可以简要提到,但主要详细解释悲观的步骤和注意事项。 最后,确保回答结构清晰,分点说明,给出示例代码(比如MySQL的示例),并提到数据库的差异,比如PostgreSQL也支持FOR UPDATE,而SQL Server可能需要使用WITH (UPDLOCK)之类的提示。这样用户可以根据自己使用的数据库具体调整实现方式。</think>在数据库层面实现悲观的核心思路是:**通过 SQL 语句在查询时显式定目标数据**,确保在事务结束前其他事务无法修改加锁。以下是具体实现方法和注意事项: --- ### **实现步骤(以 MySQL 为例)** 1. **开启事务** 悲观需配合事务使用,需先执 `BEGIN` 或 `START TRANSACTION`。 ```sql START TRANSACTION; ``` 2. **查询时加锁** 使用 `SELECT ... FOR UPDATE` 定目标记录)。 ```sql SELECT * FROM table_name WHERE id = 1 -- 确保 WHERE 件命中索引字段(避免表) FOR UPDATE; -- 对查询到的记录加排他 ``` 3. **执业务操作** 在定后执更新或其他操作: ```sql UPDATE table_name SET stock = stock - 1 WHERE id = 1; ``` 4. **提交/回滚事务** 完成操作后立即提交或回滚以释放: ```sql COMMIT; -- 或 ROLLBACK; ``` --- ### **关键注意事项** 1. **的粒度** - ****:默认(需 WHERE 件命中索引,否则可能升级为表)。 - **表级**:`LOCK TABLES table_name WRITE;`(需手动控制,推荐)。 2. **死风险** 多个事务互相等待可能导致死。需设置事务超时或重试机制(如 `innodb_lock_wait_timeout`)。 3. **数据库差异** - **Oracle**:同样支持 `SELECT ... FOR UPDATE`。 - **SQL Server**:需使用 `WITH (UPDLOCK)` 提示。 - **PostgreSQL**:支持 `FOR UPDATE`,为类似 MySQL。 --- ### **适用场景** 1. **高竞争写操作**(如库存扣减、订单支付) 2. **强数据一致性要求**(如账户余额变更) --- ### **与乐观对比** | | **悲观** | **乐观** | |--| | 实现方式 | 数据库原生机制(如 `FOR UPDATE`) | 应用层版本号/时间戳控制(如 `version` 字段) | | 并发性能 | 低(阻塞其他事务) | 高(无阻塞,冲突时回滚) | | 适用场景 | 写多读少,冲突频繁 | 读多写少,冲突较少 | --- **总结**:悲观通过数据库原生机制强制独占数据,适合短事务且冲突严重的场景,但需谨慎控制事务时长和范围以避免性能问题。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值