5

私は2つの関連する質問をしました(sqliteクエリを実行した後に結果のフェッチを高速化するにはどうすればよいですか?そしてsqlite.fetchall()がとても遅いのは正常ですか?)。いくつか変更を加えてスピードアップしましたが、selectステートメントが完了するまでに1時間以上かかります。

、、、および値featureを含むテーブルがあります。これらの値は一緒に長方形の角になります(私の古い質問を読んだ場合、テーブルからmin()とmax()を取得する代わりに、これらの値を別々に保存すると、より速く動作します)。そして、と値を持つテーブルを取得しました。スペクトルの値がフィーチャの長方形内にある場合に、フィーチャをスペクトルにリンクするテーブルがあります。rtMinrtMaxmzMinmzMaxconvexhull
spectrumrtmzrtmz

これを行うには、次のsqlおよびpythonコードを使用して、スペクトルと機能のIDを取得します。

self.cursor.execute("SELECT spectrum_id, feature_table_id "+
                    "FROM `spectrum` "+
                    "INNER JOIN `feature` "+
                    "ON feature.msrun_msrun_id = spectrum.msrun_msrun_id "+
                    "WHERE spectrum.scan_start_time >= feature.rtMin "+
                    "AND spectrum.scan_start_time <= feature.rtMax "+
                    "AND spectrum.base_peak_mz >= feature.mzMin "+
                    "AND spectrum.base_peak_mz <= feature.mzMax")        
spectrumAndFeature_ids = self.cursor.fetchall()

for spectrumAndFeature_id in spectrumAndFeature_ids:
        spectrum_has_feature_inputValues = (spectrumAndFeature_id[0], spectrumAndFeature_id[1])
        self.cursor.execute("INSERT INTO `spectrum_has_feature` VALUES (?,?)",spectrum_has_feature_inputValues)

実行、フェッチオール、挿入の時間を計り、次のようになりました。

query took: 74.7989799976 seconds
5888.845541 seconds since fetchall
returned a length of: 10822
inserting all values took: 3.29669690132 seconds

したがって、このクエリには約1時間半かかり、そのほとんどの時間はfetchall()を実行します。どうすればこれをスピードアップできますか?rtPythonコードでとmz比較を行う必要がありますか?


アップデート:

取得したインデックスを示すために、テーブルのcreateステートメントを次に示します。

CREATE  TABLE IF NOT EXISTS `feature` (
  `feature_table_id` INT PRIMARY KEY NOT NULL ,
  `feature_id` VARCHAR(40) NOT NULL ,
  `intensity` DOUBLE NOT NULL ,
  `overallquality` DOUBLE NOT NULL ,
  `charge` INT NOT NULL ,
  `content` VARCHAR(45) NOT NULL ,
  `intensity_cutoff` DOUBLE NOT NULL,
  `mzMin` DOUBLE NULL ,
  `mzMax` DOUBLE NULL ,
  `rtMin` DOUBLE NULL ,
  `rtMax` DOUBLE NULL ,
  `msrun_msrun_id` INT NOT NULL ,
  CONSTRAINT `fk_feature_msrun1`
    FOREIGN KEY (`msrun_msrun_id` )
    REFERENCES `msrun` (`msrun_id` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION);

  CREATE UNIQUE INDEX `id_UNIQUE` ON `feature` (`feature_table_id` ASC);
  CREATE INDEX `fk_feature_msrun1` ON `feature` (`msrun_msrun_id` ASC);



CREATE  TABLE IF NOT EXISTS `spectrum` (
  `spectrum_id` INT PRIMARY KEY NOT NULL ,
  `spectrum_index` INT NOT NULL ,
  `ms_level` INT NOT NULL ,
  `base_peak_mz` DOUBLE NOT NULL ,
  `base_peak_intensity` DOUBLE NOT NULL ,
  `total_ion_current` DOUBLE NOT NULL ,
  `lowest_observes_mz` DOUBLE NOT NULL ,
  `highest_observed_mz` DOUBLE NOT NULL ,
  `scan_start_time` DOUBLE NOT NULL ,
  `ion_injection_time` DOUBLE,
  `binary_data_mz` BLOB NOT NULL,
  `binaray_data_rt` BLOB NOT NULL,
  `msrun_msrun_id` INT NOT NULL ,
  CONSTRAINT `fk_spectrum_msrun1`
    FOREIGN KEY (`msrun_msrun_id` )
    REFERENCES `msrun` (`msrun_id` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION);

  CREATE INDEX `fk_spectrum_msrun1` ON `spectrum` (`msrun_msrun_id` ASC);



CREATE  TABLE IF NOT EXISTS `spectrum_has_feature` (
  `spectrum_spectrum_id` INT NOT NULL ,
  `feature_feature_table_id` INT NOT NULL ,
  CONSTRAINT `fk_spectrum_has_feature_spectrum1`
    FOREIGN KEY (`spectrum_spectrum_id` )
    REFERENCES `spectrum` (`spectrum_id` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION,
  CONSTRAINT `fk_spectrum_has_feature_feature1`
    FOREIGN KEY (`feature_feature_table_id` )
    REFERENCES `feature` (`feature_table_id` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION);

  CREATE INDEX `fk_spectrum_has_feature_feature1` ON `spectrum_has_feature` (`feature_feature_table_id` ASC);
  CREATE INDEX `fk_spectrum_has_feature_spectrum1` ON `spectrum_has_feature` (`spectrum_spectrum_id` ASC);

アップデート2:

20938のスペクトル、305742の機能、2つのmsrunがあります。その結果、10822の一致が発生します。


アップデート3:

新しいインデックスを使用して(CREATE INDEX fk_spectrum_msrun1_2ON spectrummsrun_msrun_idbase_peak_mz);)、保存の間に約20秒:クエリにかかった時間:76.4599349499秒fetchallから5864.15418601秒


アップデート4:

EXPLAIN QUERY PLANからの印刷:

(0, 0, 0, u'SCAN TABLE spectrum (~1000000 rows)'), (0, 1, 1, u'SEARCH TABLE feature USING INDEX fk_feature_msrun1 (msrun_msrun_id=?) (~2 rows)') 
4

7 に答える 7

6

2つの大きなテーブルを相互に関連付けています。簡単な計算:300k x 20k=60億行。これらすべての行を返すだけの場合は、確かにI / Oバウンドになります(ただし、実際には(O)出力側のみです)。ただし、ここでCPUが確実にバインドされるように、返される行は10k行しかないため、where句はほとんどすべてを除外します。

SQLiteは、「 OR最適化」と呼ばれるものを除いて、一度に複数のインデックスを使用することはできません。さらに、内部結合は「 WHERE句の追加の用語に変換される」ため、パフォーマンスが向上することはありません。

結論として、SQLiteはsayetal。ほど効率的にクエリを実行することができませんpostgresql

クエリをどれだけ最適化できるかを知りたいと思ったので、シナリオを試してみました。最終的に、最良の最適化はすべての明示的なインデックス(!)を削除することであるように思われます。SQLiteは、私が試したさまざまなアプローチよりも優れたパフォーマンスをもたらすオンザフライのインデックス/インデックスを計算しているようです。

デモンストレーションとして、自分のスキーマから派生したこのスキーマを検討してください。

CREATE TABLE feature ( -- 300k
    feature_id INTEGER PRIMARY KEY,
    mzMin DOUBLE,
    mzMax DOUBLE,
    rtMin DOUBLE,
    rtMax DOUBLE,
    lnk_feature INT);
CREATE TABLE spectrum ( -- 20k
    spectrum_id INTEGER PRIMARY KEY,
    mz DOUBLE,
    rt DOUBLE,
    lnk_spectrum INT);

feature300k行とspectrum20k行があります(これを行うPythonコードは以下のどこかにあります)。明示的なインデックスは指定されておらず、定義により暗黙的なインデックスのみINTEGER PRIMARY KEYが指定されています。

INTEGER PRIMARY KEY列は別として、UNIQUE制約とPRIMARY KEY制約の両方が、データベースにインデックスを作成することによって実装されます( "CREATE UNIQUE INDEX"ステートメントと同じ方法で)。このようなインデックスは、データベース内の他のインデックスと同様に、クエリを最適化するために使用されます。その結果、UNIQUEまたはPRIMARY KEY制約の対象となっている列のセットにインデックスを作成しても、多くの場合、利点はありません(ただし、かなりのオーバーヘッドがあります)。

上記のスキーマを使用して、SQLiteは、クエリの実行中にインデックスを作成すると述べていますlnk_feature

sqlite> EXPLAIN QUERY PLAN SELECT feature_id, spectrum_id FROM spectrum, feature
   ...> WHERE lnk_feature = lnk_spectrum
   ...>     AND rt >= rtMin AND rt <= rtMax
   ...>     AND mz >= mzMin AND mz <= mzMax;
0|0|0|SCAN TABLE spectrum (~20000 rows)
0|1|1|SEARCH TABLE feature USING AUTOMATIC COVERING INDEX (lnk_feature=?) (~7 rows)

また、その列または他の列のインデックスを使用してテストしたとしても、そのクエリを実行する最も速い方法は、これらのインデックスを使用しないことです。

Pythonを使用して上記のクエリを実行した最速は20分です。これには、の完了が含まれます.fetchall()。ある時点で150倍の行があるとおっしゃっています。私があなたであるかどうかを調べ始めpostgresqlます;-)...作業をスレッドに分割し、クエリを完了する時間を、同時に実行できるスレッドの数(つまり、使用可能なCPUの数)。

いずれにせよ、これが私が使用したコードです。自分で実行して、環境内でクエリが実行された速度を報告できますか。私はを使用していることに注意してapswください。使用できない場合は、独自のsqlite3モジュールを使用するように調整する必要があります。

#!/usr/bin/python
import apsw, random as rand, time

def populate(cu):
    cu.execute("""
CREATE TABLE feature ( -- 300k
    feature_id INTEGER PRIMARY KEY,
    mzMin DOUBLE, mzMax DOUBLE,
    rtMin DOUBLE, rtMax DOUBLE,
    lnk_feature INT);
CREATE TABLE spectrum ( -- 20k
    spectrum_id INTEGER PRIMARY KEY,
    mz DOUBLE, rt DOUBLE,
    lnk_spectrum INT);""")
    cu.execute("BEGIN")
    for i in range(300000):
        ((mzMin, mzMax), (rtMin, rtMax)) = (get_min_max(), get_min_max())
        cu.execute("INSERT INTO feature VALUES (NULL,%s,%s,%s,%s,%s)" 
                    % (mzMin, mzMax, rtMin, rtMax, get_lnk()))
    for i in range(20000):
        cu.execute("INSERT INTO spectrum VALUES (NULL,%s,%s,%s)"
                    % (get_in_between(), get_in_between(), get_lnk()))
    cu.execute("COMMIT")
    cu.execute("ANALYZE")

def get_lnk():
    return rand.randint(1, 2)

def get_min_max():
    return sorted((rand.normalvariate(0.5, 0.004), 
                   rand.normalvariate(0.5, 0.004)))

def get_in_between():
    return rand.normalvariate(0.5, 0.49)

def select(cu):
    sql = """
    SELECT feature_id, spectrum_id FROM spectrum, feature
    WHERE lnk_feature = lnk_spectrum
        AND rt >= rtMin AND rt <= rtMax
        AND mz >= mzMin AND mz <= mzMax"""
    start = time.time()
    cu.execute(sql)
    print ("%s rows; %.2f seconds" % (len(cu.fetchall()), time.time() - start))

cu = apsw.Connection('foo.db').cursor()
populate(cu)
select(cu)

私が得る出力:

54626 rows; 1210.96 seconds
于 2012-05-05T16:36:57.870 に答える
2

SQLの部分でそれをより良くしてください。

一言で言えば、INDEXESを使用してください!

于 2012-05-04T08:37:20.573 に答える
1

範囲の比較には、>=と<=の代わりにbetweenを使用します。

self.cursor.execute("SELECT spectrum_id, feature_table_id "+
                        "FROM `spectrum` "+
                        "INNER JOIN `feature` "+
                        "ON feature.msrun_msrun_id = spectrum.msrun_msrun_id "+
                        "WHERE spectrum.scan_start_time between feature.rtMin " + 
                        "AND feature.rtMax "+
                        "AND spectrum.base_peak_mz between feature.mzMin "+
                        "AND feature.mzMax")   

非クラスター化インデックスは、spectrum.scan_start_time、feature.rtMin、feature.rtMax、spectrum.base_peak_mz、m feature.mzMin、およびfeature.mzMaxフィールドに作成できます。

于 2012-05-04T08:36:39.410 に答える
1

通常のRDBMSでは、テーブル間spectrumでハッシュ結合を実行する必要があります。featuresハッシュ結合に対して強制的に実行できる場合は、クエリが実行されるはずです。

ただし、単一のクエリを試すことはできますか?

self.cursor.execute("INSERT INTO `spectrum_has_feature` " + 
                    "SELECT spectrum_id, feature_table_id "+
                    "FROM `spectrum` "+
                    "INNER JOIN `feature` "+
                    "ON feature.msrun_msrun_id = spectrum.msrun_msrun_id "+
                    "WHERE spectrum.scan_start_time >= feature.rtMin "+
                    "AND spectrum.scan_start_time <= feature.rtMax "+
                    "AND spectrum.base_peak_mz >= feature.mzMin "+
                    "AND spectrum.base_peak_mz <= feature.mzMax")  
于 2012-05-04T09:06:38.830 に答える
1

Ludoのスクリプトを実行したところ、システムで1451秒が報告されました。次に、次のインデックスを追加しました。これにより、時間が875秒に短縮されました(40%短縮)。

CREATE INDEX idx1 ON feature (lnk_feature, mzMin, mzMax, rtMin, rtMax);

それでも目がくらむほど速くはありませんが、より良いです。EXPLAINQUERYPLANの出力は次のとおりです。

0|0|0|SCAN TABLE spectrum (~20000 rows)
0|1|1|SEARCH TABLE feature USING COVERING INDEX idx1 (lnk_feature=? AND mzMin<?) (~7 rows)

これはカバーインデックスであることに注意してください。つまり、SQLiteはインデックスから必要なすべてのフィールドを読み取ることができるため、機能テーブルから読み取る必要はありません。また、WHERE句の5つの条件のうち2つ(1つだけではなく)のインデックスを使用することもできます。

于 2012-09-16T22:09:30.040 に答える
0

これを変える

CREATE INDEX `fk_spectrum_msrun1` ON `spectrum` (`msrun_msrun_id` ASC);

これらの1つ(どちらか選択的)に

CREATE INDEX `fk_spectrum_msrun1_1` ON `spectrum` (`msrun_msrun_id`, `scan_start_time`);
CREATE INDEX `fk_spectrum_msrun1_2` ON `spectrum` (`msrun_msrun_id`, `base_peak_mz`);

1つ目はの比較を高速化できscan_start_time、2つ目はの比較を高速化できる可能性がありますbase_peak_mz。これらは不等式の比較であるため、両方の列のインデックスは役に立ちません。

于 2012-05-04T09:56:35.307 に答える
0

1.<=または=>の代わりにbetweenを使用します。

2.scan_start_timeとbase_peak_mzにインデックスを追加します

于 2012-05-04T10:39:11.537 に答える