高并发环境下安全修改同一行数据库数据的策略分享

 更新时间:2023年06月01日 10:00:56   作者:SJT  
随着互联网技术的发展,越来越多的应用需要在高并发环境中运行,数据库的并发控制成为了业务的关键,本文将介绍如何在高并发情况下,安全地修改数据库中的同一行数据,需要的可以参考一下

随着互联网技术的飞速发展,越来越多的应用需要在高并发环境中运行,尤其是在处理大量用户请求时,数据库的并发控制成为了业务的关键。本文将介绍如何在高并发情况下,安全地修改数据库中的同一行数据。

1. 理解并发问题

并发操作的问题主要源于多个事务在同一时间尝试访问或修改同一行数据。这可能导致数据不一致,或者在极端情况下,导致数据丢失。为了防止这种情况,我们需要一种机制来确保在一个时间点,只有一个事务能修改特定的数据行。

2. 悲观锁

悲观锁是一种常见的并发控制技术,适用于数据竞争激烈的场景。当一个事务尝试读取或修改数据时,悲观锁会假设其他事务可能也会尝试修改该数据,因此会在数据上加锁。其它事务在此期间将不能对该数据进行修改,直到锁被释放。这种锁机制主要通过数据库提供的锁机制实现,如行锁、表锁等。

Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
    conn = dataSource.getConnection();
    //关闭自动提交
    conn.setAutoCommit(false);
    //SELECT ... FOR UPDATE是悲观锁的典型应用,此时会锁住被选中的行,其他事务无法更新这些行
    String sql = "SELECT * FROM product WHERE id = ? FOR UPDATE";
    ps = conn.prepareStatement(sql);
    ps.setInt(1, 1);
    rs = ps.executeQuery();
    if (rs.next()) {
        //获取当前库存数量
        int count = rs.getInt("count");
        if (count > 0) {
            String updateSql = "UPDATE product SET count = count - 1 WHERE id = ?";
            ps = conn.prepareStatement(updateSql);
            ps.setInt(1, 1);
            ps.executeUpdate();
            conn.commit();
            System.out.println("扣减成功,剩余库存:" + (count - 1));
        } else {
            System.out.println("库存不足,扣减失败");
        }
    }
} catch (Exception e) {
    if (conn != null) {
        try {
            //如果出现异常,进行回滚
            conn.rollback();
        } catch (SQLException e1) {
            e1.printStackTrace();
        }
    }
    e.printStackTrace();
} finally {
    //在最后,关闭资源
    if (rs != null) {
        try {
            rs.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    if (ps != null) {
        try {
            ps.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    if (conn != null) {
        try {
            conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

3. 乐观锁

与悲观锁不同,乐观锁在数据被修改时并不加锁,而是在数据提交时检查数据是否被其他事务修改过。如果在一个事务处理过程中数据被另一个事务修改,那么这个事务就会回滚并重新尝试。乐观锁适用于数据竞争不激烈,读操作远多于写操作的场景。

在Java中,乐观锁常常通过"版本号"或者"时间戳"等方式实现。当读取数据时,同时也会读取数据的版本号,当更新数据时,会带上这个版本号,只有当数据库中的当前版本和带上的版本号相同,才会更新数据,并把版本号加1。

示例如下:首先,我们需要在实体类中添加一个版本号字段,并且用@Version注解标注

import javax.persistence.*;
@Entity
@Data
public class Product {
    @Id
    @GeneratedValue
    private Long id;
    private Integer count;
    //版本号,用于实现乐观锁
    @Version
    private Integer version;
}

然后,在进行更新操作时,JPA会自动带上版本号进行检查:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class ProductService {
    @Autowired
    private ProductRepository productRepository;
    @Transactional
    public void reduceCount(Long productId) {
        //找到产品
        Product product = productRepository.findById(productId).get();
        //检查库存
        if (product.getCount() > 0) {
            //如果库存足够,减少库存
            product.setCount(product.getCount() - 1);
            productRepository.save(product);
        } else {
            //库存不足,抛出异常
            throw new RuntimeException("库存不足");
        }
    }
}

4. MVCC(多版本并发控制)

多版本并发控制(MVCC)是一种用于增加数据库并发性能的技术,它会为每一行数据创建版本,以支持高并发读写。每当有新的事务尝试修改数据,它都会创建一个新的数据版本,而不是直接修改原始数据。这样可以让读事务继续访问旧版本数据,而写事务则修改新版本数据,从而提高并发性能。

5. 分布式锁

在分布式系统中,由于数据可能分散在多个节点上,我们需要一种全局的锁机制来保证数据一致性。分布式锁可以提供这样的机制,常见的实现方式有基于数据库的分布式锁,基于缓存(Redis等)的分布式锁,以及基于Zookeeper的分布式锁等。

// 使用 Redisson 库实现 Redis 分布式锁
RedissonClient redisson = Redisson.create();
RLock lock = redisson.getLock("myLock");
try{
    lock.lock();
    // 执行业务操作
}finally{
    lock.unlock();
}

总的来说,在高并发环境下安全地修改同一行数据,我们需要选择合适的锁机制,以保证数据的一致性和系统的性能。同时,我们也需要关注系统的实际需求和性能瓶颈,以实现最优的并发控制策略。

到此这篇关于高并发环境下安全修改同一行数据库数据的策略分享的文章就介绍到这了,更多相关修改数据库数据内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • springboot @Validated的概念及示例实战

    springboot @Validated的概念及示例实战

    这篇文章主要介绍了springboot @Validated的概念以及实战,使用 @Validated 注解,Spring Boot 应用可以有效地实现输入验证,提高数据的准确性和应用的安全性,本文结合实例给大家讲解的非常详细,需要的朋友可以参考下
    2024-04-04
  • Docker使用Dockerfile自定义镜像方式

    Docker使用Dockerfile自定义镜像方式

    这篇文章主要介绍了Docker使用Dockerfile自定义镜像方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-08-08
  • 浅谈servlet中的request与response

    浅谈servlet中的request与response

    下面小编就为大家带来一篇浅谈servlet中的request与response。小编觉得挺不错的,现在就分享给大家,也给大家做个参加。一起跟随小编过来看看吧
    2016-07-07
  • springboot + vue 实现递归生成多级菜单(实例代码)

    springboot + vue 实现递归生成多级菜单(实例代码)

    这篇文章主要介绍了springboot + vue 实现递归生成多级菜单,本文通过实例代码给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2019-12-12
  • Java锁的升级策略 偏向锁 轻量级锁 重量级锁

    Java锁的升级策略 偏向锁 轻量级锁 重量级锁

    在本文中小编给的大家整理了关于Java锁的升级策略 偏向锁 轻量级锁 重量级锁的相关知识点内容,需要的朋友们参考下。
    2019-06-06
  • Java编程枚举类实战代码分享

    Java编程枚举类实战代码分享

    这篇文章主要介绍了Java编程枚举类实战代码分享,具有一定借鉴价值,需要的朋友可以了解下。
    2017-11-11
  • Java8中LocalDateTime与时间戳timestamp的互相转换

    Java8中LocalDateTime与时间戳timestamp的互相转换

    这篇文章主要给大家介绍了关于Java8中LocalDateTime与时间戳timestamp的互相转换,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-03-03
  • Java基础之CardLayout的使用

    Java基础之CardLayout的使用

    这篇文章主要介绍了Java基础之CardLayout的使用,文中有非常详细的代码示例,对正在学习java基础的小伙伴们有很好地帮助,需要的朋友可以参考下
    2021-05-05
  • Java 数据结构与算法系列精讲之背包问题

    Java 数据结构与算法系列精讲之背包问题

    背包问题是一个非常典型的考察动态规划应用的题目,对其加上不同的限制和条件,可以衍生出诸多变种,若要全面理解动态规划,就必须对背包问题了如指掌
    2022-02-02
  • 一文详解如何在Java Maven项目中使用JUnit 5进行测试

    一文详解如何在Java Maven项目中使用JUnit 5进行测试

    这篇文章主要介绍了如何在Java Maven项目中使用JUnit 5进行测试的相关资料,JUnit5是一个流行的Java测试框架,它涵盖了JUnit5的概述、环境配置、编写测试用例、运行测试、高级特性和最佳实践,需要的朋友可以参考下
    2025-04-04

最新评论