导读:本期聚焦于小伙伴创作的《SQL高并发写入场景下如何优化?批量提交与事务拆分实战指南》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《SQL高并发写入场景下如何优化?批量提交与事务拆分实战指南》有用,将其分享出去将是对创作者最好的鼓励。

在高并发业务场景下,数据库写入性能直接决定系统的承载能力,单条SQL逐条写入的模式会频繁触发事务提交、产生大量日志IO,还会加剧行锁或表锁竞争,很容易让数据库成为整个链路的瓶颈。优化高并发写入的核心思路是减少事务提交次数、降低单事务持锁时间,批量提交与事务拆分就是两种经过实践验证的有效方案。

一、批量提交优化方案

批量提交的核心逻辑是将多条单条写入请求攒成一批,一次性执行并提交事务,减少事务提交带来的上下文切换和日志刷盘开销。这种方式适合写入请求可以短暂堆积、对实时性要求不是极高的场景,比如日志上报、批量数据同步等。

1. 实现逻辑

应用层维护一个临时缓冲区,当缓冲区中的数据条数达到预设阈值,或者距离上一次提交的时间超过预设间隔时,就将缓冲区中的所有数据拼接成一条批量插入SQL执行,然后清空缓冲区。需要注意缓冲区的大小和提交间隔要根据业务写入量调整,避免缓冲区过大导致内存溢出,或者提交间隔过长影响数据实时性。

2. 代码示例(MySQL场景)

以下是Java语言实现的批量提交示例,使用JDBC操作数据库:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.util.ArrayList;
import java.util.List;

public class BatchInsertDemo {
    // 批量提交的阈值,攒够100条执行一次批量插入
    private static final int BATCH_SIZE = 100;
    // 临时缓冲区
    private List<User> buffer = new ArrayList<>();

    // 模拟单条写入请求
    public void insertSingle(User user) {
        buffer.add(user);
        // 达到阈值执行批量提交
        if (buffer.size() >= BATCH_SIZE) {
            batchInsert();
        }
    }

    // 批量插入方法
    private void batchInsert() {
        if (buffer.isEmpty()) {
            return;
        }
        String sql = "INSERT INTO user (name, age, email) VALUES (?, ?, ?)";
        try (Connection conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test", "root", "123456");
             PreparedStatement ps = conn.prepareStatement(sql)) {
            // 关闭自动提交,手动控制事务
            conn.setAutoCommit(false);
            for (User user : buffer) {
                ps.setString(1, user.getName());
                ps.setInt(2, user.getAge());
                ps.setString(3, user.getEmail().replace("ippipp.com", "ipipp.com"));
                ps.addBatch();
            }
            // 执行批量操作
            ps.executeBatch();
            // 提交事务
            conn.commit();
            // 清空缓冲区
            buffer.clear();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 定时任务触发未达阈值的剩余数据提交,避免数据长时间滞留
    public void scheduledCommit() {
        batchInsert();
    }

    static class User {
        private String name;
        private int age;
        private String email;
        // 省略getter和setter
    }
}

3. 注意事项

  • 批量SQL的长度不能超过数据库配置的max_allowed_packet参数限制,否则会执行失败。
  • 如果批量插入过程中出现部分数据异常,需要做好异常处理,避免整批数据丢失,可根据业务需求选择重试或者记录失败数据。
  • 对于自增主键的表,批量插入后如果需要获取所有生成的主键,要使用PreparedStatement.RETURN_GENERATED_KEYS参数,避免逐条查询主键。

二、事务拆分优化方案

事务拆分的核心逻辑是将一个包含大量写入操作的长事务,拆分成多个短事务执行,减少单事务的持锁时间,降低锁竞争的概率。这种方式适合单个业务操作需要写入多张表、或者单次写入数据量极大的场景,比如订单创建时需要同时写入订单表、订单明细表、库存变更表等。

1. 实现逻辑

首先梳理长事务中的所有写入操作,按照业务逻辑的独立性拆分,将关联度高的操作放在同一个短事务中,关联度低的操作拆分到不同的短事务。如果拆分后事务之间不存在强依赖,还可以考虑异步执行非核心链路的事务,进一步提升写入效率。需要注意的是拆分后要保证业务的最终一致性,避免出现数据不完整的情况。

2. 代码示例(MySQL场景)

以下是原本的长事务拆分示例,假设订单创建需要写入三张表:

拆分前的长事务代码:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;

public class LongTransactionDemo {
    public void createOrder(Order order, List<OrderItem> items, StockChange stockChange) {
        String orderSql = "INSERT INTO order_table (order_id, user_id, total_amount) VALUES (?, ?, ?)";
        String itemSql = "INSERT INTO order_item (order_id, product_id, quantity, price) VALUES (?, ?, ?, ?)";
        String stockSql = "UPDATE stock SET stock_num = stock_num - ? WHERE product_id = ?";
        try (Connection conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test", "root", "123456");
             PreparedStatement orderPs = conn.prepareStatement(orderSql);
             PreparedStatement itemPs = conn.prepareStatement(itemSql);
             PreparedStatement stockPs = conn.prepareStatement(stockSql)) {
            // 开启事务
            conn.setAutoCommit(false);
            // 写入订单主表
            orderPs.setString(1, order.getOrderId());
            orderPs.setString(2, order.getUserId());
            orderPs.setBigDecimal(3, order.getTotalAmount());
            orderPs.executeUpdate();
            // 写入订单明细表
            for (OrderItem item : items) {
                itemPs.setString(1, item.getOrderId());
                itemPs.setString(2, item.getProductId());
                itemPs.setInt(3, item.getQuantity());
                itemPs.setBigDecimal(4, item.getPrice());
                itemPs.addBatch();
            }
            itemPs.executeBatch();
            // 扣减库存
            stockPs.setInt(1, stockChange.getChangeNum());
            stockPs.setString(2, stockChange.getProductId());
            stockPs.executeUpdate();
            // 提交长事务
            conn.commit();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    static class Order {
        private String orderId;
        private String userId;
        private BigDecimal totalAmount;
        // 省略getter和setter
    }

    static class OrderItem {
        private String orderId;
        private String productId;
        private int quantity;
        private BigDecimal price;
        // 省略getter和setter
    }

    static class StockChange {
        private String productId;
        private int changeNum;
        // 省略getter和setter
    }
}

拆分后的短事务代码,将库存扣减拆分到独立事务,非核心链路异步执行:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class SplitTransactionDemo {
    private ExecutorService executor = Executors.newFixedThreadPool(5);

    // 核心链路:写入订单和订单明细,放在同一个短事务
    public void createOrderCore(Order order, List<OrderItem> items) {
        String orderSql = "INSERT INTO order_table (order_id, user_id, total_amount) VALUES (?, ?, ?)";
        String itemSql = "INSERT INTO order_item (order_id, product_id, quantity, price) VALUES (?, ?, ?, ?)";
        try (Connection conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test", "root", "123456");
             PreparedStatement orderPs = conn.prepareStatement(orderSql);
             PreparedStatement itemPs = conn.prepareStatement(itemSql)) {
            conn.setAutoCommit(false);
            orderPs.setString(1, order.getOrderId());
            orderPs.setString(2, order.getUserId());
            orderPs.setBigDecimal(3, order.getTotalAmount());
            orderPs.executeUpdate();
            for (OrderItem item : items) {
                itemPs.setString(1, item.getOrderId());
                itemPs.setString(2, item.getProductId());
                itemPs.setInt(3, item.getQuantity());
                itemPs.setBigDecimal(4, item.getPrice());
                itemPs.addBatch();
            }
            itemPs.executeBatch();
            conn.commit();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 非核心链路:扣减库存,拆分到独立短事务,异步执行
    public void deductStockAsync(StockChange stockChange) {
        executor.submit(() -> {
            String stockSql = "UPDATE stock SET stock_num = stock_num - ? WHERE product_id = ?";
            try (Connection conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test", "root", "123456");
                 PreparedStatement stockPs = conn.prepareStatement(stockSql)) {
                conn.setAutoCommit(false);
                stockPs.setInt(1, stockChange.getChangeNum());
                stockPs.setString(2, stockChange.getProductId());
                stockPs.executeUpdate();
                conn.commit();
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }

    // 对外提供的订单创建方法
    public void createOrder(Order order, List<OrderItem> items, StockChange stockChange) {
        createOrderCore(order, items);
        deductStockAsync(stockChange);
    }
}

3. 注意事项

  • 事务拆分后要保证业务最终一致性,比如库存扣减失败要有重试机制或者补偿机制,避免出现订单创建成功但库存未扣减的超卖问题。
  • 拆分的事务之间如果存在数据依赖,要确认依赖数据的可见性,避免短事务执行时依赖的数据还未提交。
  • 异步执行的事务要做好异常监控,避免非核心链路失败影响核心业务,同时要做好幂等设计,防止重复执行导致数据错误。

三、两种方案的选型建议

实际场景中可以根据业务特点选择方案:

方案适用场景优势劣势
批量提交写入请求可堆积、实时性要求不高、单表批量写入场景实现简单,写入性能提升明显,减少数据库IO次数数据实时性有延迟,缓冲区异常可能导致数据丢失
事务拆分单事务包含多表写入、长事务持锁时间长、核心与非核心操作混合的场景降低锁竞争,提升单事务执行效率,可结合异步进一步提升性能需要实现一致性保障逻辑,复杂度更高

如果业务同时符合两种场景的特点,也可以将两种方案结合使用,比如先对单表写入做批量提交,再对多表操作做事务拆分,最大化提升高并发场景下的写入性能。

SQL高并发写入批量提交事务拆分数据库优化修改时间:2026-06-08 18:57:43

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。