SQLite adalah database relasional populer yang Anda sematkan ke dalam aplikasi Anda. Dengan peningkatan jumlah data dalam database Anda, Anda perlu menerapkan penyetelan kinerja SQLite. Artikel ini membahas indeks dan perangkapnya, penggunaan perencana kueri, mode jurnal Write-Ahead-Logging (WAL) dan peningkatan ukuran cache. Ini juga menjelaskan tentang pentingnya mengukur dampak tweak Anda, menggunakan pengujian otomatis.
Pengantar
SQLite adalah sistem database relasional (DB) yang populer . Tidak seperti saudaranya yang lebih besar, berbasis client-server, seperti MySQL, SQLite dapat disematkan ke dalam aplikasi Anda sebagai perpustakaan . SQLite memiliki kumpulan fitur yang sangat mirip dan juga dapat menangani jutaan baris, mengingat Anda mengetahui beberapa tip dan trik tentang penyetelan kinerja. Seperti yang akan ditunjukkan pada bagian berikut, ada lebih banyak yang perlu diketahui tentang penyetelan kinerja SQLite daripada hanya membuat indeks.
Buat indeks, tetapi dengan hati-hati
Ide dasar indeks adalah untuk mempercepat membaca data tertentu , yaitu, SELECT
pernyataan dengan WHERE
ayat. Indeks juga mempercepat penyortiran data (ORDER BY
), atau JOIN
tabel. Sayangnya, indeks adalah pedang bermata dua, karena menggunakan ruang disk tambahan dan memperlambat manipulasi data (INSERT
, UPDATE
, DELETE
).
Saran umum adalah membuat indeks sesedikit mungkin, tetapi sebanyak yang diperlukan . Juga, indeks hanya masuk akal untuk lebih besar database, dengan ribuan atau jutaan baris.
Gunakan perencana kueri untuk menganalisis kueri Anda
Cara indeks digunakan secara internal oleh SQLite didokumentasikan, tetapi tidak terlalu mudah untuk dipahami. Sebagaimana diuraikan lebih lanjut oleh artikel ini, sebaiknya menganalisis kueri dengan mengawalinya dengan EXPLAIN QUERY PLAN
. Lihatlah setiap jalur keluaran, di mana ada tiga varian dasar:
SEARCH table ...
garis adalah pertanda baik – SQLite menggunakan salah satu indeks Anda!SCAN table ... USING INDEX
pertanda buruk,SCAN table ...
bahkan lebih buruk!
Cobalah untuk menghindari SCAN table [using index]
entri dalam output EXPLAIN QUERY PLAN
bila memungkinkan, karena Anda akan mengalami masalah kinerja pada database yang lebih besar. Gunakan EXPLAIN QUERY PLAN
untuk iteratif tambahkan atau ubah indeks Anda sampai tidak ada lagi SCAN table
entri muncul.
Optimalkan kueri yang melibatkan IS NOT
Memeriksa IS NOT ...
mahal karena SQLite harus memindai semua baris tabel, bahkan jika kolom yang terpengaruh memiliki indeks . Indeks hanya berguna jika Anda mencari nilai tertentu, yaitu perbandingan yang melibatkan (lebih kecil), > (lebih besar), atau = (sama), tetapi tidak berlaku untuk !=(tidak sama).
Trik kecil yang rapi adalah Anda dapat mengganti WHERE column != value
dengan WHERE column > value OR column < value
. Ini akan menggunakan indeks kolom dan secara efektif mempengaruhi semua baris yang nilainya tidak sama dengan value
. Demikian pula, sebuah WHERE stringColumn != ''
dapat diganti dengan WHERE stringColumn > ''
, karena string dapat diurutkan. Namun, saat menerapkan trik ini, pastikan Anda mengetahui cara SQLite menangani NULL
perbandingan. Misalnya, SQLite mengevaluasi NULL > ''
sebagai FALSE
.
Jika Anda menggunakan trik perbandingan seperti itu, ada peringatan lain jika kueri Anda berisi WHERE
dan ORDER BY
, masing-masing dengan kolom yang berbeda:ini akan membuat kueri menjadi tidak efisien lagi. Jika memungkinkan, gunakan sama kolom di WHERE
dan ORDER BY
, atau buat indeks penutup yang melibatkan keduanya WHERE
dan ORDER BY
kolom.
Tingkatkan kecepatan tulis dengan Write-Ahead-Log
The Write-Ahead-Logging (WAL) mode jurnal secara signifikan meningkatkan kinerja tulis/perbarui , dibandingkan dengan putar balik default default modus jurnal. Namun, seperti yang didokumentasikan di sini, ada beberapa peringatan . Misalnya, mode WAL tidak tersedia pada sistem operasi tertentu. Selain itu, ada jaminan konsistensi data yang berkurang jika terjadi kegagalan perangkat keras . Pastikan untuk membaca beberapa halaman pertama untuk memahami apa yang Anda lakukan.
Saya menemukan bahwa perintah PRAGMA synchronous = NORMAL
memberikan percepatan 3-4x. Menyetel journal_mode
ke WAL
kemudian meningkatkan kinerja lagi secara signifikan (sekitar 10x atau lebih, tergantung pada sistem operasinya).
Selain peringatan yang telah saya sebutkan, Anda juga harus mengetahui hal-hal berikut:
- Dengan menggunakan mode jurnal WAL, akan ada dua file tambahan di sebelah file database pada sistem file Anda, yang memiliki nama yang sama dengan database, tetapi diberi akhiran “-shm” dan “-wal”. Biasanya Anda tidak perlu peduli, tetapi jika Anda ingin mengirim database ke komputer lain saat aplikasi Anda sedang berjalan, jangan lupa untuk menyertakan kedua file tersebut. SQLite akan memadatkan kedua file ini ke dalam file utama setiap kali Anda biasanya menutup semua koneksi database yang terbuka.
- Kinerja penyisipan atau pembaruan akan turun sesekali, setiap kali kueri memicu penggabungan konten file log WAL ke dalam file database utama. Ini disebut pos pemeriksaan , lihat di sini.
- Saya menemukan bahwa
PRAGMA
s yang mengubahjournal_mode
dansynchronous
tampaknya tidak terus-menerus disimpan dalam database. Jadi, saya selalu jalankan kembali setiap kali saya membuka koneksi database baru, daripada hanya menjalankannya saat membuat tabel untuk pertama kalinya.
Ukur semuanya
Setiap kali Anda menambahkan penyesuaian kinerja, pastikan untuk mengukur dampaknya. Pengujian (unit) otomatis adalah pendekatan yang bagus untuk ini. Mereka membantu mendokumentasikan temuan Anda untuk tim Anda, dan mereka akan mengungkap perilaku menyimpang dari waktu ke waktu , misalnya saat Anda memperbarui ke versi SQLite yang lebih baru. Contoh untuk apa yang dapat Anda ukur:
- Apa efek menggunakan WAL mode jurnal melalui rollback mode? Apa efek dari
PRAGMA
peningkatan kinerja lainnya (seharusnya) s? - Setelah Anda menambah/mengubah/menghapus indeks, seberapa cepat
SELECT
pernyataan menjadi? Seberapa lambatINSERT/DELETE/UPDATE
pernyataan menjadi? - Berapa banyak ruang disk tambahan yang digunakan indeks?
Untuk salah satu pengujian ini, pertimbangkan untuk mengulanginya dengan berbagai ukuran database. Misalnya. jalankan di database kosong, dan juga di database yang sudah berisi ribuan (atau jutaan) entri. Anda juga harus menjalankan pengujian pada perangkat dan sistem operasi yang berbeda, terutama jika lingkungan pengembangan dan produksi Anda sangat berbeda.
Sesuaikan ukuran cache
SQLite menyimpan informasi sementara dalam cache (dalam RAM), mis. sambil membangun hasil SELECT
query, atau ketika memanipulasi data yang belum di-commit. Secara default, ukuran ini hanya 2 MB . Mesin desktop modern dapat menghemat lebih banyak. Jalankan PRAGMA cache_size = -kibibytes
untuk meningkatkan nilai ini (ingat minus tanda di depan nilainya!). Lihat di sini untuk informasi lebih lanjut. Sekali lagi, ukur apa pengaruh setelan ini terhadap performa!
Gunakan REPLACE INTO untuk membuat atau memperbarui baris
Ini mungkin bukan tweak kinerja karena ini adalah trik kecil yang rapi. Misalkan Anda perlu memperbarui baris dalam tabel t
, atau buat baris jika belum ada. Daripada menggunakan dua kueri (SELECT
diikuti dengan INSERT
atau UPDATE
), gunakan REPLACE INTO
(dokumen resmi).
Agar ini berfungsi, tambahkan kolom dummy tambahan (mis. replacer
) ke tabel t
, yang memiliki UNIQUE
memaksa. Deklarasi kolom dapat mis. menjadi ... replacer INTEGER UNIQUE ...
itu adalah bagian dari CREATE TABLE
. Anda penyataan. Kemudian gunakan kueri seperti
REPLACE INTO t (col1, col2, ..., replacer) VALUES (?,?,...,1)
Code language: SQL (Structured Query Language) (sql)
Saat kueri ini dijalankan untuk pertama kalinya, ia hanya akan melakukan INSERT
. Saat dijalankan kedua kalinya, UNIQUE
batasan replacer
kolom akan dipicu, dan perilaku resolusi konflik akan menyebabkan baris lama dihapus, membuat yang baru secara otomatis. Anda mungkin juga menemukan bahwa perintah UPSERT terkait berguna.
Kesimpulan
Setelah jumlah baris dalam database Anda bertambah, penyesuaian kinerja menjadi suatu keharusan. Indeks adalah solusi yang paling umum. Mereka memperdagangkan peningkatan kompleksitas waktu untuk mengurangi kompleksitas ruang, meningkatkan kecepatan baca, sementara secara negatif mempengaruhi kinerja modifikasi data. A sudah saya tunjukkan, Anda harus ekstra hati-hati saat membandingkan ketidaksetaraan di SELECT
pernyataan, karena SQLite tidak dapat menggunakan indeks untuk jenis perbandingan seperti itu. Saya biasanya merekomendasikan menggunakan perencana kueri yang menjelaskan apa yang terjadi secara internal untuk setiap kueri SQL. Setiap kali Anda mengubah sesuatu, ukur dampaknya!