Apakah ada kerugian menggunakan varchar generik (255) untuk semua bidang berbasis teks?

100

Aku punya contactstabel yang berisi bidang-bidang seperti postcode, first name, last name, town, country, phone numberdll, yang semuanya didefinisikan sebagai VARCHAR(255)meskipun tak satu pun dari bidang-bidang ini akan pernah datang dekat untuk memiliki 255 karakter. (Jika Anda bertanya-tanya, begini karena migrasi Ruby on Rails memetakan bidang String ke VARCHAR(255)secara default dan saya tidak pernah repot-repot menimpanya).

Karena VARCHAR hanya akan menyimpan jumlah karakter aktual dari bidang tersebut (bersama dengan panjang bidang), apakah ada keuntungan yang berbeda (kinerja atau sebaliknya) untuk menggunakan, katakanlah, VARCHAR(16)lebih VARCHAR(255)?

Selain itu, sebagian besar bidang ini memiliki indeks. Apakah ukuran VARCHAR yang lebih besar di lapangan mempengaruhi ukuran atau kinerja indeks sama sekali?

FYI Saya menggunakan MySQL 5.

Olly
sumber
2
@ceejayoz, menyatakan bahwa jawaban yang diterima salah tanpa menjelaskan mengapa tidak terlalu membantu. Yang membuatnya lebih buruk adalah bahwa jawaban yang diterima dapat berubah seiring waktu dan komentar Anda akan membingungkan orang sehingga berpikir bahwa jawaban baru yang diterima itu salah.
Gili
1
@Gili Menghapus komentar saya karena OP tampaknya mengubah penerimaan mereka. Poin bagus, di masa depan saya akan menunjukkan jawaban mana yang saya bicarakan dan mengapa.
ceejayoz
Beberapa jawaban lain pada pertanyaan duplikat ini, stackoverflow.com/questions/1262174/…
James McMahon

Jawaban:

129

Dalam penyimpanan, VARCHAR(255)cukup pintar untuk menyimpan hanya panjang yang Anda butuhkan pada baris tertentu, tidak seperti CHAR(255)yang selalu menyimpan 255 karakter.

Tetapi karena Anda menandai pertanyaan ini dengan MySQL, saya akan menyebutkan tip khusus MySQL: karena baris disalin dari lapisan mesin penyimpanan ke lapisan SQL, VARCHARbidang diubah CHARuntuk mendapatkan keuntungan dari bekerja dengan baris dengan lebar tetap. Jadi string dalam memori menjadi berlapis hingga panjang maksimumVARCHAR kolom yang Anda nyatakan .

Saat kueri Anda secara implisit menghasilkan tabel sementara, misalnya saat menyortir atau GROUP BY, ini bisa menggunakan banyak memori. Jika Anda menggunakan banyak VARCHAR(255)bidang untuk data yang tidak perlu sepanjang itu, ini bisa membuat tabel sementara menjadi sangat besar.

Anda mungkin juga ingin mengetahui bahwa perilaku "padding out" ini berarti bahwa string yang dideklarasikan dengan rangkaian karakter utf8 mengisi hingga tiga byte per karakter bahkan untuk string yang Anda simpan dengan konten byte tunggal (misalnya karakter ascii atau latin1). Dan juga set karakter utf8mb4 menyebabkan string menjadi empat byte per karakter dalam memori.

Jadi VARCHAR(255)dalam utf8 menyimpan string pendek seperti "Tidak ada opini" membutuhkan 11 byte pada disk (sepuluh karakter dengan karakter yang lebih rendah, ditambah satu byte untuk panjangnya) tetapi membutuhkan 765 byte dalam memori, dan dengan demikian dalam tabel temp atau hasil yang diurutkan.

Saya telah membantu pengguna MySQL yang tanpa sadar sering membuat tabel temp 1,5GB dan mengisi ruang disk mereka. Mereka memiliki banyak VARCHAR(255)kolom yang dalam praktiknya menyimpan string yang sangat pendek.

Sebaiknya tentukan kolom berdasarkan jenis data yang ingin Anda simpan. Ini memiliki manfaat untuk menegakkan batasan terkait aplikasi, seperti yang telah disebutkan orang lain. Tetapi memiliki manfaat fisik untuk menghindari pemborosan memori yang saya jelaskan di atas.

Sulit untuk mengetahui alamat pos yang paling panjang tentunya, itulah sebabnya banyak orang memilih panjang VARCHARyang tentunya lebih panjang dari alamat manapun. Dan 255 adalah kebiasaan karena ini adalah panjang maksimum a VARCHARyang panjangnya dapat dikodekan dengan satu byte. Itu juga merupakan VARCHARpanjang maksimum di MySQL yang lebih tua dari 5.0.

Bill Karwin
sumber
6
Saya selalu berpikir 255digunakan agar panjang string bisa masuk ke dalam satu byte
BlueRaja - Danny Pflughoeft
3
@BlueRaja: Itu mungkin benar untuk database yang struktur file internalnya menyandikan panjang string dalam satu byte, atau jika mereka menyandikan string pendek dalam satu byte. Tapi itu tidak lagi berlaku untuk kebanyakan database.
Bill Karwin
7
@BlueRaja: InnoDB tidak menyimpan panjang varchar berikut, ini menyimpan serangkaian offset bidang untuk semua bidang di baris. Offset bidang ini mungkin 1 byte jika ukuran baris total kurang dari 127 byte, atau 2 byte. Lihat forge.mysql.com/wiki/MySQL_Internals_InnoDB
Bill Karwin
6
@BlueRaja: MyISAM (bagi mereka yang masih menggunakannya) menyimpan panjang varchar, dan ini dapat disimpan dalam 1 atau 2 byte. Namun: "Saat mengirim kunci ke handler untuk index_read () atau records_in_range, kami selalu menggunakan panjang 2-byte untuk VARCHAR agar lebih sederhana." Lihat forge.mysql.com/wiki/MySQL_Internals_MyISAM
Bill Karwin
1
satu pertanyaan - menyortir dan mengelompokkan berdasarkan bidang apa pun atau bidang varchar itu sendiri?
Rohit Banga
24

Selain pertimbangan ukuran dan kinerja dari pengaturan ukuran varchar (dan mungkin lebih penting, karena penyimpanan dan pemrosesan menjadi lebih murah setiap detik), kerugian menggunakan varchar (255) "hanya karena" adalah integritas data berkurang .

Menentukan batas maksimum untuk string adalah hal yang baik untuk dilakukan untuk mencegah string yang lebih panjang dari yang diharapkan memasuki RDBMS dan menyebabkan buffer overrun atau pengecualian / kesalahan nanti saat mengambil dan mengurai nilai dari database yang lebih panjang (lebih banyak byte) dari yang diharapkan.

Misalnya, jika Anda memiliki bidang yang menerima string dua karakter untuk singkatan negara, maka Anda tidak memiliki alasan untuk mengharapkan pengguna Anda (dalam konteks ini, pemrogram) untuk memasukkan nama negara secara lengkap. Karena Anda tidak ingin mereka memasukkan "Antigua dan Barbuda" (AG) atau "Pulau Heard dan Kepulauan McDonald" (HM), Anda tidak mengizinkannya di lapisan database. Juga, kemungkinan beberapa programmer belum melakukan RTFM untuk dokumentasi desain ( yang pasti ada ) untuk mengetahui untuk tidak melakukan ini.

Setel bidang untuk menerima dua karakter dan biarkan RDBMS menanganinya (baik secara anggun dengan memotong atau tidak dengan benar dengan menolak SQL mereka dengan kesalahan).

Contoh data nyata yang tidak memiliki alasan melebihi panjang tertentu:

  • Kode Pos Kanada dalam format A1A1A1 dan selalu 6 karakter, bahkan untuk Sinterklas (6 karakter tidak termasuk spasi yang dapat ditentukan untuk keterbacaan).
  • alamat email - hingga 64 byte sebelum @, hingga 255 byte setelahnya. Tidak lebih, jangan sampai Anda merusak Internet.
  • Nomor Telepon Amerika Utara tidak boleh lebih dari 10 digit (tidak termasuk kode negara).
  • Komputer yang menjalankan (versi terbaru) Windows tidak boleh memiliki nama komputer yang lebih panjang dari 63 byte , meskipun lebih dari 15 tidak disarankan dan akan merusak sistem server Windows NT Anda.
  • Singkatan negara bagian terdiri dari 2 karakter (seperti kode negara yang dicontohkan di atas)
  • Nomor pelacakan UPS terdiri dari 18-, 12-, 11-, atau 9-karakter. Angka 18 karakter dimulai dengan "1Z" dan angka 11 karakter diawali dengan "T" yang membuat Anda bertanya-tanya bagaimana mereka mengirimkan semua paket tersebut jika mereka tidak mengetahui perbedaan antara huruf dan angka.

Dan seterusnya...

Luangkan waktu untuk memikirkan data Anda dan batasannya. Jika Anda seorang arsitek, pengembang, atau programmer, itu adalah tugas Anda .

Dengan menggunakan varchar (n) dan bukan varchar (255) Anda menghilangkan masalah di mana pengguna (pengguna akhir, pemrogram, program lain) memasukkan data panjang yang tidak terduga yang akan kembali menghantui kode Anda nanti.

Dan saya tidak mengatakan Anda juga tidak harus menerapkan pembatasan ini dalam kode logika bisnis yang digunakan oleh aplikasi Anda.

shufler
sumber
5
Kode pos Kanada sebenarnya memiliki 7 digit, spasi di tengahnya penting, dan harus ditunjukkan pada label surat. Nomor telepon Amerika Utara mungkin memiliki lebih dari 10 digit jika ada perpanjangan. Jika Anda baik-baik saja karena tidak dapat menyimpan ekstensi nomor telepon, 10 digit baik-baik saja, tetapi Anda mungkin akan menyesalinya.
Kibbee
3
Jelas ada kasus untuk membatasi integritas data. Padahal, masih mudah untuk terlalu membatasi. Terapkan batasan untuk data yang Anda kontrol, dan terapkan batasan yang wajar untuk persyaratan data yang tidak dapat Anda kontrol. Pembatasan nomor telepon dan email Anda masuk akal (dengan asumsi Anda tidak pernah menginternasionalkan). Persyaratan Anda yang mengatakan bahwa memotong kode negara dua karakter adalah hal yang "anggun" adalah gila. Anda tahu ada kesalahan, jangan potong dan terima. Jika Anda memotong, ada kemungkinan sangat tinggi Anda akan mendapatkan kode negara yang salah.
coderjoe
Sebagian besar aplikasi akan memiliki validasi data sebelum mengirimkannya ke database ...
Cobby
2
Tentu. Paling. Tetapi saya merasa di sini Anda berasumsi bahwa pengembang yang mengembangkan aplikasi baru untuk database yang ada mengetahui batasan data (kami tidak semua ahli dalam setiap jenis data dan bagaimana penerapannya di setiap database. ). Hanya karena Anda dapat memvalidasi data dalam aplikasi Anda tidak berarti Anda melakukannya.
shufler
3
the design documentation (which surely exists)Hah. : D
Camilo Martin
14

Aku bersamamu. Perhatian yang cerewet terhadap detail adalah nyeri di leher dan memiliki nilai terbatas.

Dahulu kala, disk adalah komoditas yang berharga dan kami biasa mengeluarkan peluru untuk mengoptimalkannya. Harga penyimpanan telah turun dengan faktor 1.000, membuat waktu yang dihabiskan untuk memeras setiap byte menjadi kurang berharga.

Jika Anda hanya menggunakan bidang CHAR, Anda bisa mendapatkan baris dengan panjang tetap. Ini dapat menyimpan beberapa disk yang ditampilkan kembali secara nyata jika Anda memilih ukuran yang akurat untuk bidang. Anda mungkin mendapatkan data yang lebih padat (lebih sedikit I / O untuk pemindaian tabel) dan pembaruan yang lebih cepat (lebih mudah menemukan ruang terbuka di blok untuk pembaruan dan penyisipan.)

Namun, jika Anda memperkirakan ukuran Anda secara berlebihan, atau ukuran data Anda yang sebenarnya adalah variabel, Anda akan menghabiskan ruang dengan bidang CHAR. Data akan berakhir kurang padat (mengarah ke lebih banyak I / O untuk pengambilan besar).

Umumnya, manfaat kinerja dari upaya untuk meletakkan ukuran pada bidang variabel kecil. Anda dapat dengan mudah melakukan benchmark dengan menggunakan VARCHAR (255) dibandingkan dengan CHAR (x) untuk melihat apakah Anda dapat mengukur perbedaannya.

Namun, terkadang, saya perlu memberikan petunjuk "kecil", "sedang", "besar". Jadi saya menggunakan 16, 64, dan 255 untuk ukuran.

S. Lott
sumber
13

Saat ini, saya tidak bisa membayangkan itu benar-benar penting lagi.

Ada overhead komputasi untuk menggunakan kolom panjang variabel, tetapi dengan kelebihan CPU saat ini, itu bahkan tidak perlu dipertimbangkan. Sistem I / O sangat lambat sehingga membuat biaya komputasi apa pun untuk menangani varchars tidak ada secara efektif. Faktanya, harga varchar secara komputasi mungkin merupakan kemenangan bersih atas jumlah ruang disk yang disimpan dengan menggunakan bidang panjang variabel di atas bidang panjang tetap. Anda kemungkinan besar memiliki kepadatan baris yang lebih besar.

Sekarang, kerumitan bidang varchar adalah Anda tidak dapat dengan mudah menemukan data melalui nomor catatannya. Ketika Anda memiliki ukuran baris dengan panjang tetap (dengan bidang panjang tetap), sangat mudah untuk menghitung blok disk yang ditunjukkan oleh id baris. Dengan ukuran baris yang bervariasi, jenis seperti itu akan keluar jendela.

Jadi, sekarang Anda perlu mempertahankan beberapa jenis indeks nomor catatan, sama seperti kunci utama lainnya, ATAU Anda perlu membuat pengenal baris yang kuat yang mengkodekan detail (seperti blok, dll.) Ke pengenal. Jika Anda melakukannya, id harus dihitung ulang jika baris dipindahkan pada penyimpanan persisten. Bukan masalah besar, hanya perlu menulis ulang semua entri indeks dan memastikan Anda a) tidak pernah memaparkannya kepada konsumen atau b) tidak pernah menyatakan bahwa nomor tersebut dapat diandalkan.

Tetapi karena kita memiliki field varchar hari ini, satu-satunya nilai varchar (16) di atas varchar (255) adalah bahwa DB akan memberlakukan batas 16 karakter pada varchar (16). Jika model DB seharusnya benar-benar mewakili model data fisik, maka memiliki panjang bidang dapat menjadi nilai. Namun, jika itu hanya "penyimpanan" daripada "model DAN penyimpanan", tidak perlu sama sekali.

Kemudian Anda hanya perlu membedakan antara bidang teks yang dapat diindeks (seperti varchar) vs sesuatu yang tidak (seperti bidang teks atau CLOB). Bidang yang dapat diindeks cenderung memiliki batasan ukuran untuk memfasilitasi indeks sedangkan bidang CLOB tidak (dalam alasan).

Will Hartung
sumber
5

Dalam pengalaman saya, jika Anda mengizinkan tipe data 255 karakter, beberapa pengguna bodoh (atau beberapa penguji berpengalaman) benar-benar akan mengisinya.

Kemudian Anda memiliki semua jenis masalah, termasuk berapa banyak ruang yang Anda izinkan untuk bidang tersebut dalam laporan dan tampilan di layar dalam aplikasi Anda. Belum lagi kemungkinan melebihi batas per baris untuk data dalam database Anda (jika Anda memiliki lebih dari beberapa bidang 255 karakter ini).

Jauh lebih mudah untuk memilih batas yang wajar di awal, kemudian menerapkannya melalui aplikasi dan database.

BradC
sumber
0

Merupakan praktik yang baik untuk mengalokasikan hanya sedikit lebih dari apa yang Anda butuhkan. Nomor telepon tidak akan pernah sebesar ini.

Salah satu alasannya adalah bahwa kecuali Anda memvalidasi terhadap entri yang besar, tidak diragukan lagi seseorang akan menggunakan semua yang ada. Kemudian Anda mungkin kehabisan ruang di baris Anda. Saya tidak yakin tentang batas MySQL tetapi 8060 adalah ukuran baris maksimum di MS SQL.

Default yang lebih normal adalah 50 imho, dan kemudian meningkat jika perlu membuktikannya.

merpati
sumber
Terima kasih. Saya sangat setuju tentang ini sebagai praktik yang baik. Ini adalah aspek kinerja yang saya sangat ingin klarifikasi
Olly
0

Dalam konteks mysql, ini bisa menjadi penting ketika bekerja dengan indeks pada kolom varchar tersebut, karena mysql memiliki maks. batas 767 byte per baris-indeks.

Ini berarti bahwa ketika menambahkan indeks di beberapa kolom varchar 255 Anda bisa mencapai batas ini dengan agak cepat / bahkan lebih cepat pada kolom utf8 atau utf8mb4 seperti yang ditunjukkan pada jawaban di atas

staabm
sumber