パフォーマンス向上に対する SQL プリコンパイルとバッチ サブミットの効果の調査

バックグラウンド

プロジェクトは、Kafka からデータを取得した後、ビジネス処理によって挿入、更新、削除ステートメントを生成し、Oracle データベースで実行します。プロジェクトは最初に単一の sql ステートメントを使用して、プリコンパイルおよびサブミットされていない方法で sql ステートメントを実行します。kafka データの増加に伴い、Oracle の SQL ステートメントの実行パフォーマンスが低下し、最終的に Oracle データベース全体が崩壊し、SQL ステートメントを実行できなくなりました。したがって、最適化が必要です。採用された最終的な最適化ソリューションは、問題を解決するためにバッチ送信 + SQL プリコンパイルでした。パフォーマンスの向上に対するバッチ送信と SQL プリコンパイルの影響について説明しましょう。

SQL プリコンパイルとバッチ送信

SQL プリコンパイルに関して最初に頭に浮かぶのは、SQL インジェクションの防止です。SQL コンパイルは SQL 実行パフォーマンスの向上にどのように役立ちますか? ブロガーは多くの情報を調べましたが、そのほとんどは次の図を使用して説明しています: クライアントが SQL ステートメントをサーバーに送信するとき、サーバーは常に構文が正しいかどうかを確認する必要があります
ここに画像の説明を挿入
。 SQL文が正しい場合は、SQL文を実行可能な関数にコンパイルし、最後にSQL文を実行してください。その中でも構文チェックやコンパイルは、SQL文の実行よりも時間がかかる場合があります。

Oracle データベースと Mysql データベースでは、SQL プリコンパイルを実装するメカニズムが異なります.次の実験では、2 つのデータベースの SQL プリコンパイルのパフォーマンス向上の違いを比較します.

jdbc でのバッチ投入では、バッチで投入する前に、プリコンパイル (PrepareStatement) が同じ構造の SQL ステートメント (SQL ステートメントはまったく同じで、パラメーターが異なる) である必要があります。構造が異なる SQL ステートメントを同じバッチでサブミットすることはできません。プリコンパイルされていない送信 (ステートメント) は、さまざまな構造の SQL ステートメントをバッチで送信できます。

上記の仕組みにより、プリコンパイルされたSQLは同一文のバッチでしか投入できないため、同じ構造のSQL文が少なく、構造の異なるSQL文が大部分を占める場合、パフォーマンスの高いプリコンパイルされたSQLをバッチで投入するかどうかまたはバッチでプリコンパイルされていないものを送信する 高性能、選択方法は? 次の実験で疑問を解決してください。

オラクルのデータ実験

ネイティブ jdbc を使用して oracle データベースに接続し、プリコンパイルされていない単一の送信、プリコンパイルされた単一の送信、プリコンパイルされていないバッチの送信、および比較のためにプリコンパイルされたバッチの送信を実行します。

テスト テーブルの構造は次のとおりです。
ここに画像の説明を挿入

ステートメントを挿入

プリコンパイルされていない単一のコミット

コードは以下のように表示されます:

 /**
     * 非预编译,单条提交,提交5000次,insert
     * @throws Exception
     */
    public static void test1_1() throws  Exception{
    
    
        Class.forName("oracle.jdbc.driver.OracleDriver");
        Connection com = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521/orcl?useUnicode=true&characterEncoding=utf8", "demo", "AsDf!123");
        // DriverManager 注册驱动
        // Connection 数据库连接对象  url(指定连接的路径 语法:“jdbc:mysql://ip地址:端口号/数据库名称”)
        Statement stat = com.createStatement();
        //执行 sql 语句的对象
        long start=System.currentTimeMillis();
        for(int i=0;i<10000;i++){
    
    
            String sql="";
            sql="INSERT INTO ceshi (\n" +
                    "\t name,\n" +
                    "\t age,\n" +
                    "\t sex,\n" +
                    "\t school,\n" +
                    "\t birth,\n" +
                    "\t money,\n" +
                    "\t createtime,\n" +
                    "\t remark \n" +
                    ")\n" +
                    "VALUES\n" +
                    "\t(\n" +
                    "\t\t '张三',\n" +
                    "\t\t '18',\n" +
                    "\t\t '男',\n" +
                    "\t\t '测试测试',\n" +
                    "\t\t to_date('2023/03/11','yyyy-MM-dd'),\n" +
                    "\t\t '10.22',\n" +
                    "\t\t to_date('2023-11-11 11:11:11','yyyy-mm-dd HH:MI:SS'),\n" +
                    "\t\t'测试11111111测试222222222测试'\n" +
                    "\t)";
            boolean result = stat.execute(sql);
        }
        System.out.println("test1_1执行时间:"+(System.currentTimeMillis()-start));



        com.close();
        stat.close();
        com.close();
    }

5000 個のデータを挿入し、3 回実行すると、結果は次のようになります。

test1_1 実行時間: 806
test1_1 実行時間: 749
test1_1 実行時間: 762

単一のコミットをプリコンパイルする

コードは以下のように表示されます:

/**
     * 预编译,单条提交,提交5000次,insert
     */
    public static void test1_2() throws  Exception{
    
    
        Class.forName("oracle.jdbc.driver.OracleDriver");
        Connection com = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521/orcl?useUnicode=true&characterEncoding=utf8", "demo", "AsDf!123");
        // DriverManager 注册驱动
        // Connection 数据库连接对象  url(指定连接的路径 语法:“jdbc:mysql://ip地址:端口号/数据库名称”)
        //执行 sql 语句的对象

        String sql="INSERT INTO ceshi (\n" +
                "\t name,\n" +
                "\t age,\n" +
                "\t sex,\n" +
                "\t school,\n" +
                "\t birth,\n" +
                "\t money,\n" +
                "\t createtime,\n" +
                "\t remark \n" +
                ")\n" +
                "VALUES\n" +
                "\t(\n" +
                "\t\t ?,\n" +
                "\t\t ?,\n" +
                "\t\t ?,\n" +
                "\t\t ?,\n" +
                "\t\t ?,\n" +
                "\t\t ?,\n" +
                "\t\t ?,\n" +
                "\t\t ? \n" +
                "\t)";
        PreparedStatement stat = com.prepareStatement(sql);
        long start=System.currentTimeMillis();
        for(int i=0;i<10000;i++){
    
    
            Date date = new Date(System.currentTimeMillis());
            Timestamp tt = new Timestamp(date.getTime());
            stat.setObject(1,"张三"+i);
            stat.setObject(2,20);
            stat.setObject(3,"男"+i);
            stat.setObject(4,"测试ceshi"+i);
            //stat.setObject(5,"to_date('2023/03/11','yyyy-MM-dd')");
            stat.setObject(5, date);
            stat.setObject(6,10);
           // stat.setObject(7,"TO_TIMESTAMP('2023-11-11 11:11:11','yyyy-mm-dd HH:MI:SS')");
            //stat.setObject(7,Calendar.getInstance().getTime());
            stat.setObject(7,tt);
            stat.setObject(8,"测试11111111测试222222222测试"+i);
            boolean result = stat.execute();
        }

        System.out.println("test1_2执行时间:"+(System.currentTimeMillis()-start));



        com.close();
        stat.close();
        com.close();
    }

5000 個のデータを挿入すると、結果は次のようになります。

test1_2 実行時間: 771
test1_2 実行時間: 787
test1_2 実行時間: 780

プリコンパイルされていないバッチ送信

コードは以下のように表示されます:

 /**
     * 非预编译批量提交 insert 5000条
     */
    public static void test2_1() throws  Exception{
    
    
        Class.forName("oracle.jdbc.driver.OracleDriver");
        Connection com = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521/orcl?useUnicode=true&characterEncoding=utf8", "demo", "AsDf!123");
        // DriverManager 注册驱动
        // Connection 数据库连接对象  url(指定连接的路径 语法:“jdbc:mysql://ip地址:端口号/数据库名称”)
        Statement stat = com.createStatement();
        //执行 sql 语句的对象

        for(int i=0;i<10000;i++){
    
    
            String sql="";
            sql="INSERT INTO ceshi (\n" +
                    "\t name,\n" +
                    "\t age,\n" +
                    "\t sex,\n" +
                    "\t school,\n" +
                    "\t birth,\n" +
                    "\t money,\n" +
                    "\t createtime,\n" +
                    "\t remark \n" +
                    ")\n" +
                    "VALUES\n" +
                    "\t(\n" +
                    "\t\t '张三',\n" +
                    "\t\t '18',\n" +
                    "\t\t '男',\n" +
                    "\t\t '测试测试',\n" +
                    "\t\t to_date('2023/03/11','yyyy-MM-dd'),\n" +
                    "\t\t '10.22',\n" +
                    "\t\t to_date('2023-11-11 11:11:11','yyyy-mm-dd HH:MI:SS'),\n" +
                    "\t\t'测试11111111测试222222222测试'\n" +
                    "\t)";
           stat.addBatch(sql);
        }
        long start=System.currentTimeMillis();
        stat.executeBatch();
        System.out.println("test2_1执行时间:"+(System.currentTimeMillis()-start));



        com.close();
        stat.close();
        com.close();
    }

一度に 5,000 個のアイテムをバッチで送信すると、結果は次のようになります。

test2_1 実行時間: 779
test2_1 実行時間: 834
test2_1 実行時間: 823

プリコンパイル済みバッチ送信

コードは以下のように表示されます:

 /**
     * 预编译批量提交 insert 5000条
     */
    public static void test2_2() throws Exception{
    
    
        Class.forName("oracle.jdbc.driver.OracleDriver");
        Connection com = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521/orcl?useUnicode=true&characterEncoding=utf8", "demo", "AsDf!123");
        // DriverManager 注册驱动
        // Connection 数据库连接对象  url(指定连接的路径 语法:“jdbc:mysql://ip地址:端口号/数据库名称”)
        //执行 sql 语句的对象

        String sql="INSERT INTO ceshi (\n" +
                "\t name,\n" +
                "\t age,\n" +
                "\t sex,\n" +
                "\t school,\n" +
                "\t birth,\n" +
                "\t money,\n" +
                "\t createtime,\n" +
                "\t remark \n" +
                ")\n" +
                "VALUES\n" +
                "\t(\n" +
                "\t\t ?,\n" +
                "\t\t ?,\n" +
                "\t\t ?,\n" +
                "\t\t ?,\n" +
                "\t\t ?,\n" +
                "\t\t ?,\n" +
                "\t\t ?,\n" +
                "\t\t ? \n" +
                "\t)";
        PreparedStatement stat = com.prepareStatement(sql);

        for(int i=0;i<10000;i++){
    
    
            Date date = new Date(System.currentTimeMillis());
            Timestamp tt = new Timestamp(date.getTime());
            stat.setObject(1,"张三"+i);
            stat.setObject(2,20);
            stat.setObject(3,"男"+i);
            stat.setObject(4,"测试ceshi"+i);
            //stat.setObject(5,"to_date('2023/03/11','yyyy-MM-dd')");
            stat.setObject(5, date);
            stat.setObject(6,10);
            // stat.setObject(7,"TO_TIMESTAMP('2023-11-11 11:11:11','yyyy-mm-dd HH:MI:SS')");
            //stat.setObject(7,Calendar.getInstance().getTime());
            stat.setObject(7,tt);
            stat.setObject(8,"测试11111111测试222222222测试"+i);
            stat.addBatch();
        }
        long start=System.currentTimeMillis();
        stat.executeBatch();
        System.out.println("test2_2执行时间:"+(System.currentTimeMillis()-start));



        com.close();
        stat.close();
        com.close();
    }

一度に 5,000 件のアイテムを送信すると、実行結果は次のようになります。

test2_2 実行時間: 34
test2_2 実行時間: 59
test2_2 実行時間: 36

insert...values(),() 複数のステートメントを挿入する

Oracle では、複数の値を挿入するための挿入の SQL 構文は次のとおりです。

INSERT ALL
INTO ceshi(name,age,sex,school,birth,money,createtime,remark) values('测试1',19,'女','xuexiao',to_date('2023/03/11','yyyy-MM-dd'),2023,to_date('2023-11-11 11:11:11','yyyy-mm-dd HH:MI:SS'),'测试111')
INTO ceshi(name,age,sex,school,birth,money,createtime,remark) values('测试2',19,'女','xuexiao',to_date('2023/03/11','yyyy-MM-dd'),2023,to_date('2023-11-11 11:11:11','yyyy-mm-dd HH:MI:SS'),'测试111')
INTO ceshi(name,age,sex,school,birth,money,createtime,remark) values('测试3',19,'女','xuexiao',to_date('2023/03/11','yyyy-MM-dd'),2023,to_date('2023-11-11 11:11:11','yyyy-mm-dd HH:MI:SS'),'测试111')
SELECT 1 FROM DUAL;

このステートメントをテストして、一度に 1000 個のデータを挿入します。コードは次のとおりです。

public static void test3_1() throws  Exception{
    
    
        Class.forName("oracle.jdbc.driver.OracleDriver");
        Connection com = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521/orcl?useUnicode=true&characterEncoding=utf8", "demo", "AsDf!123");
        // DriverManager 注册驱动
        // Connection 数据库连接对象  url(指定连接的路径 语法:“jdbc:mysql://ip地址:端口号/数据库名称”)
        Statement stat = com.createStatement();
        //执行 sql 语句的对象

        String sql="INSERT ALL ";
        for(int i=0;i<1000;i++){
    
    
            sql+=" INTO ceshi (\n" +
                    "\t name,\n" +
                    "\t age,\n" +
                    "\t sex,\n" +
                    "\t school,\n" +
                    "\t birth,\n" +
                    "\t money,\n" +
                    "\t createtime,\n" +
                    "\t remark \n" +
                    ")\n" +
                    "VALUES\n" +
                    "\t(\n" +
                    "\t\t '张三',\n" +
                    "\t\t '18',\n" +
                    "\t\t '男',\n" +
                    "\t\t '测试测试',\n" +
                    "\t\t to_date('2023/03/11','yyyy-MM-dd'),\n" +
                    "\t\t '10.22',\n" +
                    "\t\t to_date('2023-11-11 11:11:11','yyyy-mm-dd HH:MI:SS'),\n" +
                    "\t\t'测试11111111测试222222222测试'\n" +
                    "\t) ";
        }
        sql+=" SELECT 1 FROM DUAL";
        long start=System.currentTimeMillis();
        stat.execute(sql);
        System.out.println("test3_1执行时间:"+(System.currentTimeMillis()-start));



        com.close();
        stat.close();
        com.close();
    }

実行結果は次のとおりです。

test3_1 実行時間: 1748

結論は

以上の結果をまとめると次のようになります。

プリコンパイルされていない、単一の提出、5000 の提出

test1_1 実行時間: 806
test1_1 実行時間: 749
test1_1 実行時間: 762

プリコンパイル、1 回の送信、5000 件の送信

test1_2 実行時間: 771
test1_2 実行時間: 787
test1_2 実行時間: 780

プリコンパイルされていないバッチ送信挿入 5000
test2_1 実行時間: 779
test2_1 実行時間: 834
test2_1 実行時間: 823 プリ
コンパイル済みバッチ送信挿入 5000
test2_2 実行時間: 34
test2_2 実行時間: 59
test2_2 実行時間: 36
insert...values ステートメント1回性交1000回挿入
test3_1実行時間:1748

上記の結果から、挿入...値...複数の値のパフォーマンスが最悪であることがわかります。プリコンパイルされていない単一送信、プリコンパイルされた単一送信、およびプリコンパイルされていないバッチ送信のパフォーマンスは、すべて同じ桁数です。プリコンパイルされた一括コミットは、最も高速に実行されます。したがって、大規模なバッチでデータを Oracle データベースに挿入する場合、最速のパフォーマンスを実現するためにプリコンパイル済みバッチ送信が使用されます。

更新ステートメント

プリコンパイルされていない単一のコミット

コードは以下のように表示されます:

/**
     * 非预编译单条update提交,5000条
     */
    public static void test4_1() throws Exception{
    
    
        Class.forName("oracle.jdbc.driver.OracleDriver");
        Connection com = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521/orcl?useUnicode=true&characterEncoding=utf8", "demo", "AsDf!123");
        // DriverManager 注册驱动
        // Connection 数据库连接对象  url(指定连接的路径 语法:“jdbc:mysql://ip地址:端口号/数据库名称”)
        Statement stat = com.createStatement();
        Random random=new Random();
        //执行 sql 语句的对象
        long start=System.currentTimeMillis();
        for(int i=0;i<10000;i++){
    
    
            String sql="";
            sql="update ceshi set age=11"+i+",sex='未知"+i+"',school='修改"+i+"',birth=to_date('2023/03/11','yyyy-MM-dd') where id = "+(35000+i);//*random.nextInt(30000)
           // System.out.println(sql);
            boolean result = stat.execute(sql);
        }
        System.out.println("test4_1执行时间:"+(System.currentTimeMillis()-start));



        com.close();
        stat.close();
        com.close();
    }

5000 エントリを実行すると、結果は次のようになります。

test4_1 実行時間: 14881

10,000 エントリを実行すると、結果は次のようになります。

test4_1 実行時間: 32295

単一のコミットをプリコンパイルする

コードは以下のように表示されます:

/**
     * 预编译单条update提交,5000条
     * @throws Exception
     */
    public static void test4_2() throws  Exception{
    
    
        Class.forName("oracle.jdbc.driver.OracleDriver");
        Connection com = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521/orcl?useUnicode=true&characterEncoding=utf8", "demo", "AsDf!123");
        // DriverManager 注册驱动
        // Connection 数据库连接对象  url(指定连接的路径 语法:“jdbc:mysql://ip地址:端口号/数据库名称”)
        //执行 sql 语句的对象

        String sql="update ceshi set age=?,sex=?,school=?,birth=? where id = ?";
        PreparedStatement stat = com.prepareStatement(sql);
        long start=System.currentTimeMillis();
        for(int i=0;i<10000;i++){
    
    
            Date date = new Date(System.currentTimeMillis());
            Timestamp tt = new Timestamp(date.getTime());
            stat.setObject(1,i);
            stat.setObject(2,"男"+i);
            stat.setObject(3,"测试ceshi"+i);
            //stat.setObject(5,"to_date('2023/03/11','yyyy-MM-dd')");
            stat.setObject(4, date);
            stat.setObject(5,15000+i);
            // stat.setObject(7,"TO_TIMESTAMP('2023-11-11 11:11:11','yyyy-mm-dd HH:MI:SS')");
            //stat.setObject(7,Calendar.getInstance().getTime());
            boolean result = stat.execute();
        }

        System.out.println("test4_2执行时间:"+(System.currentTimeMillis()-start));



        com.close();
        stat.close();
        com.close();
    }

5000 エントリを実行すると、結果は次のようになります。

test4_2 実行時間: 11430

10,000 エントリを実行すると、結果は次のようになります。

test4_2 実行時間: 25123

プリコンパイルされていないバッチ送信

コードは以下のように表示されます:

 /**
     * 非预编译,批量提交5000条
     */
    public static void test5_1() throws  Exception{
    
    
        Class.forName("oracle.jdbc.driver.OracleDriver");
        Connection com = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521/orcl?useUnicode=true&characterEncoding=utf8", "demo", "AsDf!123");
        // DriverManager 注册驱动
        // Connection 数据库连接对象  url(指定连接的路径 语法:“jdbc:mysql://ip地址:端口号/数据库名称”)
        Statement stat = com.createStatement();
        //执行 sql 语句的对象

        for(int i=0;i<10000;i++){
    
    
           String sql="update ceshi set age=11"+i+",sex='未知"+i+"',school='修改"+i+"',birth=to_date('2023/03/11','yyyy-MM-dd') where id = "+(50000+i);//*random.nextInt(30000)

            stat.addBatch(sql);
        }
        long start=System.currentTimeMillis();
        stat.executeBatch();
        System.out.println("test5_1执行时间:"+(System.currentTimeMillis()-start));



        com.close();
        stat.close();
        com.close();
    }

5000 エントリを実行すると、結果は次のようになります。

test5_1 実行時間: 15733

10,000 エントリを実行すると、結果は次のようになります。

test5_1 実行時間: 31539

プリコンパイル済みバッチ送信

コードは以下のように表示されます:

/**
     * 预编译批量提交5000条
     */
    public static void test5_2() throws Exception{
    
    
        Class.forName("oracle.jdbc.driver.OracleDriver");
        Connection com = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521/orcl?useUnicode=true&characterEncoding=utf8", "demo", "AsDf!123");
        // DriverManager 注册驱动
        // Connection 数据库连接对象  url(指定连接的路径 语法:“jdbc:mysql://ip地址:端口号/数据库名称”)
        //执行 sql 语句的对象

        String sql="update ceshi set age=?,sex=?,school=?,birth=? where id = ?";
        PreparedStatement stat = com.prepareStatement(sql);

        for(int i=0;i<10000;i++){
    
    
            Date date = new Date(System.currentTimeMillis());
            Timestamp tt = new Timestamp(date.getTime());
            stat.setObject(1,i);
            stat.setObject(2,"男"+i);
            stat.setObject(3,"测试ceshi"+i);
            //stat.setObject(5,"to_date('2023/03/11','yyyy-MM-dd')");
            stat.setObject(4, date);
            stat.setObject(5,30000+i);
            // stat.setObject(7,"TO_TIMESTAMP('2023-11-11 11:11:11','yyyy-mm-dd HH:MI:SS')");
            //stat.setObject(7,Calendar.getInstance().getTime());
            stat.addBatch();
        }
        long start=System.currentTimeMillis();
        stat.executeBatch();
        System.out.println("test5_2执行时间:"+(System.currentTimeMillis()-start));



        com.close();
        stat.close();
        com.close();
    }

5000 エントリを実行すると、結果は次のようになります。

test5_2 実行時間: 8808

10,000 エントリを実行すると、結果は次のようになります。

test5_2 実行時間: 17672

結論は

要約更新実験結果は次のとおりです。

プリコンパイルされていない単一の更新サブミッション、5000 アイテム:
test4_1 実行時間: 14881 プリ
コンパイルされていない単一の更新サブミッション、10000 アイテム:
test4_1 実行時間: 32295
プリコンパイルされた単一の更新サブミッション、5000 アイテム:
test4_2 実行時間: 11430 の
プリコンパイルされた単一の更新サブミッション、10000 エントリ:
test4_2 実行時間: 25123
プリコンパイルされていないバッチ送信更新、5000 エントリ:
test5_1 実行時間: 15733 プリコンパイル
されていないバッチ送信更新、10000 エントリ:
test5_1 実行時間: 31539 プリ
コンパイルされたバッチ送信更新、5000 エントリ:
test5_2 実行時間: 8808
プリコンパイルバッチ送信更新、10000 アイテム:
test5_2 実行時間: 17672

上記の結果から、プリコンパイルされたシングル サブミッションのパフォーマンスは、プリコンパイルされていないシングル サブミッションよりも高いことがわかります。これは、プリコンパイルによって SQL の解析と検証を繰り返す時間が短縮され、パフォーマンスが向上することを証明しています。しかし全体として、非プリコンパイルのパフォーマンスに対するプリコンパイルの影響は特に明らかではありません。
プリコンパイルされていないバッチ サブミッションのパフォーマンスは、基本的に、非バッチ サブミッションのシングル サブミッションのパフォーマンスと同じです。つまり、バッチ サブミッションのパフォーマンスの向上はあまり明白ではありません。
プリコンパイルされたバッチ送信のパフォーマンスは、プリコンパイルされていない単一の送信およびプリコンパイルされていないバッチ送信よりも大幅に優れており、パフォーマンスは 2 倍高速です。Oracle では、プリコンパイルによってパフォーマンスが大幅に向上することがわかります。プリコンパイルの場合、バッチ サブミッションのパフォーマンスの向上は、シングル サブミッションの場合と比べてやや明白です。
したがって、Oracle データベースの更新の場合、パフォーマンスに影響を与える最初の要因は SQL プリコンパイルであり、2 番目の要因はバッチ サブミットです。さらに、update ステートメントの実行パフォーマンスは、insert ステートメントの実行パフォーマンスよりもはるかに低くなります。

削除ステートメント

コードは以下のように表示されます:

/**
     * 非预编译单条delete,5000条
     */

    public static void test6_1() throws  Exception{
    
    
        Class.forName("oracle.jdbc.driver.OracleDriver");
        Connection com = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521/orcl?useUnicode=true&characterEncoding=utf8", "demo", "AsDf!123");
        // DriverManager 注册驱动
        // Connection 数据库连接对象  url(指定连接的路径 语法:“jdbc:mysql://ip地址:端口号/数据库名称”)
        Statement stat = com.createStatement();
        //执行 sql 语句的对象
        long start=System.currentTimeMillis();
        for(int i=0;i<5000;i++){
    
    
            String sql="";
            sql="delete from ceshi where id="+i;
            boolean result = stat.execute(sql);
        }
        System.out.println("test6_1执行时间:"+(System.currentTimeMillis()-start));



        com.close();
        stat.close();
        com.close();
    }

    /**
     * 预编译单条delet,5000条
     */
    public static void test6_2() throws  Exception{
    
    
        Class.forName("oracle.jdbc.driver.OracleDriver");
        Connection com = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521/orcl?useUnicode=true&characterEncoding=utf8", "demo", "AsDf!123");
        // DriverManager 注册驱动
        // Connection 数据库连接对象  url(指定连接的路径 语法:“jdbc:mysql://ip地址:端口号/数据库名称”)
        //执行 sql 语句的对象

        String sql="delete from ceshi where id = ?";
        PreparedStatement stat = com.prepareStatement(sql);
        long start=System.currentTimeMillis();
        for(int i=0;i<5000;i++){
    
    
            Date date = new Date(System.currentTimeMillis());
            Timestamp tt = new Timestamp(date.getTime());
            stat.setObject(1,i+10000);
            boolean result = stat.execute();
        }

        System.out.println("test6_2执行时间:"+(System.currentTimeMillis()-start));



        com.close();
        stat.close();
        com.close();
    }

    /**
     * 非预编译批量提交,5000条
     */
    public static void test7_1() throws  Exception{
    
    
        Class.forName("oracle.jdbc.driver.OracleDriver");
        Connection com = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521/orcl?useUnicode=true&characterEncoding=utf8", "demo", "AsDf!123");
        // DriverManager 注册驱动
        // Connection 数据库连接对象  url(指定连接的路径 语法:“jdbc:mysql://ip地址:端口号/数据库名称”)
        Statement stat = com.createStatement();
        //执行 sql 语句的对象

        for(int i=0;i<5000;i++){
    
    
            String sql= sql="delete from ceshi where id="+(i+20000);

            stat.addBatch(sql);
        }
        long start=System.currentTimeMillis();
        stat.executeBatch();
        System.out.println("test7_1执行时间:"+(System.currentTimeMillis()-start));



        com.close();
        stat.close();
        com.close();
    }

    /**
     * 预编译批量提交,5000条
     */
    public static void test7_2() throws Exception{
    
    
        Class.forName("oracle.jdbc.driver.OracleDriver");
        Connection com = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521/orcl?useUnicode=true&characterEncoding=utf8", "demo", "AsDf!123");
        // DriverManager 注册驱动
        // Connection 数据库连接对象  url(指定连接的路径 语法:“jdbc:mysql://ip地址:端口号/数据库名称”)
        //执行 sql 语句的对象

        String sql="delete from ceshi where id = ?";
        PreparedStatement stat = com.prepareStatement(sql);

        for(int i=0;i<5000;i++){
    
    
            Date date = new Date(System.currentTimeMillis());
            Timestamp tt = new Timestamp(date.getTime());
            stat.setObject(1,i+5000);

            stat.addBatch();
        }
        long start=System.currentTimeMillis();
        stat.executeBatch();
        System.out.println("test7_2执行时间:"+(System.currentTimeMillis()-start));



        com.close();
        stat.close();
        com.close();
    }

実行結果は次のとおりです。

delete ステートメント:
プリコンパイルされていない単一の削除、5000:
test6_1 実行時間: 15559

単一の削除をプリコンパイル、5000:
test6_2 実行時間: 12477

5000 アイテムのプリコンパイルされていないバッチ送信:

コンパイル済みバッチ送信、5000 アイテム:
test7_1 実行時間: 15599

コンパイル済みバッチ送信、5000 エントリ:
test7_2 実行時間: 7433

結論は

上記の実験データから、delete ステートメントは update ステートメントに似ており、プリコンパイルされていないシングルとバッチのパフォーマンスは同じであることがわかります。プリコンパイルのパフォーマンスが大幅に改善され、プリコンパイルのバッチ送信のパフォーマンスが向上しました。

オラクルの総合的な結論

Oracle データベースの場合、insert ステートメントの実行は、update ステートメントや delete ステートメントよりも高速です。また、プリコンパイルされた SQL は、SQL の実行パフォーマンスを向上させる効果があります。バッチ送信の場合、プリコンパイルされていないバッチ送信と単一送信のパフォーマンスに大きな違いはありません。コンパイル済みバッチ送信のパフォーマンスの向上は、コンパイル済みの単一送信よりも明らかです。したがって、Oracle データベース sql の実行パフォーマンスを向上させるには、プリコンパイル + バッチ送信が最適な最適化ソリューションです。

MYSQL 実験データ

MYSQL データベースのバッチ送信とプリコンパイル済み SQL を見てみましょう.結論は Oracle データベースと一致していますか? It is important to note that in MYSQL, the precompile and batch submit functions are disabled by default. プリコンパイルおよびバッチ送信機能を有効にするには、jdbc 接続データベースの URL に次のパラメーターを追加する必要があります。

rewriteBatchedStatements=true でバッチ送信を有効にします
useServerPrepStmts=true で SQL プリコンパイルを有効にします
cachePrepStmts=true でプリコンパイル キャッシュを有効にします

上記の 3 つのパラメーターについてはここでは説明しませんが、この記事では主に SQL 実行のパフォーマンスを比較します。

ステートメントを挿入

プリコンパイル済み/プリコンパイルされていない単一のコミット

コードは以下のように表示されます:

 public static void test1_1() throws  Exception{
    
    
        Class.forName("com.mysql.cj.jdbc.Driver");
        Connection com = DriverManager.getConnection("jdbc:mysql://localhost:3306/demo?useServerPrepStmts=true&cachePrepStmts=true", "root", "AsDf!123");
        // DriverManager 注册驱动
        // Connection 数据库连接对象  url(指定连接的路径 语法:“jdbc:mysql://ip地址:端口号/数据库名称”)
        Statement stat = com.createStatement();
        //执行 sql 语句的对象
        long start=System.currentTimeMillis();
        for(int i=0;i<5000;i++){
    
    
            String sql="";
                sql="INSERT INTO `demo`.`ceshi` (\n" +
                        "\t`name`,\n" +
                        "\t`age`,\n" +
                        "\t`sex`,\n" +
                        "\t`school`,\n" +
                        "\t`birth`,\n" +
                        "\t`money`,\n" +
                        "\t`createtime`,\n" +
                        "\t`remark`\n" +
                        ")\n" +
                        "VALUES\n" +
                        "\t(\n" +
                        "\t\t'张三',\n" +
                        "\t\t'18',\n" +
                        "\t\t'男',\n" +
                        "\t\t'测试测试',\n" +
                        "\t\t'2023-03-07',\n" +
                        "\t\t'10.22',\n" +
                        "\t\t'2023-03-07 23:05:11',\n" +
                        "\t\t'测试11111111测试222222222测试'\n" +
                        "\t)";
            boolean result = stat.execute(sql);
        }
        System.out.println("test1_1执行时间:"+(System.currentTimeMillis()-start));



        com.close();
        stat.close();
        com.close();
    }
public static void test1_2() throws  Exception{
    
    //
        Class.forName("com.mysql.cj.jdbc.Driver");
        Connection com = DriverManager.getConnection("jdbc:mysql://localhost:3306/demo?useServerPrepStmts=true&cachePrepStmts=true", "root", "AsDf!123");
        // DriverManager 注册驱动
        // Connection 数据库连接对象  url(指定连接的路径 语法:“jdbc:mysql://ip地址:端口号/数据库名称”)
        String sql="INSERT INTO ceshi(`name`,`age`,`sex`,`school`,`birth`,`money`,`createtime`,`remark`) VALUES(?,?,?,?,?,?,?,?)";
        System.out.println(sql);
        PreparedStatement stat = com.prepareStatement(sql);
        //执行 sql 语句的对象
        long start=System.currentTimeMillis();
        for(int i=0;i<5000;i++){
    
    
                stat.setObject(1,"张三"+i);
                stat.setObject(2,"20"+i);
                stat.setObject(3,"男"+i);
                stat.setObject(4,"测试ceshi"+i);
                stat.setObject(5,"2023-03-07");
                stat.setObject(6,"10.22");
                stat.setObject(7,"2023-03-07 23:05:11");
                stat.setObject(8,"测试11111111测试222222222测试"+i);

            boolean result = stat.execute();
        }
        System.out.println("test1_2执行时间:"+(System.currentTimeMillis()-start));



        com.close();
        stat.close();
        com.close();
    }

実行結果は次のとおりです。

プリコンパイルなし、単一サブミッション、5000 サブミッション、挿入:
test1_1 実行時間: 12177 テスト 1_1
実行時間: 10684
非プリコンパイル、単一サブミッション、10000 サブミッション、挿入:
test1_1 実行時間: 21078
テスト 1_1 実行時間: 21581 プリ
コンパイル、単一サブミット、サブミット5000 回、挿入
test1_2 実行時間: 10660
test1_2 実行時間: 10885
プリコンパイル、単一サブミット、サブミット 10000 回、挿入
test1_2 実行時間: 21487
test1_2 実行時間: 21710

結論: MYSQL の挿入の場合、プリコンパイルと非プリコンパイルのパフォーマンスは、SQL の単一のサブミットと同じくらい貧弱です。

プリコンパイル済み/プリコンパイルされていないバッチ送信

コードは以下のように表示されます:

public static void test2_1() throws  Exception{
    
    
        Class.forName("com.mysql.cj.jdbc.Driver");
        Connection com = DriverManager.getConnection("jdbc:mysql://localhost:3306/demo?rewriteBatchedStatements=true", "root", "AsDf!123");
        // DriverManager 注册驱动
        // Connection 数据库连接对象  url(指定连接的路径 语法:“jdbc:mysql://ip地址:端口号/数据库名称”)
        Statement stat = com.createStatement();
        //执行 sql 语句的对象

        for(int i=0;i<10000;i++){
    
    
            String sql="";
            sql="INSERT INTO `demo`.`ceshi` (\n" +
                    "\t`name`,\n" +
                    "\t`age`,\n" +
                    "\t`sex`,\n" +
                    "\t`school`,\n" +
                    "\t`birth`,\n" +
                    "\t`money`,\n" +
                    "\t`createtime`,\n" +
                    "\t`remark`\n" +
                    ")\n" +
                    "VALUES\n" +
                    "\t(\n" +
                    "\t\t'张三',\n" +
                    "\t\t'18',\n" +
                    "\t\t'男',\n" +
                    "\t\t'测试测试',\n" +
                    "\t\t'2023-03-07',\n" +
                    "\t\t'10.22',\n" +
                    "\t\t'2023-03-07 23:05:11',\n" +
                    "\t\t'测试11111111测试222222222测试'\n" +
                    "\t)";
            stat.addBatch(sql);
        }
        long start=System.currentTimeMillis();
        stat.executeBatch();
        System.out.println("test2_1执行时间:"+(System.currentTimeMillis()-start));



        com.close();
        stat.close();
        com.close();
    }
 public static void test2_2() throws  Exception{
    
    //&useServerPrepStmts=true
        Class.forName("com.mysql.cj.jdbc.Driver");
        Connection com = DriverManager.getConnection("jdbc:mysql://localhost:3306/demo?rewriteBatchedStatements=true&useServerPrepStmts=true", "root", "AsDf!123");
        // DriverManager 注册驱动
        // Connection 数据库连接对象  url(指定连接的路径 语法:“jdbc:mysql://ip地址:端口号/数据库名称”)
        String sql="INSERT INTO ceshi(`name`,`age`,`sex`,`school`,`birth`,`money`,`createtime`,`remark`) VALUES(?,?,?,?,?,?,?,?)";
        System.out.println(sql);
        PreparedStatement stat = com.prepareStatement(sql);
        //执行 sql 语句的对象

        for(int i=0;i<5000;i++){
    
    
            stat.setObject(1,"张三"+i);
            stat.setObject(2,"20"+i);
            stat.setObject(3,"男"+i);
            stat.setObject(4,"测试ceshi"+i);
            stat.setObject(5,"2023-03-07");
            stat.setObject(6,"10.22");
            stat.setObject(7,"2023-03-07 23:05:11");
            stat.setObject(8,"测试11111111测试222222222测试"+i);

            stat.addBatch();
        }
        long start=System.currentTimeMillis();
        stat.executeBatch();
        System.out.println("test2_2执行时间:"+(System.currentTimeMillis()-start));



        com.close();
        stat.close();
        com.close();
    }

実行結果は次のとおりです。

プリコンパイルなしでバッチで 5000 の挿入を送信: test2_1 の
実行時間: 9781 test2_1 の実行時間
: 10137 93プリコンパイル済み バッチで 5000 個の挿入をコンパイルして送信: (useServerPrepStmts パラメーターを追加して、SQL プリコンパイルを有効にします) test2_2 の実行時間: 173 test2_2 の実行時間: 154





結論: MYSQL のプリコンパイル済みおよびバッチ サブミットされた挿入ステートメントのパフォーマンスは、プリコンパイルされていないものよりもはるかに高くなっています。プリコンパイルされていないバッチ コミットは、単一のコミット (コンパイル済み/プリコンパイルされていない) よりもわずかにパフォーマンスが向上しています。

上記の結果には問題があります. プリコンパイルされていないバッチ送信のパフォーマンスが、プリコンパイルを有効にしないプリコンパイルよりも高いのはなぜですか? 常識的には、プリコンパイルを有効にしない場合は、プリコンパイルなしのバッチ投入と同等であり、プリコンパイルを有効にした場合よりもパフォーマンスが高くなることはありません。この疑問を持って、MYSQL ログ ファイルを確認しました。SQL プリコンパイルが有効になっていない場合、MYSQL は PrepareStatement の挿入ステートメントを挿入 ... 値(),()...() の形式でマルチデータ挿入にスプライスすることがわかりました。プリコンパイル機能が有効になった後、MYSQL は最初に挿入 SQL を準備してプリコンパイルし、次に挿入ステートメントを実行します。このように、効率は遅くなります。したがって、MYSQL 挿入では、insert...values(),()...() が最も効率的です。

更新ステートメント

コードは以下のように表示されます:

 /**
     * 预编译与非预编译update语句比较---阈值
     */
    public static void test5_1() throws Exception{
    
    
        Class.forName("com.mysql.cj.jdbc.Driver");
        System.out.println(000);
        Connection com = DriverManager.getConnection("jdbc:mysql://localhost:3306/demo?rewriteBatchedStatements=true", "root", "AsDf!123");
        System.out.println(666);
        // DriverManager 注册驱动
        // Connection 数据库连接对象  url(指定连接的路径 语法:“jdbc:mysql://ip地址:端口号/数据库名称”)
        Statement stat = com.createStatement();
        //执行 sql 语句的对象
        System.out.println(111);
        for(int i=0;i<5000;i++){
    
    
         String  sql="UPDATE `demo`.`ceshi`\n" +
                    " set `name` = '张三"+i+"',\n" +
                    " `age` = '18"+i+"',\n" +
                    " `sex` = '男"+i+"',\n" +
                    " `school` = '测试测试"+i+"',\n" +
                    " `birth` = '2023-03-07',\n" +
                    " `money` = '10.22',\n" +
                    " `createtime` = '2023-03-07 23:05:11',\n" +
                    " `remark` = '测试11111111测试222222222测试'\n" +
                    "WHERE\n" +
                    "\t(`id` = '"+(141646+i)+"')";
            stat.addBatch(sql);
        }
        System.out.println(222);
        long start=System.currentTimeMillis();
        stat.executeBatch();
        System.out.println("第一次test5_1执行时间:"+(System.currentTimeMillis()-start));
        stat.clearBatch();
        for(int i=0;i<5000;i++){
    
    
            String  sql="UPDATE `demo`.`ceshi`\n" +
                    " set `name` = '张三"+i+"',\n" +
                    " `age` = '18"+i+"',\n" +
                    " `sex` = '男"+i+"',\n" +
                    " `school` = '测试测试"+i+"',\n" +
                    " `birth` = '2023-03-07',\n" +
                    " `money` = '10.22',\n" +
                    " `createtime` = '2023-03-07 23:05:11',\n" +
                    " `remark` = '测试11111111测试222222222测试'\n" +
                    "WHERE\n" +
                    "\t(`id` = '"+(160646+i)+"')";
            stat.addBatch(sql);
        }
        System.out.println(222);
         start=System.currentTimeMillis();
        stat.executeBatch();
        System.out.println("第二次test5_1执行时间:"+(System.currentTimeMillis()-start));

        stat.clearBatch();
        for(int i=0;i<5000;i++){
    
    
            String  sql="UPDATE `demo`.`ceshi`\n" +
                    " set `name` = '张三"+i+"',\n" +
                    " `age` = '18"+i+"',\n" +
                    " `sex` = '男"+i+"',\n" +
                    " `school` = '测试测试"+i+"',\n" +
                    " `birth` = '2023-03-07',\n" +
                    " `money` = '10.22',\n" +
                    " `createtime` = '2023-03-07 23:05:11',\n" +
                    " `remark` = '测试11111111测试222222222测试'\n" +
                    "WHERE\n" +
                    "\t(`id` = '"+(182316+i)+"')";
            stat.addBatch(sql);
        }
        System.out.println(222);
        start=System.currentTimeMillis();
        stat.executeBatch();
        System.out.println("第三次test5_1执行时间:"+(System.currentTimeMillis()-start));



        com.close();
        stat.close();
        com.close();
    }

    public static void test5_2() throws Exception{
    
    //&useServerPrepStmts=true&cachePrepStmts=true
        Class.forName("com.mysql.cj.jdbc.Driver");
        System.out.println(000);
        Connection com = DriverManager.getConnection("jdbc:mysql://localhost:3306/demo?rewriteBatchedStatements=true", "root", "AsDf!123");
        System.out.println(111);
        // DriverManager 注册驱动
        // Connection 数据库连接对象  url(指定连接的路径 语法:“jdbc:mysql://ip地址:端口号/数据库名称”)
        String sql="UPDATE ceshi\n" +
                "SET \n" +
                " `name` = ?," +
                " `age` = ?," +
                " `sex` = ?," +
                " `school` = ?," +
                " `birth` = ?," +
                " `money` = ?," +
                " `createtime` = ?," +
                " `remark` = ?" +
                " WHERE\n" +
                "\t`id` = ?";
        PreparedStatement stat = com.prepareStatement(sql);
        //执行 sql 语句的对象
        for(int i=0;i<5000;i++){
    
    
            stat.setObject(1,"张三"+i);
            stat.setObject(2,"191"+i);
            stat.setObject(3,"1男1"+i);
            stat.setObject(4,"测试ceshi1"+i);
            stat.setObject(5,"2023-03-07");
            stat.setObject(6,"10.2211");
            stat.setObject(7,"2023-03-07 23:05:11");
            stat.setObject(8,"测试11111111测试222222222测试"+i);
            stat.setObject(9,189305+i);
            stat.addBatch();
        }

        long start=System.currentTimeMillis();
        stat.executeBatch();
        System.out.println("第一次test5_2执行时间:"+(System.currentTimeMillis()-start));
        stat.clearBatch();
        //执行 sql 语句的对象
        for(int i=0;i<5000;i++){
    
    
            stat.setObject(1,"张三"+i);
            stat.setObject(2,"191"+i);
            stat.setObject(3,"1男1"+i);
            stat.setObject(4,"测试ceshi1"+i);
            stat.setObject(5,"2023-03-07");
            stat.setObject(6,"10.2211");
            stat.setObject(7,"2023-03-07 23:05:11");
            stat.setObject(8,"测试11111111测试222222222测试"+i);
            stat.setObject(9,402287+i);
            stat.addBatch();
        }

         start=System.currentTimeMillis();
        stat.executeBatch();
        System.out.println("第二次test5_2执行时间:"+(System.currentTimeMillis()-start));
        stat.clearBatch();
        //执行 sql 语句的对象
        for(int i=0;i<5000;i++){
    
    
            stat.setObject(1,"张三"+i);
            stat.setObject(2,"191"+i);
            stat.setObject(3,"1男1"+i);
            stat.setObject(4,"测试ceshi1"+i);
            stat.setObject(5,"2023-03-07");
            stat.setObject(6,"10.2211");
            stat.setObject(7,"2023-03-07 23:05:11");
            stat.setObject(8,"测试11111111测试222222222测试"+i);
            stat.setObject(9,602287+i);
            stat.addBatch();
        }

         start=System.currentTimeMillis();
        stat.executeBatch();
        System.out.println("第三次test5_2执行时间:"+(System.currentTimeMillis()-start));
        stat.clearBatch();



        com.close();
        stat.close();
        com.close();
    }

    /**
     * 预编译与非预编译单条提交  update语句测试
     */
    public static void test6_1() throws Exception{
    
    
        Class.forName("com.mysql.cj.jdbc.Driver");
        System.out.println(000);
        Connection com = DriverManager.getConnection("jdbc:mysql://localhost:3306/demo?rewriteBatchedStatements=true", "root", "AsDf!123");
        System.out.println(666);
        // DriverManager 注册驱动
        // Connection 数据库连接对象  url(指定连接的路径 语法:“jdbc:mysql://ip地址:端口号/数据库名称”)
        Statement stat = com.createStatement();
        long start=System.currentTimeMillis();
        //执行 sql 语句的对象
        for(int i=0;i<5000;i++){
    
    
          //  long ss=System.currentTimeMillis();
            String  sql="UPDATE `demo`.`ceshi`\n" +
                    " set `name` = '张三"+i+"',\n" +
                    " `age` = '18"+i+"',\n" +
                    " `sex` = '男"+i+"',\n" +
                    " `school` = '测试测试"+i+"',\n" +
                    " `birth` = '2023-03-07',\n" +
                    " `money` = '10.22',\n" +
                    " `createtime` = '2023-03-07 23:05:11',\n" +
                    " `remark` = '测试11111111测试222222222测试'\n" +
                    "WHERE\n" +
                    "\t(`id` = '"+(12426+i)+"')";
           stat.execute(sql);
            //System.out.println("test6_1第"+i+"次执行时间:"+(System.currentTimeMillis()-ss));
        }

        System.out.println("总共test6_1执行时间:"+(System.currentTimeMillis()-start));



        com.close();
        stat.close();
        com.close();
    }

    public static void test6_2() throws Exception{
    
     //&useServerPrepStmts=true
        Class.forName("com.mysql.cj.jdbc.Driver");
        System.out.println(000);
        Connection com = DriverManager.getConnection("jdbc:mysql://localhost:3306/demo?rewriteBatchedStatements=true", "root", "AsDf!123");
        System.out.println(111);
        // DriverManager 注册驱动
        // Connection 数据库连接对象  url(指定连接的路径 语法:“jdbc:mysql://ip地址:端口号/数据库名称”)
        String sql="UPDATE ceshi\n" +
                "SET \n" +
                " `name` = ?," +
                " `age` = ?," +
                " `sex` = ?," +
                " `school` = ?," +
                " `birth` = ?," +
                " `money` = ?," +
                " `createtime` = ?," +
                " `remark` = ?" +
                " WHERE\n" +
                "\t`id` = ?";
        PreparedStatement stat = com.prepareStatement(sql);
        //执行 sql 语句的对象
        long start=System.currentTimeMillis();
        for(int i=0;i<5000;i++){
    
    
           // long ss=System.currentTimeMillis();
            stat.setObject(1,"张三"+i);
            stat.setObject(2,"191"+i);
            stat.setObject(3,"1男1"+i);
            stat.setObject(4,"测试ceshi1"+i);
            stat.setObject(5,"2023-03-07");
            stat.setObject(6,"10.2211");
            stat.setObject(7,"2023-03-07 23:05:11");
            stat.setObject(8,"测试11111111测试222222222测试"+i);
            stat.setObject(9,567299+i);
            stat.execute();
          //  System.out.println("test6_2第"+i+"次执行时间:"+(System.currentTimeMillis()-ss));
        }
        System.out.println("第一次test6_2执行时间:"+(System.currentTimeMillis()-start));

        com.close();
        stat.close();
        com.close();
    }

実行結果は次のとおりです。

プリコンパイルされていない単一の更新プログラムの送信、5000 アイテム:
test6_1 の合計実行時間: 10292 test6_1の合計実行時間:
10947 :最初の test5_1 の実行時間: 9051 2 番目の test5_1 の実行時間: 7818 3番目の test5_1 の実行時間: 6747










結論: 単一の送信でもバッチの送信でも、プリコンパイルと非プリコンパイルのパフォーマンスは似ています。バッチ コミットは、単一のコミットよりもわずかに優れたパフォーマンスを発揮します。MYSQL 更新の場合、プリコンパイルによってパフォーマンスが向上することはありません。バッチ送信により、パフォーマンスがわずかに向上します。

削除ステートメント

コードは以下のように表示されます:

 /**
     * 删除语句  批量提交   预编译与非预编译比较
     */
    public static void test7_1() throws Exception{
    
    
        Class.forName("com.mysql.cj.jdbc.Driver");
        System.out.println(000);
        Connection com = DriverManager.getConnection("jdbc:mysql://localhost:3306/demo?rewriteBatchedStatements=true&useServerPrepStmts=true&cachePrepStmts=true", "root", "AsDf!123");
        System.out.println(666);
        com.setAutoCommit(false);
        // DriverManager 注册驱动
        // Connection 数据库连接对象  url(指定连接的路径 语法:“jdbc:mysql://ip地址:端口号/数据库名称”)
        Statement stat = com.createStatement();
        long start=System.currentTimeMillis();
        //执行 sql 语句的对象
        for(int i=0;i<10000;i++){
    
    
            //  long ss=System.currentTimeMillis();
            String  sql="DELETE  from `demo`.`ceshi`\n" +
                    "WHERE\n" +
                    "\t(`id` = '"+(11150+i)+"')";
            stat.addBatch(sql);
            //System.out.println("test6_1第"+i+"次执行时间:"+(System.currentTimeMillis()-ss));
        }
        stat.executeBatch();
        com.commit();
        System.out.println("总共test7_1执行时间:"+(System.currentTimeMillis()-start));



        com.close();
        stat.close();
        com.close();
    }

    public static void test7_2() throws Exception{
    
    
        Class.forName("com.mysql.cj.jdbc.Driver");
        System.out.println(000);
        Connection com = DriverManager.getConnection("jdbc:mysql://localhost:3306/demo?rewriteBatchedStatements=true&useServerPrepStmts=true&cachePrepStmts=true", "root", "AsDf!123");
        System.out.println(111);
        com.setAutoCommit(false);
        // DriverManager 注册驱动
        // Connection 数据库连接对象  url(指定连接的路径 语法:“jdbc:mysql://ip地址:端口号/数据库名称”)
        String sql="delete from  ceshi\n" +
                " WHERE\n" +
                "\t`id` = ?";
        PreparedStatement stat = com.prepareStatement(sql);
        //执行 sql 语句的对象
        long start=System.currentTimeMillis();
        for(int i=0;i<10000;i++){
    
    
            // long ss=System.currentTimeMillis();

            stat.setObject(1,32260+i);
            stat.addBatch();
            //  System.out.println("test6_2第"+i+"次执行时间:"+(System.currentTimeMillis()-ss));
        }
        stat.executeBatch();
        com.commit();
        System.out.println("第一次test7_2执行时间:"+(System.currentTimeMillis()-start));

        com.close();
        stat.close();
        com.close();
    }

実行結果は次のとおりです。

単一の送信、100 件の送信:
test7_1 の合計実行時間: 4116 (プリコンパイルなし)
最初の test7_2 の実行時間: 4303 (プリコンパイル済み)
バッチ送信、100 件の送信:
test7_1 の合計実行時間: 164 (プリコンパイルなし)
最初の回 test7_2 の実行時間: 158 (コンパイル済み)

結論: プリコンパイルによって削除のパフォーマンスが向上することはありませんが、バッチ送信によってパフォーマンスが向上します。

MYSQL の結論

MYSQL の場合、プリコンパイルは挿入ステートメントでのみ SQL パフォーマンスを向上させることができます。ただし、大規模なバッチで挿入のパフォーマンスを向上させる最善の方法は、insert...values(),()...() ステートメントであり、プリコンパイルは最初の選択肢ではありません。update ステートメントでは、プリコンパイルによって SQL パフォーマンスが向上することはありません。バッチ送信により、実行パフォーマンスがいくらか向上します。MYSQL では、insert...values(),()...() と一括削除のパフォーマンスが速く、update ステートメントの実行パフォーマンスも非常に遅いです。

最終的な結論

バッチ送信と SQL プリコンパイルは、Oracle データベースと Mysql データベースではまったく異なります。
Oracle では、プリコンパイルによって SQL 実行のパフォーマンスが大幅に向上します。プリコンパイルを前提に、バッチ投入することでさらに性能が向上します。
Mysql では、プリコンパイルは SQL 実行のパフォーマンス向上には影響せず、単に SQL インジェクションを防ぐためのものかもしれません。insert...values(),()...() は最高のパフォーマンスで挿入します。更新と削除のバッチ送信により、パフォーマンスが向上する可能性があります。

おすすめ

転載: blog.csdn.net/qq1309664161/article/details/129677700