Skip to main content
  1. Posts/

Cara Menggunakan Indexes di MySQL

·9 mins·
mysql mysql
Table of Contents

Untuk meningkatkan kinerja operasi SELECT atau mempercepat hasil query, Anda bisa menambahkan index pada satu atau lebih kolom yang digunakan dalam query. Berikut adalah beberapa jenis index pada MySQL yang akan dibahas beserta implementasinya.

Saya asumsikan Anda telah menginstall MySQL di server. Lalu Anda bisa menggunakan database Anda sendiri atau download sample database di GitHub - datacharmer/test_db

Primary Index
#

Primary keys adalah jenis index yang menyimpan actual row data secara bersamaan dengan key-nya (disimpan pada leaf).

Contoh sederhana dari penggunaan primary key adalah jika Anda membuat schema tabel dengan awalan kolom seperti id kemudian menambahkan AUTO_INCREMENT dan Primary key, maka pengambilan data menggunakan WHERE id tersebut akan dieksekusi dengan sangat cepat. Ini dikarenakan MySQL tidak perlu melakukan scan keseluruhan data dan hanya mengambil dari id yang diinginkan. Ditambah dengan penggunaan AUTO_INCREMENT yang membuat data menjadi terurut.

Untuk implementasinya saya menggunakan tabel employees yang berisi 300024 row dengan schema berikut.

mysql> desc employees;
+------------+---------------+------+-----+---------+-------+
| Field      | Type          | Null | Key | Default | Extra |
+------------+---------------+------+-----+---------+-------+
| emp_no     | int           | NO   | PRI | NULL    |       |
| birth_date | date          | NO   |     | NULL    |       |
| first_name | varchar(14)   | NO   |     | NULL    |       |
| last_name  | varchar(16)   | NO   |     | NULL    |       |
| gender     | enum('M','F') | NO   |     | NULL    |       |
| hire_date  | date          | NO   |     | NULL    |       |
+------------+---------------+------+-----+---------+-------+

Jika melakukan query menggunakan emp_no sebagai where clause, maka hasil dari query tersebut bisa lebih cepat dikarenakan MySQL tidak perlu melakukan scan pada 300 ribu baris melainkan hanya mengambil dari emp_no yang diinginkan. Perhatikan pada bagian key dan rows.

mysql> EXPLAIN SELECT * FROM employees WHERE emp_no=15000;
+----+-------------+-----------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table     | partitions | type  | possible_keys | key     | key_len | ref   | rows | filtered | Extra |
+----+-------------+-----------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | employees | NULL       | const | PRIMARY       | PRIMARY | 4       | const |    1 |   100.00 | NULL  |
+----+-------------+-----------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

Waktu yang dibutuhan saat menjalankan query sekitar 0.0006 seconds

mysql> SELECT * FROM employees WHERE emp_no=15000;
+--------+------------+------------+-----------+--------+------------+
| emp_no | birth_date | first_name | last_name | gender | hire_date  |
+--------+------------+------------+-----------+--------+------------+
|  15000 | 1959-11-29 | Thanasis   | Bahi      | F      | 1988-03-27 |
+--------+------------+------------+-----------+--------+------------+
1 row in set (0.00 sec)

Secondary Index
#

Berbeda dengan primary index, secondary index tidak menyimpan row, tapi menyimpan primary key pada leaf dan menggunakan primary key sebagai locator data. Jadi ini akan menduplikasi primary key pada setiap secondary index sehingga tidak disarankan apabila primary key adalah varchar yang menyimpan cukup banyak data per row.

Biasanya jenis index ini digunakan karena adanya kebutuhan melakukan query secara terus menerus/cukup sering yang menggunakan spesifik column sebagai filternya.

Untuk implementasinya masih menggunakan tabel employees dengan struktur seperti berikut.

+------------+---------------+------+-----+---------+-------+
| Field      | Type          | Null | Key | Default | Extra |
+------------+---------------+------+-----+---------+-------+
| emp_no     | int           | NO   | PRI | NULL    |       |
| birth_date | date          | NO   |     | NULL    |       |
| first_name | varchar(14)   | NO   |     | NULL    |       |
| last_name  | varchar(16)   | NO   |     | NULL    |       |
| gender     | enum('M','F') | NO   |     | NULL    |       |
| hire_date  | date          | NO   |     | NULL    |       |
+------------+---------------+------+-----+---------+-------+

Misalnya untuk menjalankan query menggunakan column hire_date sebagai filternya.

mysql> SELECT * FROM employees WHERE hire_date='1991-05-26';
+--------+------------+------------+-------------+--------+------------+
| emp_no | birth_date | first_name | last_name   | gender | hire_date  |
+--------+------------+------------+-------------+--------+------------+
|  20243 | 1953-07-19 | Eberhardt  | Danner      | F      | 1991-05-26 |
|  20555 | 1963-08-04 | Christophe | Valette     | F      | 1991-05-26 |
|  21084 | 1952-06-19 | Srinidhi   | Belinskaya  | M      | 1991-05-26 |
|  24726 | 1957-05-09 | Rance      | Blokdijk    | M      | 1991-05-26 |
|  27836 | 1957-08-02 | Werner     | Thiria      | M      | 1991-05-26 |
|  45191 | 1964-08-22 | Kazunori   | Rothenberg  | M      | 1991-05-26 |
|  47591 | 1956-01-05 | Rildo      | Cummings    | M      | 1991-05-26 |
...
65 rows in set (0.30 sec)

Bila dicoba explain query, MySQL akan scan keseluruhan rows yang ada di tabel untuk menemukan data yang diinginkan hire_date=1991-05-26

mysql> EXPLAIN SELECT * FROM employees WHERE hire_date='1991-05-26';
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-------------+
| id | select_type | table     | partitions | type | possible_keys | key  | key_len | ref  | rows   | filtered | Extra       |
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-------------+
|  1 | SIMPLE      | employees | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 299600 |    10.00 | Using where |
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

Karena query tersebut akan selalu digunakan pada aplikasi atau query secara manual, maka perlu ditambahkan index pada column hire_date untuk mempercepat query.

ALTER TABLE employees ADD INDEX `idx_hire_date` (`hire_date`)

Untuk melihat penggunaan space INDEX gunakan perintah.

SELECT database_name, table_name, index_name, ROUND(stat_value * @@innodb_page_size / 1024 / 1024, 2) size_in_mb
FROM mysql.innodb_index_stats
WHERE stat_name='size' 
AND index_name !='PRIMARY' 
AND table_name="employees";
+---------------+------------+---------------+------------+
| database_name | table_name | index_name    | size_in_mb |
+---------------+------------+---------------+------------+
| employees     | employees  | idx_hire_date |       4.52 |
+---------------+------------+---------------+------------+
1 row in set (0.00 sec)

Setelah index ditambahkan, ketika explain query kembali dapat dilihat pada jumlah rows sudah berbeda menandakan MySQL hanya melakukan scan pada 65 rows saja dan tidak melakukan scan pada keseluruhan data tabel.

mysql> EXPLAIN SELECT * FROM employees WHERE hire_date='1991-05-26';
+----+-------------+-----------+------------+------+---------------+---------------+---------+-------+------+----------+-------+
| id | select_type | table     | partitions | type | possible_keys | key           | key_len | ref   | rows | filtered | Extra |
+----+-------------+-----------+------------+------+---------------+---------------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | employees | NULL       | ref  | idx_hire_date | idx_hire_date | 3       | const |   65 |   100.00 | NULL  |
+----+-------------+-----------+------------+------+---------------+---------------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

Waktu yang diperlukan untuk menjalankan query menjadi 0.0008 seconds

mysql> SELECT * FROM employees WHERE hire_date='1991-05-26';
+--------+------------+------------+-------------+--------+------------+
| emp_no | birth_date | first_name | last_name   | gender | hire_date  |
+--------+------------+------------+-------------+--------+------------+
|  20243 | 1953-07-19 | Eberhardt  | Danner      | F      | 1991-05-26 |
|  20555 | 1963-08-04 | Christophe | Valette     | F      | 1991-05-26 |
|  21084 | 1952-06-19 | Srinidhi   | Belinskaya  | M      | 1991-05-26 |
|  24726 | 1957-05-09 | Rance      | Blokdijk    | M      | 1991-05-26 |
|  27836 | 1957-08-02 | Werner     | Thiria      | M      | 1991-05-26 |
|  45191 | 1964-08-22 | Kazunori   | Rothenberg  | M      | 1991-05-26 |
|  47591 | 1956-01-05 | Rildo      | Cummings    | M      | 1991-05-26 |
...
65 rows in set (0.00 sec)

Composite Index
#

Jenis index ini memungkinkan penambahan beberapa column sekaligus sebagai key (up to 16 keys) sehingga sering disebut sebagai Multi-column/Composite/Compound index.

Pada composite index, index key akan di construct berdasarkan gabungan dari column yang digunakan pada tabel tersebut, Misal index yang dibentu dari gabungan (c1,c2,c3) akan membentuk key c1c2c3. Hal inilah yang menyebabkan pencarian menggunakan index ini harus selalu mengikut sertakan prefix urutan awal/sebelumnya dalam where clause nya.

Contoh jika melakukan pencarian pada index(c1,c2) akan menjadi seperti berikut:

  • where col1 = “a” -> using index
  • where col2 = “b” -> not using index
  • where col1 = “a” and col2 = “b” -> using index

Untuk implementasinya masih menggunakan tabel employees dengan struktur seperti berikut.

+------------+---------------+------+-----+---------+-------+
| Field      | Type          | Null | Key | Default | Extra |
+------------+---------------+------+-----+---------+-------+
| emp_no     | int           | NO   | PRI | NULL    |       |
| birth_date | date          | NO   |     | NULL    |       |
| first_name | varchar(14)   | NO   |     | NULL    |       |
| last_name  | varchar(16)   | NO   |     | NULL    |       |
| gender     | enum('M','F') | NO   |     | NULL    |       |
| hire_date  | date          | NO   |     | NULL    |       |
+------------+---------------+------+-----+---------+-------+

Buat query untuk memfilter berdasarkan column last_name dan gender.

mysql> EXPLAIN SELECT * FROM `employees` WHERE last_name='Aamodt' AND gender='F';
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-------------+
| id | select_type | table     | partitions | type | possible_keys | key  | key_len | ref  | rows   | filtered | Extra       |
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-------------+
|  1 | SIMPLE      | employees | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 299202 |     5.00 | Using where |
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

Dapat diketahui jika pencarian menggunakan column last_name dan gender akan membuat MySQL melakukan scan keseluruhan rows yang ada di tabel.

Kemudian buat Composite Index untuk mempercepat query.

ALTER TABLE employees ADD INDEX `idx_lastname_gender` (last_name,gender);

Jalankan query sebelumnya.

mysql> EXPLAIN SELECT * FROM `employees` WHERE last_name='Aamodt' AND gender='F';
+----+-------------+-----------+------------+------+---------------------+---------------------+---------+-------------+------+----------+-----------------------+
| id | select_type | table     | partitions | type | possible_keys       | key                 | key_len | ref         | rows | filtered | Extra                 |
+----+-------------+-----------+------------+------+---------------------+---------------------+---------+-------------+------+----------+-----------------------+
|  1 | SIMPLE      | employees | NULL       | ref  | idx_lastname_gender | idx_lastname_gender | 67      | const,const |   85 |   100.00 | Using index condition |
+----+-------------+-----------+------------+------+---------------------+---------------------+---------+-------------+------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)

Jumlah rows menjadi lebih sedikit menandakan Index bekerja sehingga memperkecil scope data yang perlu dicari MySQL.

Sesuai cara kerja composite index yang telah dijelaskan, apabila Anda mencoba filter menggunakan last_name sebegai where clause maka Index akan tetap bekerja.

mysql> EXPLAIN SELECT * FROM `employees` WHERE last_name='Aamodt';
+----+-------------+-----------+------------+------+---------------------+---------------------+---------+-------+------+----------+-------+
| id | select_type | table     | partitions | type | possible_keys       | key                 | key_len | ref   | rows | filtered | Extra |
+----+-------------+-----------+------------+------+---------------------+---------------------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | employees | NULL       | ref  | idx_lastname_gender | idx_lastname_gender | 66      | const |  205 |   100.00 | NULL  |
+----+-------------+-----------+------------+------+---------------------+---------------------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

Sedangkan jika menggunakan gender sebagai awalan where clause maka Index tidak akan bekerja yang menyebabkan MySQL perlu scan keseluruhan rows.

mysql> EXPLAIN SELECT * FROM `employees` WHERE gender='F';
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-------------+
| id | select_type | table     | partitions | type | possible_keys | key  | key_len | ref  | rows   | filtered | Extra       |
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-------------+
|  1 | SIMPLE      | employees | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 299202 |    50.00 | Using where |
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

UNIQUE Key Index
#

Unique key index cukup berbeda dengan index yang lain. Index jenis ini digunakan untuk menjaga data integrity pada suatu tabel, dengan cara memastikan tidak ada data pada sebuah tabel yang dupicate entry.

Biasanya dengan menambahkan Primary Key pada column, maka Unique akan secara default aktif, Namun Unique key dapat ditambahkan juga di non primary key column.

Untuk implementasinya saya membuat tabel user dengan struktur seperti berikut.

+-------+--------------+------+-----+---------+----------------+
| Field | Type         | Null | Key | Default | Extra          |
+-------+--------------+------+-----+---------+----------------+
| id    | int          | NO   | PRI | NULL    | auto_increment |
| user  | varchar(64)  | NO   |     | NULL    |                |
| name  | varchar(128) | NO   |     | NULL    |                |
| email | varchar(128) | NO   |     | NULL    |                |
+-------+--------------+------+-----+---------+----------------+

Lalu Anda ingin user tidak dapat mendaftar 2x dengan akun email yang sama sehingga perlu ditambahkan Unique key.

ALTER TABLE `user` ADD UNIQUE `uniq_email` (`email`);

Kemudian test insert data dengan akun email yang sama beberapa kali.

mysql> INSERT INTO `user` VALUES (NULL, 'jonh', 'jonh', '[email protected]');
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO `user` VALUES (NULL, 'jonh', 'jonh', '[email protected]');
ERROR 1062 (23000): Duplicate entry '[email protected]' for key 'user.uniq_email'
mysql> INSERT INTO `user` VALUES (NULL, 'jonh', 'jonh', '[email protected]');
ERROR 1062 (23000): Duplicate entry '[email protected]' for key 'user.uniq_email'

Source
#

Related

Setting Up InnoDB Memcached Plugin in MySQL
·3 mins
mysql mysql
Speed Up Query Joins MariaDB
·1 min
mysql mysql
Optimizing MySQL Using Query Cache
·4 mins
mysql mysql
Cara Export dan Import User MySQL
·1 min
mysql mysql
ERROR 1273 (HY000): Unknown collation Saat Import DB
·1 min
mysql mysql
Export Baris Tertentu dari Tabel MySQL
·1 min
mysql mysql