Kemungkinan INDEX pada bidang VARCHAR di MySql

40

Saya bekerja di database MySql , dengan tabel seperti ini:

+--------------+
|  table_name  |
+--------------+
|    myField   |
+--------------+

... dan saya perlu membuat banyak pertanyaan seperti ini (dengan 5-10 string dalam daftar) :

SELECT myField FROM table_name
WHERE myField IN ('something', 'other stuff', 'some other a bit longer'...)

Akan ada sekitar 24.000.000 baris unik

1) Haruskah saya menggunakan kunci a FULLTEXTdan dan INDEXuntuk saya VARCHAR(150)?
2) Jika saya menambah karakter dari 150 menjadi 220 atau 250 ... apakah itu akan membuat perbedaan besar? (Apakah ada cara untuk menghitungnya?)
3) Seperti yang saya katakan, mereka akan menjadi unik, jadi myField harus menjadi KUNCI UTAMA . Bukankah jarang menambahkan PRIMARY KEY ke bidang yang sudah menjadi VARCHAR INDEX / FULLTEXT?

Menara Markus
sumber
Anda tidak perlu menggunakan PRIMARY untuk keunikan. Sudah ada UNIK untuk itu.
kommradHomer

Jawaban:

62

SARAN # 1: Pengindeksan Standar

CREATE TABLE mytable
(
    id int not null auto_increment,
    myfield varchar(255) not null,
    primary key (id),
    key (myfield)
);

Jika Anda mengindeks seperti ini, Anda dapat mencari seluruh string atau melakukan pencarian SEPERTI berorientasi kiri

SARAN # 2: Pengindeksan FULLTEXT

CREATE TABLE mytable
(
    id int not null auto_increment,
    myfield varchar(255) not null,
    primary key (id),
    fulltext (myfield)
);

Anda dapat secara efektif menggunakan pencarian untuk setiap kata kunci dan juga seluruh frasa. Anda perlu menentukan daftar kata kunci khusus karena MySQL tidak akan mengindeks 543 kata .

Berikut ini adalah posting saya yang lain dari dua tahun terakhir pada indeks FULLTEXT

SARAN # 3: Pengindeksan Hash

CREATE TABLE mytable
(
    id int not null auto_increment,
    myfield varchar(255) not null,
    hashmyfield char(32) not null,
    primary key (id),
    key (hashmyfield)
);

Jika Anda mencari satu nilai spesifik dan nilai-nilai itu bisa melebihi 32 karakter, Anda bisa menyimpan nilai hash:

INSERT INTO mytable (myfield,hashmyfield)
VALUES ('whatever',MD5('whatever'));

Dengan begitu, Anda hanya mencari nilai hash untuk mengambil hasil

SELECT * FROM mytable WHERE hashmyfield = MD5('whatever');

Cobalah !!!

RolandoMySQLDBA
sumber
Saya tidak memiliki reputasi yang cukup untuk memilih jawaban Anda tetapi saya harus mengatakan itu HEBAT. Terima kasih atas penjelasan dan contohnya. Saya pikir pengindeksan hash adalah yang terbaik untuk kasus saya, ini adalah solusi yang luar biasa. Tetapi masih ada satu pertanyaan: menurut Anda apa batas dari baris untuk pencarian cepat di tabel? [menggunakan KUNCI VARCHAR (32) untuk pencarian]
Mark Tower
2
Opsi hash di sini masih berupa teks dan 32 byte untuk apa yang sebenarnya 16 byte. Anda dapat menggunakan bidang bigint dengan conv (kiri (md5 ('apa pun'), 16), 16, -10). Tidak ada numerik 16 byte tetapi Anda mungkin menemukan setengah dari md5 cukup dan kemudian hanya 8 byte dalam indeks
atxdba
1
Tidak baik menggunakan MD5 atau SHA1 untuk menghasilkan string yang akan diindeks. Distribusi string yang dihasilkan oleh fungsi hashing seperti MD5 atau SHA1 adalah acak dalam ruang besar yang mengurangi efisiensi indeks Anda, yang dapat memperlambat pernyataan INSERT dan SELECT. Berikut ini adalah pos yang menjelaskannya: code-epicenter.com/...
Mr.M
Saya minta maaf karena ini adalah utas lama, tetapi pertanyaan saya terkait langsung dengan ini tetapi saya tidak bisa mendapatkan jawaban yang jelas untuk kebutuhan saya dengan membaca di atas dan artikel serupa lainnya. Skenario saya adalah: Saya sedang mengembangkan sistem persediaan yang sangat sederhana yang hanya terdiri dari satu meja untuk saat ini. Itu diakses secara eksternal melalui API sehingga semua konfigurasi dilakukan di tempat lain - yang merupakan alasan mengapa kita hanya perlu satu tabel. Dua kolom yang saya pikirkan tentang pengindeksan, masing-masing akan memiliki sekitar 200 entri unik, dengan panjang <20 karakter. Haruskah saya mempertimbangkan untuk menambahkan indeks?
Mike
Apakah pencarian seperti ini berorientasi kiri like 'a%'?
Akuntan
18

MySQL memungkinkan Anda untuk menentukan indeks awalan yang berarti Anda menentukan karakter N pertama dari string asli untuk diindeks, dan triknya adalah memilih nomor N yang cukup panjang untuk memberikan selektivitas yang baik, tetapi cukup pendek untuk menghemat ruang. Awalan harus cukup panjang untuk membuat indeks hampir berguna jika Anda akan mengindeks seluruh kolom.

Sebelum kita melangkah lebih jauh, mari kita mendefinisikan beberapa istilah penting. Selektivitas indeks adalah rasio total nilai indeks yang berbeda dan jumlah baris . Berikut adalah satu contoh untuk tabel tes:

+-----+-----------+
| id  | value     |
+-----+-----------+
| 1   | abc       |
| 2   | abd       |
| 3   | adg       |
+-----+-----------+

Jika kita hanya mengindeks karakter pertama (N = 1), maka tabel indeks akan terlihat seperti tabel berikut:

+---------------+-----------+
| indexedValue  | rows      |
+---------------+-----------+
| a             | 1,2,3     |
+---------------+-----------+

Dalam hal ini, selektivitas indeks sama dengan IS = 1/3 = 0,33.

Sekarang mari kita lihat apa yang akan terjadi jika kita menambah jumlah karakter yang diindeks menjadi dua (N = 2).

+---------------+-----------+
| indexedValue  | rows      |
+---------------+-----------+
| ab             | 1,2      |
| ad             | 3        |
+---------------+-----------+

Dalam skenario ini IS = 2/3 = 0,66 yang berarti kami meningkatkan selektivitas indeks, tetapi kami juga meningkatkan ukuran indeks. Triknya adalah mencari angka minimal N yang akan menghasilkan selektivitas indeks maksimal .

Ada dua pendekatan yang dapat Anda lakukan perhitungan untuk tabel database Anda. Saya akan melakukan demonstrasi pada dump database ini .

Katakanlah kita ingin menambahkan kolom last_name di tabel karyawan ke indeks, dan kami ingin mendefinisikan angka terkecil N yang akan menghasilkan selektivitas indeks terbaik.

Pertama mari kita mengidentifikasi nama belakang yang paling sering:

select count(*) as cnt, last_name from employees group by employees.last_name order by cnt

+-----+-------------+
| cnt | last_name   |
+-----+-------------+
| 226 | Baba        |
| 223 | Coorg       |
| 223 | Gelosh      |
| 222 | Farris      |
| 222 | Sudbeck     |
| 221 | Adachi      |
| 220 | Osgood      |
| 218 | Neiman      |
| 218 | Mandell     |
| 218 | Masada      |
| 217 | Boudaillier |
| 217 | Wendorf     |
| 216 | Pettis      |
| 216 | Solares     |
| 216 | Mahnke      |
+-----+-------------+
15 rows in set (0.64 sec)

Seperti yang Anda lihat, nama belakang Baba adalah yang paling sering. Sekarang kita akan menemukan awalan last_name paling sering terjadi , dimulai dengan lima huruf awalan.

+-----+--------+
| cnt | prefix |
+-----+--------+
| 794 | Schaa  |
| 758 | Mande  |
| 711 | Schwa  |
| 562 | Angel  |
| 561 | Gecse  |
| 555 | Delgr  |
| 550 | Berna  |
| 547 | Peter  |
| 543 | Cappe  |
| 539 | Stran  |
| 534 | Canna  |
| 485 | Georg  |
| 417 | Neima  |
| 398 | Petti  |
| 398 | Duclo  |
+-----+--------+
15 rows in set (0.55 sec)

Ada lebih banyak kejadian dari setiap awalan, yang berarti kita harus meningkatkan angka N hingga nilainya hampir sama seperti pada contoh sebelumnya.

Ini adalah hasil untuk N = 9

select count(*) as cnt, left(last_name,9) as prefix from employees group by prefix order by cnt desc limit 0,15;

+-----+-----------+
| cnt | prefix    |
+-----+-----------+
| 336 | Schwartzb |
| 226 | Baba      |
| 223 | Coorg     |
| 223 | Gelosh    |
| 222 | Sudbeck   |
| 222 | Farris    |
| 221 | Adachi    |
| 220 | Osgood    |
| 218 | Mandell   |
| 218 | Neiman    |
| 218 | Masada    |
| 217 | Wendorf   |
| 217 | Boudailli |
| 216 | Cummings  |
| 216 | Pettis    |
+-----+-----------+

Ini adalah hasil untuk N = 10.

+-----+------------+
| cnt | prefix     |
+-----+------------+
| 226 | Baba       |
| 223 | Coorg      |
| 223 | Gelosh     |
| 222 | Sudbeck    |
| 222 | Farris     |
| 221 | Adachi     |
| 220 | Osgood     |
| 218 | Mandell    |
| 218 | Neiman     |
| 218 | Masada     |
| 217 | Wendorf    |
| 217 | Boudaillie |
| 216 | Cummings   |
| 216 | Pettis     |
| 216 | Solares    |
+-----+------------+
15 rows in set (0.56 sec)

Ini hasil yang sangat bagus. Ini berarti bahwa kita dapat membuat indeks pada kolom last_name dengan mengindeks hanya 10 karakter pertama. Dalam tabel definisi, last_name didefinisikan sebagai VARCHAR(16), dan ini berarti kami telah menyimpan 6 byte (atau lebih jika ada karakter UTF8 dalam nama belakang) per entri. Dalam tabel ini ada 1637 nilai berbeda dikalikan dengan 6 byte sekitar 9KB, dan bayangkan bagaimana angka ini akan tumbuh jika tabel kita berisi jutaan baris.

Anda dapat membaca cara-cara lain untuk menghitung jumlah N dalam indeks Prefixed posting saya di MySQL .

Menggunakan fungsi MD5 dan SHA1 untuk menghasilkan nilai yang harus diindeks juga bukan pendekatan yang baik . Mengapa? Baca di pos Bagaimana memilih tipe data yang tepat untuk kunci utama dalam database MySQL

Mr.M
sumber
Ini adalah jawaban yang sangat jelas untuk pertanyaan yang berbeda.
mustaccio
1
Apakah kamu bercanda?
Mr.M
Bisakah Anda menjelaskan apa yang salah, atau apa yang tidak dapat diterapkan pada pertanyaan?
Mr.M
2
Hai Tuan. Saya sebenarnya suka jawaban Anda. Mengapa Dalam jawaban lama saya, saya berkata dalam SARAN # 1: If you index like this, you can either look for the whole string or do left-oriented LIKE searches. Saya juga mengatakan dalam SARAN # 3: If you are looking for one specific value and those values could be lengths well beyond 32 characters, you could store the hash value:. Jawaban Anda secara memadai menunjukkan mengapa seseorang tidak harus menggunakan kunci besar dan harus mengindeks pada karakter paling kiri, yang dapat membuat perbedaan dalam kinerja. Jawaban Anda ada di sini. +1 untuk jawaban Anda dan Selamat datang di DBA StackExchange.
RolandoMySQLDBA