Apa karakteristik kinerja sqlite dengan file database yang sangat besar? [Tutup]

325

Saya tahu bahwa sqlite tidak berkinerja baik dengan file database yang sangat besar bahkan ketika mereka didukung (dulu ada komentar di situs sqlite yang menyatakan bahwa jika Anda memerlukan ukuran file di atas 1GB Anda mungkin ingin mempertimbangkan untuk menggunakan rdbms perusahaan. Dapatkah dapat menemukannya lagi, mungkin terkait dengan versi sqlite yang lebih lama).

Namun, untuk tujuan saya, saya ingin mengetahui seberapa buruk sebenarnya sebelum saya mempertimbangkan solusi lain.

Saya berbicara tentang file data sqlite dalam kisaran multi-gigabyte, mulai 2GB dan seterusnya. Adakah yang punya pengalaman dengan ini? Ada tips / ide?

Snazzer
sumber
1
Menggunakan threading (koneksi per utas) mungkin hanya membantu untuk membaca - stackoverflow.com/a/24029046/743263
malkia
23
Tahun 2016: Saya memiliki database 5 GB yang berjalan di SQLite tanpa masalah. Saya memasang dataset yang sama persis di Postgres. SQLite menjalankan kueri kompleks dalam 2,7 ms, Postgres dalam 2,5 ms. Saya akhirnya menggunakan Postgres untuk akses Regex yang lebih mudah dan fitur indeks yang lebih baik. Tapi saya terkesan dengan SQLite dan bisa menggunakannya juga.
Paulb

Jawaban:

246

Jadi saya melakukan beberapa tes dengan sqlite untuk file yang sangat besar, dan sampai pada beberapa kesimpulan (setidaknya untuk aplikasi spesifik saya).

Pengujian melibatkan file sqlite tunggal dengan tabel tunggal, atau beberapa tabel. Setiap tabel memiliki sekitar 8 kolom, hampir semua bilangan bulat, dan 4 indeks.

Idenya adalah untuk memasukkan data yang cukup sampai file sqlite sekitar 50GB.

Meja Tunggal

Saya mencoba memasukkan beberapa baris ke dalam file sqlite hanya dengan satu tabel. Ketika file sekitar 7GB (maaf saya tidak bisa spesifik tentang jumlah baris) penyisipan terlalu lama. Saya memperkirakan bahwa pengujian saya untuk memasukkan semua data saya akan memakan waktu 24 jam atau lebih, tetapi tidak lengkap bahkan setelah 48 jam.

Ini membuat saya menyimpulkan bahwa tabel sqlite tunggal yang sangat besar akan memiliki masalah dengan penyisipan, dan mungkin juga operasi lainnya.

Saya kira ini bukan kejutan, karena tabel semakin besar, memasukkan dan memperbarui semua indeks membutuhkan waktu lebih lama.

Beberapa Tabel

Saya kemudian mencoba membagi data berdasarkan waktu pada beberapa tabel, satu tabel per hari. Data untuk 1 tabel asli dipecah menjadi ~ 700 tabel.

Pengaturan ini tidak memiliki masalah dengan penyisipan, tidak butuh waktu lebih lama seiring berjalannya waktu, karena tabel baru dibuat untuk setiap hari.

Masalah Vakum

Seperti yang ditunjukkan oleh i_like_caffeine, perintah VACUUM adalah masalah semakin besar file sqlite. Karena semakin banyak sisipan / penghapusan yang dilakukan, fragmentasi file pada disk akan semakin buruk, sehingga tujuannya adalah secara berkala VACUUM untuk mengoptimalkan file dan memulihkan ruang file.

Namun, sebagaimana ditunjukkan oleh dokumentasi , salinan lengkap dari database dibuat untuk melakukan kekosongan, membutuhkan waktu yang sangat lama untuk diselesaikan. Jadi, semakin kecil basis datanya, semakin cepat operasi ini akan selesai.

Kesimpulan

Untuk aplikasi spesifik saya, saya mungkin akan membagi data lebih dari beberapa file db, satu per hari, untuk mendapatkan yang terbaik dari kinerja vakum dan kecepatan penyisipan / penghapusan.

Ini menyulitkan pertanyaan, tetapi bagi saya, itu tradeoff yang bermanfaat untuk dapat mengindeks data sebanyak ini. Keuntungan tambahan adalah saya bisa menghapus seluruh file db untuk menjatuhkan data sehari (operasi umum untuk aplikasi saya).

Saya mungkin harus memantau ukuran tabel per file juga untuk melihat kapan kecepatan akan menjadi masalah.

Sayang sekali sepertinya tidak ada metode vakum tambahan selain vakum otomatis . Saya tidak dapat menggunakannya karena tujuan saya untuk vakum adalah untuk mendefrag file (ruang file bukan masalah besar), yang vakum otomatis tidak lakukan. Faktanya, dokumentasi menyatakan ini dapat memperburuk fragmentasi, jadi saya harus menggunakan vakum penuh pada file secara berkala.

Snazzer
sumber
5
Info yang sangat berguna. Spekulasi murni tetapi saya bertanya-tanya apakah api cadangan baru dapat digunakan untuk membuat versi database Anda yang tidak terfragmentasi setiap hari, dan menghindari kebutuhan untuk menjalankan VACUUM.
eodonohoe
24
Saya ingin tahu, apakah semua INSER Anda dalam transaksi?
Paul Lefebvre
9
Ya, sisipan dilakukan dalam batch 10.000 pesan per transaksi.
Snazzer
6
Sistem file apa yang Anda gunakan? Jika ext {2,3,4}, apa pengaturan data =, apakah penjurnalan diaktifkan? Selain pola IO, cara sqlite flush ke disk mungkin signifikan.
Tobu
5
Saya menguji terutama di windows, jadi tidak bisa mengomentari perilaku di linux.
Snazzer
169

Kami menggunakan DBS 50 GB + di platform kami. tidak mengeluh bekerja dengan baik. Pastikan Anda melakukan semuanya dengan benar! Apakah Anda menggunakan pernyataan yang sudah ditentukan sebelumnya? * SQLITE 3.7.3

  1. Transaksi
  2. Pernyataan yang dibuat sebelumnya
  3. Terapkan pengaturan ini (tepat setelah Anda membuat DB)

    PRAGMA main.page_size = 4096;
    PRAGMA main.cache_size=10000;
    PRAGMA main.locking_mode=EXCLUSIVE;
    PRAGMA main.synchronous=NORMAL;
    PRAGMA main.journal_mode=WAL;
    PRAGMA main.cache_size=5000;

Semoga ini akan membantu orang lain, bekerja dengan baik di sini

Alex
sumber
22
Baru-baru ini diuji dengan dbs dalam kisaran 160GB, bekerja dengan sangat baik.
Snazzer
10
Juga PRAGMA main.temp_store = MEMORY;.
Vikrant Chaudhary
40
@Alex, mengapa ada dua PRAGMA main.cache_size = 5000 ;?
Jack
23
Jangan hanya membabi buta menerapkan optimasi ini. Secara khusus, sinkron = NORMAL tidak aman untuk crash. Yaitu, proses crash pada waktu yang tepat dapat merusak database Anda bahkan tanpa adanya kegagalan disk. sqlite.org/pragma.html#pragma_synchronous
mpm
22
@ Alex, bisakah Anda menjelaskan nilai-nilai itu dan perbedaan antara mereka dan yang standar?
4m1nh4j1
65

Saya telah membuat basis data SQLite hingga berukuran 3,5GB tanpa masalah kinerja yang nyata. Jika saya ingat dengan benar, saya pikir SQLite2 mungkin memiliki beberapa batasan yang lebih rendah, tetapi saya tidak berpikir SQLite3 memiliki masalah seperti itu.

Menurut halaman Batas SQLite , ukuran maksimum setiap halaman basis data adalah 32 ribu. Dan halaman maksimum dalam database adalah 1024 ^ 3. Jadi menurut hitungan matematis saya yang keluar menjadi 32 terabyte. Saya pikir Anda akan mencapai batas sistem file Anda sebelum mencapai SQLite!

Paul Lefebvre
sumber
3
Bergantung pada operasi apa yang Anda lakukan, mencoba menghapus 3000 baris dalam database 8G sqlite, dibutuhkan waktu yang cukup bagi Anda untuk membuat sebotol pers Prancis yang bagus, lol
benjaminz
4
@ Benjaminz, Anda pasti salah melakukannya. Jika Anda membungkus penghapusan 3k baris dalam satu transaksi, itu harus hampir instan. Saya memiliki kesalahan ini sendiri: menghapus baris 10k satu per satu membutuhkan waktu 30 menit. Tapi begitu saya membungkus semua pernyataan hapus ke dalam satu transaksi, butuh 5 detik.
mvp
55

Sebagian besar alasan yang dibutuhkan> 48 jam untuk melakukan sisipan Anda adalah karena indeks Anda. Ini sangat cepat untuk:

1 - Jatuhkan semua indeks 2 - Lakukan semua sisipan 3 - Buat indeks lagi

pengguna352992
sumber
23
Itu terkenal ... tapi untuk proses yang berjalan lama Anda tidak akan secara berkala menjatuhkan indeks Anda untuk membangun kembali, terutama ketika Anda akan meminta mereka untuk melakukan pekerjaan. Itu adalah pendekatan yang diambil meskipun ketika sqlite db harus dibangun kembali dari awal, indeks dibuat setelah semua sisipan dilakukan.
Snazzer
24
@Snazzer dalam situasi yang sama kami menggunakan tabel "akumulator": sekali sehari kami kemudian akan memindahkan baris akumulasi dari tabel akumulator ke tabel utama dalam satu transaksi. Di mana diperlukan tampilan mengurus menyajikan kedua tabel sebagai satu tabel.
CAFxX
4
Pilihan lain adalah untuk menjaga indeks, tetapi pra-sortir data dalam urutan indeks sebelum Anda memasukkannya.
Steven Kryskalla
1
@ SvenvenKryskalla bagaimana jika dibandingkan dengan menjatuhkan indeks dan membuatnya kembali? Adakah tautan yang Anda kenal yang telah dibandingkan?
mcmillab
1
@mcmillab Ini adalah tahun yang lalu jadi saya tidak ingat semua detail atau statistik benchmark, tetapi berpikir secara intuitif, memasukkan N elemen yang dipesan secara acak ke dalam indeks akan memakan waktu O (NlogN), sementara memasukkan N elemen yang diurutkan akan mengambil O (N ) waktu.
Steven Kryskalla
34

Selain rekomendasi yang biasa:

  1. Indeks penurunan untuk memasukkan massal.
  2. Sisipan / pembaruan batch dalam transaksi besar.
  3. Setel cache penyangga Anda / nonaktifkan jurnal / w PRAGMA.
  4. Gunakan mesin 64bit (untuk dapat menggunakan banyak cache ™).
  5. [ditambahkan Juli 2014] Gunakan persamaan tabel umum (CTE) alih-alih menjalankan beberapa kueri SQL! Membutuhkan rilis SQLite 3.8.3.

Saya telah mempelajari yang berikut dari pengalaman saya dengan SQLite3:

  1. Untuk kecepatan memasukkan maksimum, jangan gunakan skema dengan batasan kolom apa pun. (Ubah tabel nanti sesuai kebutuhan Anda tidak dapat menambahkan kendala dengan ALTER TABLE).
  2. Optimalkan skema Anda untuk menyimpan apa yang Anda butuhkan. Kadang-kadang ini berarti memecah tabel dan / atau bahkan mengompresi / mengubah data Anda sebelum memasukkan ke database. Contoh yang bagus adalah menyimpan alamat IP sebagai integer (panjang).
  3. Satu tabel per file db - untuk meminimalkan pertikaian kunci. (Gunakan ATTACH DATABASE jika Anda ingin memiliki objek koneksi tunggal.
  4. SQLite dapat menyimpan berbagai jenis data dalam kolom yang sama (pengetikan dinamis), gunakan itu untuk keuntungan Anda.

Selamat datang pertanyaan / komentar. ;-)

Lester Cheung
sumber
1
Seberapa besar dampak yang Anda dapatkan dari 'satu tabel per file db'? Kedengarannya menarik. Apakah Anda pikir itu akan sangat berarti jika meja Anda hanya memiliki 3 meja dan sedang dibangun dari awal?
Martin Velez
4
@martin benci mengatakannya tetapi jawabannya tergantung . Idenya adalah mempartisi data ke ukuran yang bisa dikelola. Dalam use case saya, saya mengumpulkan data dari host yang berbeda dan melakukan pelaporan data setelah fakta sehingga pendekatan ini bekerja dengan baik. Partisi berdasarkan tanggal / waktu seperti yang disarankan oleh orang lain harus bekerja dengan baik untuk data yang rentang waktu yang lama saya bayangkan.
Lester Cheung
3
@Lester Cheung: Mengenai orang kedua Anda # 1: Ini adalah pemahaman saya dari dokumen dan pengalaman pribadi bahwa hingga hari ini, SQLite3 tidak mendukung penambahan kendala dengan ALTER TABLE setelah pembuatan tabel. Satu-satunya cara untuk menambah atau menghapus batasan dari baris tabel yang ada adalah membuat tabel baru dengan karakteristik yang diinginkan dan menyalin semua baris, yang cenderung jauh lebih lambat daripada menyisipkan sekali dengan kendala.
Mumbleskates
3
@Widdershins, Anda benar mutlak - ALTER TABLE di SQLite tidak mengizinkan penambahan kendala. Saya tidak tahu apa yang saya merokok - akan memperbarui jawabannya - terima kasih.
Lester Cheung
Tak satu pun dari saran-saran itu ada hubungannya dengan menggunakan file db SQLite humongous. Apakah pertanyaan sudah diedit sejak jawaban ini diajukan?
A. Rager
9

Saya pikir keluhan utama tentang penskalaan sqlite adalah:

  1. Proses tunggal menulis.
  2. Tanpa mirroring.
  3. Tidak ada replikasi.
Tidak dikenal
sumber
9

Saya memiliki database SQLite 7GB. Untuk melakukan kueri tertentu dengan gabungan dalam dibutuhkan 2,6s Untuk mempercepat ini, saya mencoba menambahkan indeks. Bergantung pada indeks mana yang saya tambahkan, terkadang kueri turun ke 0,1s dan kadang-kadang naik hingga 7s. Saya pikir masalah dalam kasus saya adalah bahwa jika sebuah kolom sangat duplikat maka menambahkan indeks menurunkan kinerja :(

Mike Oxynormas
sumber
9
Mengapa kolom dengan banyak duplikat menurunkan kinerja (pertanyaan serius)?
Martin Velez
6
kolom dengan kardinalitas rendah lebih sulit diindeks: stackoverflow.com/questions/2113181/…
metrix
9

Dulu ada pernyataan dalam dokumentasi SQLite bahwa batas ukuran praktis dari file database adalah beberapa lusin GB: s. Itu sebagian besar karena perlunya SQLite untuk "mengalokasikan bitmap halaman kotor" setiap kali Anda memulai transaksi. Maka 256 byte RAM diperlukan untuk setiap MB dalam database. Memasukkan file DB 50 GB akan membutuhkan jumlah yang lumayan (2 ^ 8) * (2 ^ 10) = 2 ^ 18 = 256 MB RAM.

Tetapi pada versi terbaru SQLite, ini tidak lagi diperlukan. Baca lebih lanjut di sini .

Alix Axel
sumber
25
Saya sangat menyesal bahwa saya harus menunjukkan ini, tetapi 2^18sebenarnya hanya 256 K.
Gabriel Schreiber
7
@GabrielSchreiber itu, dan juga fakta bahwa 50GB tidak (2 ^ 10) MB, itu hanya 1GB. Jadi untuk basis data 50GB, Anda memerlukan memori 12,5MB: (2 ^ 8) * (2 ^ 10) * 50
elipoultorak
8

Saya mengalami masalah dengan file sqlite besar saat menggunakan perintah vakum.

Saya belum mencoba fitur auto_vacuum. Jika Anda berharap untuk memperbarui dan menghapus data sering maka ini layak dilihat.

eodonohoe
sumber