Indeks: kinerja integer vs string jika jumlah node sama

26

Saya mengembangkan aplikasi di Ruby on Rails dengan database PostgreSQL (9.4). Untuk kasus penggunaan saya, kolom dalam tabel akan terlihat sangat sering, karena seluruh titik aplikasi mencari atribut yang sangat spesifik pada model.

Saat ini saya memutuskan apakah akan menggunakan integertipe atau hanya menggunakan tipe string yang khas (misalnya character varying(255), yang merupakan default di Rails ) untuk kolom, karena saya tidak yakin apa perbedaan kinerja pada indeks.

Kolom-kolom ini adalah enum . Mereka memiliki ukuran tetap untuk jumlah nilai yang mungkin mereka miliki. Kebanyakan panjang enum tidak melebihi 5, artinya indeks akan lebih atau kurang tetap sepanjang masa aplikasi ; dengan demikian, indeks integer dan string akan identik dalam jumlah node.

Namun, string yang akan diindeks bisa sekitar 20 karakter, yang dalam memori kira-kira 5x dari integer (jika integer adalah 4 byte, dan string ASCII murni pada 1 byte per karakter, maka ini berlaku). Saya tidak tahu bagaimana mesin database melakukan pencarian indeks, tetapi jika perlu "memindai" string sampai sama persis , maka pada dasarnya itu berarti bahwa pencarian string akan 5x lebih lambat daripada pencarian integer; "pindai" sampai cocok untuk pencarian bilangan bulat akan menjadi 4 byte, bukan 20. Inilah yang saya bayangkan:

Nilai pencarian adalah (bilangan bulat) 4:

memindai ............................ DITEMUKAN | mendapatkan catatan ... | BYTE_1 | BYTE_2 | BYTE_3 | BYTE_4 | BYTE_5 | BYTE_6 | BYTE_7 | BYTE_8 | ... |

Nilai pencarian adalah (string) "some_val" (8 bytes):

memindai ................................................. .................................... DITEMUKAN | mendapatkan catatan ... | BYTE_1 | BYTE_2 | BYTE_3 | BYTE_4 | BYTE_5 | BYTE_6 | BYTE_7 | BYTE_8 | ... |

Saya harap itu masuk akal. Pada dasarnya, karena integer mengambil lebih sedikit ruang, ia dapat "dicocokkan" lebih cepat daripada rekan senarnya. Mungkin ini tebakan yang sepenuhnya salah, tapi aku bukan ahli, jadi itu sebabnya aku bertanya pada kalian! Saya kira jawaban yang baru saya temukan ini sepertinya mendukung hipotesis saya, tetapi saya ingin memastikan.

Jumlah nilai yang mungkin di kolom tidak akan berubah dalam menggunakan salah satu, jadi indeks itu sendiri tidak akan berubah (kecuali saya menambahkan nilai baru ke enum). Dalam hal ini, apakah akan ada perbedaan kinerja dalam menggunakan integeratau varchar(255), atau apakah menggunakan tipe integer lebih masuk akal?


Alasan saya bertanya adalah bahwa enumtipe peta Rails bilangan bulat ke kunci string, tetapi mereka tidak dimaksudkan untuk menjadi kolom yang menghadap pengguna. Pada dasarnya, Anda tidak dapat melakukan verifikasi bahwa nilai enum adalah valid, karena nilai yang tidak valid akan menyebabkan ArgumentErrorsebelum validasi apa pun dapat dijalankan. Menggunakan stringtipe akan memungkinkan validasi, tetapi jika ada biaya kinerja saya lebih suka hanya meretas masalah validasi.

Chris Cirefice
sumber

Jawaban:

32

Jawaban singkat: integerlebih cepat dari varcharatau textdi setiap aspek. Tidak masalah untuk meja kecil dan / atau kunci pendek. Perbedaannya tumbuh dengan panjang kunci dan jumlah baris.

string ... 20 karakter, yang dalam memori kira-kira 5x dari integer (jika integer adalah 4 byte, dan string adalah ASCII murni pada 1 byte per karakter, maka ini berlaku)

Untuk lebih tepatnya, tipe karakter ( textatau varchar) menempati tepat 21 byte untuk 20 karakter ASCII pada disk dan 23 byte dalam RAM. Penilaian terperinci:

Juga penting: COLLATIONaturan dapat membuat pengurutan data karakter lebih mahal - tidak seperti tipe data numerik:

Ukuran indeks mungkin bertanggung jawab atas bagian terbesar dari perbedaan kinerja dalam banyak kasus. Pertimbangkan overhead per indeks tuple (pada dasarnya sama dengan untuk tabel): 4 byte untuk pointer item dan 24 byte untuk header tuple. Jadi indeks tuple integerakan berjumlah 36 byte (termasuk 4 byte alignment padding ) dan untuk varchar(20)dengan 20 karakter ASCII akan menjadi 52 byte (juga termasuk padding). Detail:

Selain semua teori: yang terbaik adalah hanya menguji:

Postgres 9.5 memperkenalkan optimasi untuk menyortir string panjang data karakter (kata kunci "kunci disingkat" ). Tetapi bug di beberapa fungsi pustaka C di Linux memaksa proyek untuk menonaktifkan fitur untuk koleksi non-C di Postgres 9.5.2. Detail dalam catatan rilis.

Namun, jika Anda benar-benar menggunakan enumtipe Postgres , sebagian besar pertimbangan ini tidak relevan, karena semua itu diimplementasikan dengan integernilai - nilai internal. Manual:

Sebuah enumnilai menempati empat byte pada disk.

Aside: varchar(255)digunakan untuk memahami SQL Server versi awal, yang dapat menggunakan tipe data yang lebih efisien secara internal hingga batas 255 karakter. Tetapi batasan panjang ganjil dari 255 karakter tidak memiliki dampak khusus pada kinerja di Postgres sama sekali.

Erwin Brandstetter
sumber
1
Tidak ada optimasi tersembunyi di SQL Server untuk varchar(255)vs. misalnya varchar(260). Mungkin ada hal seperti itu dengan SQL Server 6.x tetapi ini tidak benar untuk waktu yang lama.
a_horse_with_no_name
@a_horse_with_no_name: terima kasih, saya mengklarifikasi sesuai.
Erwin Brandstetter
Maaf sudah terlalu lama menerima ini, saya sudah lambat dalam pengembangan proyek itu;)
Chris Cirefice
Apakah jawaban ini masih berlaku untuk Postgres 10?
Matty
1
@Matty: Masih valid. Dan saya belum melihat perubahan apa pun untuk hal 11, juga.
Erwin Brandstetter