大量数据情况下单线程插入和多线程insert数据库的性能测试

本文通过实验对比了单线程与多线程在批量数据插入数据库时的性能表现,结果显示,在合理范围内增加线程数量能有效提高插入速度。

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

大量数据情况下单线程插入和多线程insert数据库的性能测试

 

之前一直没有遇到过大批量数据入库的场景,所以一直没有思考过在大量数据的情况下单线程插入和多线程插入的性能情况。今天在看一个项目源代码的时候发现使用了多线程insert操作。

于是简单的写了一个测试程序来测试一批数据在N个线程下的insert情况。

public class ThreadImport {
    private String url="jdbc:oracle:thin:@localhost:1521:orcl";
    private String user="cmis";
    private String password="cmis";
    public Connection getConnect(){
        Connection con = null;
         try {
            Class.forName("oracle.jdbc.driver.OracleDriver");
            con=DriverManager.getConnection(url, user, password);
        } catch (Exception e) {
            e.printStackTrace();
        }
         return con;
    }
    public void multiThreadImport( final int ThreadNum){
        final CountDownLatch cdl= new CountDownLatch(ThreadNum);
        long starttime=System.currentTimeMillis();
        for(int k=1;k<=ThreadNum;k++){
            new Thread(new Runnable() {
                @Override
                public void run() {
                    Connection con=getConnect();
                    try {
                        Statement st=con.createStatement();
                        for(int i=1;i<=80000/ThreadNum;i++){
                            String uuid=UUID.randomUUID().toString();
                            st.addBatch("insert into demo_table(a,b) values('"+uuid+"','"+uuid+"')");
                            if(i%500==0){
                                st.executeBatch();
                            }
                        }
                        cdl.countDown();
                    } catch (Exception e) {
                    }finally{
                        try {
                            con.close();
                        } catch (SQLException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }).start();
        }
        try {
            cdl.await();
            long spendtime=System.currentTimeMillis()-starttime;
            System.out.println( ThreadNum+"个线程花费时间:"+spendtime);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    public static void main(String[] args) throws Exception {
        ThreadImport ti=new ThreadImport();
        ti.multiThreadImport(1);
        ti.multiThreadImport(5);
        ti.multiThreadImport(8);
        ti.multiThreadImport(10);
        ti.multiThreadImport(20);
        ti.multiThreadImport(40);
        System.out.println("笔记本CPU数:"+Runtime.getRuntime().availableProcessors());
    }

}

运行结果:

1个线程花费时间:56707
5个线程花费时间:21688
8个线程花费时间:16625
10个线程花费时间:16098
20个线程花费时间:19882
40个线程花费时间:23536
笔记本CPU数:8

发现在一定数量的线程下性能提升的还是很明显。

在实际的项目中,使用了连接池的情况下,多线程(在一定范围内)数据插入的性能还要明显一点。

### 多线程程序性能低于单线程的实际例子 #### 案例一:多线程环境下的SQLite操作 在某些情况下,使用多线程访问SQLite数据库可能会导致性能下降。SQLite默认采用单线程模式,在这种模式下,所有对数据库的操作都通过一个线程队列来进行管理,每次只有一个线程能够执行操作[^3]。 如果应用程序尝试并发地向SQLite发送多个请求,则这些请求会被排队等待处理,从而增加了总的响应时间。相比之下,单线程应用可以直接顺序调用而无需额外的同步开销,因此在这种特定场景下单线程可能表现得更好。 ```python import sqlite3 from concurrent.futures import ThreadPoolExecutor, as_completed def single_thread_sqlite(): conn = sqlite3.connect(':memory:') cursor = conn.cursor() # 创建表并插入数据 cursor.execute('CREATE TABLE test (id INTEGER)') for i in range(1000): cursor.execute(f'INSERT INTO test VALUES ({i})') result = list(cursor.execute('SELECT * FROM test')) conn.close() def multi_thread_sqlite(): def insert_data(thread_id): local_conn = sqlite3.connect(':memory:') cur = local_conn.cursor() cur.execute('CREATE TABLE IF NOT EXISTS test (id INTEGER)') for i in range(100): # 减少每线程工作量以便更快完成测试 cur.execute(f'INSERT INTO test VALUES ({thread_id}_{i})') res = list(cur.execute('SELECT COUNT(*) FROM test'))[0][0] local_conn.close() return res with ThreadPoolExecutor(max_workers=10) as executor: futures = {executor.submit(insert_data, tid): tid for tid in range(10)} results = [] for future in as_completed(futures): try: results.append(future.result()) except Exception as exc: print(exc) single_thread_time = %timeit -o single_thread_sqlite() multi_thread_time = %timeit -o multi_thread_sqlite() print(f'Single thread time: {single_thread_time.average}') print(f'Multi-thread average time per worker: {sum(multi_thread_time)/len(results)}') ``` 上述Python代码展示了如何分别以单线程方式多线程方式进行简单的SQLite写入操作,并比较两者所需的时间差异。由于SQLite在同一时刻仅允许一个线程进行读写操作的特点,这里预期的结果是多线程版本的整体耗时更长。 #### 原因分析 - **上下文切换成本高**:每当操作系统需要从一个多线程进程的一个线程转到另一个线程时,都会发生上下文切换。频繁的上下文切换不仅消耗CPU周期,还会增加内存带宽的压力。 - **锁争用严重**:为了保护共享资源免受竞态条件的影响,程序员通常会在多线程环境中引入锁定机制。然而,过多或不当使用的锁可能导致严重的瓶颈效应,尤其是在存在大量细粒度锁的情况下。 - **缓存失效问题**:现代处理器依赖于高速缓存来加速访存速度。但在多线程环境下,不同线程之间对于相同位置的数据修改容易引发缓存一致性协议中的消息交换活动,进而降低整体效能。 - **Amdahl定律限制**:即使部分任务适合并行化,但如果串行部分占比过大,那么即便无限增加硬件资源也无法获得理想的加速效果。此外,当涉及到复杂的控制流结构或是I/O密集型作业时,实际可利用的并行潜力往往十分有限[^2].
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值