Apa dampak kinerja menggunakan CHAR vs VARCHAR pada bidang ukuran tetap?

58

Saya memiliki kolom yang diindeks yang menyimpan hash MD5. Dengan demikian, kolom akan selalu menyimpan nilai 32 karakter. Untuk alasan apa pun, ini diciptakan sebagai varchar daripada char. Apakah sepadan dengan kesulitan memigrasi basis data untuk mengubahnya menjadi char? Ini di MySQL 5.0 dengan InnoDB.

Jason Baker
sumber
6
PERINGATAN Pertanyaan ini dan jawabannya ditulis sebelum InnoDB dan utf8 adalah defaultnya.
Rick James

Jawaban:

56

Pertanyaan serupa diajukan sebelumnya

Implikasi kinerja dari ukuran MySQL VARCHAR

Ini kutipan dari jawaban saya

Anda harus menyadari pengorbanan menggunakan CHAR vs VARCHAR

Dengan bidang CHAR, apa yang Anda alokasikan adalah persis apa yang Anda dapatkan. Misalnya, CHAR (15) mengalokasikan dan menyimpan 15 byte, tidak peduli bagaimana karakter yang Anda tempatkan di bidang. Manipulasi string sederhana dan mudah karena ukuran bidang data benar-benar dapat diprediksi.

Dengan bidang VARCHAR, Anda mendapatkan cerita yang sama sekali berbeda. Misalnya VARCHAR (15) sebenarnya mengalokasikan secara dinamis hingga 16 byte, hingga 15 untuk data dan, setidaknya, 1 byte tambahan untuk menyimpan panjang data. Jika Anda memiliki string 'halo' untuk menyimpan yang akan mengambil 6 byte, bukan 5. Manipulasi string harus selalu melakukan semacam pemeriksaan panjang dalam semua kasus.

Imbalan lebih jelas ketika Anda melakukan dua hal: 1. Menyimpan jutaan atau miliaran baris 2. Kolom pengindeksan yang baik CHAR atau VARCHAR

TRADEOFF # 1 Jelas, VARCHAR memegang keuntungan karena data panjang variabel akan menghasilkan baris yang lebih kecil dan, dengan demikian, file fisik yang lebih kecil.

TRADEOFF # 2 Karena bidang CHAR membutuhkan lebih sedikit manipulasi string karena lebar bidang tetap, pencarian indeks terhadap bidang CHAR rata-rata 20% lebih cepat daripada bidang VARCHAR. Ini bukan dugaan saya. Buku MySQL Database Design and Tuning melakukan sesuatu yang luar biasa pada tabel MyISAM untuk membuktikan ini. Contoh dalam buku ini melakukan sesuatu seperti berikut:

ALTER TABLE tblname ROW_FORMAT=FIXED;

Arahan ini memaksa semua VARCHAR berperilaku sebagai CHAR. Saya melakukan ini di pekerjaan saya sebelumnya pada tahun 2007 dan mengambil meja 300GB dan mempercepat pencarian indeks sebesar 20%, tanpa mengubah apa pun. Ini berfungsi seperti yang dipublikasikan. Namun, itu memang menghasilkan tabel hampir dua kali lipat, tetapi itu hanya kembali ke tradeoff # 1.

Anda dapat menganalisis data yang disimpan untuk melihat apa yang direkomendasikan MySQL untuk definisi kolom. Jalankan saja berikut ini di tabel mana saja:

SELECT * FROM tblname PROCEDURE ANALYSE();

Ini akan melintasi seluruh tabel dan merekomendasikan definisi kolom untuk setiap kolom berdasarkan data yang dikandungnya, nilai bidang minimum, nilai bidang maksimum, dan sebagainya. Terkadang, Anda hanya perlu menggunakan akal sehat dengan merencanakan CHAR vs VARCHAR. Ini adalah contoh yang bagus:

Jika Anda menyimpan alamat IP, mask untuk kolom seperti itu paling banyak 15 karakter (xxx.xxx.xxx.xxx). Saya akan melompat tepat CHAR(15)dalam sekejap karena panjang alamat IP tidak akan terlalu bervariasi dan kompleksitas manipulasi string yang dikendalikan oleh byte tambahan. Anda masih bisa melakukan PROCEDURE ANALYSE()terhadap kolom seperti itu. Bahkan mungkin merekomendasikan VARCHAR. Uang saya masih di CHAR atas VARCHAR dalam hal ini.

Masalah CHAR vs VARCHAR hanya dapat diselesaikan melalui perencanaan yang tepat. Dengan kekuatan besar datang tanggung jawab besar (klise tapi benar).

MEMPERBARUI

Ketika datang ke MD5, perhitungan strleninternal harus dihilangkan ketika mengganti seluruh format baris. Tidak perlu mengubah definisi bidang.

Jika kunci MD5 adalah satu-satunya VARCHAR yang ada, saya akan menggunakannya dan mengonversi format baris tabel menjadi tetap . Jika ada jumlah signifikan bidang VARCHAR lain yang hadir, mereka akan mendapat manfaat juga. Sebagai gantinya, tabel akan berkembang menjadi sekitar dua kali ukurannya. Tetapi kueri harus mempercepat sekitar 20% lebih tanpa penyetelan tambahan.

RolandoMySQLDBA
sumber
1
Saya pikir saya akan menggunakan char (4) atau sesuatu seperti integer yang tidak ditandatangani untuk alamat IP
Jack Douglas
@JackPDouglas Anda benar tentang hal itu.
RolandoMySQLDBA
Bukankah indeks disimpan dengan panjang yang tetap? Saya tidak mengerti bagaimana mengubah format penyimpanan ke perbaikan indeks yang diperbaiki. Apakah maksud Anda meningkatkan pemindaian tabel?
Marcus Adams
1
@ JackDouglas, Kenapa tidak bitdan binary?
Pacerier
@Pacerier itu akan lebih baik, saya setuju :)
Jack Douglas
19

Sepertinya Anda akan menghemat 1 byte per nilai atau sekitar 3% dengan mengonversi ke a char. Mungkin tidak layak jika Anda menyimpan MD5 dalam hex - Anda bisa menghemat 50% dengan menggunakan binarygantinya.

Terima kasih kepada Ovais (lihat komentar) untuk menunjukkan bahwa char(32)dapat menggunakan lebih dari 32 byte jika Anda menggunakan rangkaian karakter multibyte.

Terima kasih kepada Rick James untuk menunjukkan bahwa Anda harus menggunakan unhexfungsi untuk mengubah string hex menjadi biner:

create table foo(bar varbinary(100));
insert into foo(bar) values(md5('a')); 
insert into foo(bar) values(unhex(md5('a'))); 
select length(bar) from foo;
| panjang (bar) |
| ----------: |
| 32 |
| 16 |

db <> biola di sini

Jack Douglas
sumber
Panggilan bagus untuk beralih ke biner.
RThomas
Saya berencana untuk mengubah ini menjadi biner. Sekarang saya berpikir tentang hal itu, ukurannya tidak boleh berbeda hanya berdasarkan apakah saya menggunakan byte atau char karena pengkodean kami utf-8. Atau saya salah?
Jason Baker
@Jason - penyandian tidak berlaku untuk binary- atau apakah saya salah paham?
Jack Douglas
3
untuk kolom char (32) dengan set karakter utf-8, setiap nilai membutuhkan 32x3 byte untuk penyimpanan. Mengapa Anda perlu mengatur nilai hash MD5 menjadi utf-8. Konversi ke biner (32) akan membutuhkan 32 byte per nilai.
ovais.tariq
1
Mengubah untuk BINARYmelakukan sangat sedikit kecuali Anda juga menggunakannya UNHEX(). Artinya, Anda dapat menyimpan UNHEX(MD5(x))menjadi 16-byte BINARY(16)untuk menghemat ruang yang signifikan atas menyimpan MD5(x)ke dalam CHAR(32) CHARACTER SET ascii.
Rick James
15

Tidak ada gunanya mengubah pendapat saya. Jika Anda melihat dokumentasi di sini harus menggambarkan perbedaan antara keduanya. Dalam skenario penggunaan Anda, yang satu tidak benar-benar menawarkan manfaat yang signifikan di atas yang lain kecuali Anda benar-benar khawatir tentang sedikit biaya tambahan terkait dengan ukuran baris.

http://dev.mysql.com/doc/refman/5.0/id/char.html

Perhatikan juga komentar pertama pada dokumentasi yang saya tautkan di atas ... "CHAR hanya akan mempercepat akses Anda jika seluruh catatan berukuran tetap. Artinya, jika Anda menggunakan objek ukuran variabel apa pun, Anda sebaiknya membuat semuanya ukuran variabel. Anda tidak mendapatkan kecepatan dengan menggunakan CHAR di tabel yang juga berisi VARCHAR "

RThomas
sumber
"Speedup" itu berlaku untuk MyISAM, bukan InnoDB.
Rick James