1

私は現在、Apache Derby データベースを使用していくつかのユースケース (挿入、更新、削除など) を評価するための Java ベンチマークを開発しています。

私の実装は次のとおりです。

JVM をウォームアップした後、一連の (for ループ: (100k から 1M の反復)) をÌNSERTデータベースの (現時点では単一のテーブル) で実行します。Apache Derbyなので、知っている人のために、すべてのモードをテストします(メモリ内/組み込み、メモリ内/ネットワーク、永続的/組み込み、永続的/ネットワーク)

プロセスの実行は、singleThreaded または multiThreaded (使用してExecutors.newFixedThreadPool(poolSize)

さて、ここに私の問題があります:

1 つのスレッドのみでベンチマークを実行すると、かなり現実的な結果が得られます

In memory/embedded[Simple Integer Insert] : 35K inserts/second (1 thread)

次に、1 つ、次に 2 つの (同時) スレッドを順番に実行することにしました。

今、私は次の結果を持っています:

In memory/embedded[Simple Integer Insert] : 21K inserts/second (1 thread)
In memory/embedded[Simple Integer Insert] : 20K inserts/second (2 thread)

1 スレッドの結果が大きく変わるのはなぜですか?

基本的に、ループの前後にタイマーを開始および終了します。

// Processing
long start = System.nanoTime();

for (int i = 0; i < loopSize; i++) {
    process();
}
// end timer
long absTime = System.nanoTime() - start;
double absTimeMilli = absTime * 1e-6;

そして process() メソッド:

private void process() throws SQLException {
        PreparedStatement ps = clientConn.prepareStatement(query);
        ps.setObject(1, val);
        ps.execute();
        clientConn.commit();
        ps.close();
}

実行は順次処理されるため、コードの残りの部分 (データ処理) によってベンチマークが変更されるべきではありませんか?

シーケンシャル スレッドの数が増える (たとえば、1、2、4、8) と、結果は悪化します。

これが混乱を招く場合は、事前に申し訳ありません。必要に応じて、さらに情報を提供したり、再度説明したりします。

助けてくれてありがとう:)

編集 :

上記の実行を呼び出すメソッド (Usecase クラスから) は次のとおりです。

@Override
public ArrayList<ContextBean> bench(int loopSize, int poolSize) throws InterruptedException, ExecutionException {
    Future<ContextBean> t = null;
    ArrayList<ContextBean> cbl = new ArrayList<ContextBean>();

    try {

        ExecutorService es = Executors.newFixedThreadPool(poolSize);


        for (int i = 0; i < poolSize; i++) {
            BenchExecutor be = new BenchExecutor(eds, insertStatement, loopSize, poolSize, "test-varchar");
            t = es.submit(be); 
            cbl.add(t.get());
        }

        es.shutdown();
        es.awaitTermination(Long.MAX_VALUE,TimeUnit.MILLISECONDS);

    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (SQLException e) {
        e.printStackTrace();
    }
    return cbl;
}
4

1 に答える 1

1

簡単な操作では、すべてのデータベースが説明どおりに動作します。

その理由は、生成するすべてのスレッドが同じテーブル (またはテーブルのセット) で操作しようとするため、データベースはアクセスをシリアル化する必要があるためです。

この状況では、すべてのスレッドの動作が少し遅くなりますが、全体的な結果は (わずかに) 向上します。(シングル スレッド バージョンの 35K に対して 21K+20K=41K)。

ゲインはスレッド数とともに (通常は指数関数的に) 減少し、最終的にはロックのエスカレーションにより損失が発生する可能性があります ( https://dba.stackexchange.com/questions/12864/what-is-lock-escalationを参照)。

一般に、マルチスレッド ソリューションは、そのパフォーマンスが 1 つのリソースに拘束されるのではなく、複数の要因 (つまり、計算、複数のテーブルでの選択、異なるテーブルへの挿入) によって制限される場合に最も効果的です。

于 2013-02-20T09:49:02.843 に答える