Tại sao MySQL không có chỉ số băm trên MyISAM hoặc InnoDB?


35

Tôi có một ứng dụng sẽ chỉ chọn trên đẳng thức và tôi nghĩ rằng tôi nên sử dụng chỉ số băm trên chỉ mục btree. Điều này làm tôi mất tinh thần, các chỉ số băm không được hỗ trợ trên MyISAM hoặc InnoDB. Có chuyện gì thế?


2
Mysql cũng không hỗ trợ các chỉ mục dựa trên chức năng, chỉ mục bitmap, v.v. Chỉ vì nó là mysql ;-)

1
tôi chỉ hình dung rằng các chỉ mục băm rất ... cơ bản ... tôi cho rằng có lý do liên quan đến việc triển khai cụ thể.

1
@Alex: Tôi cá rằng lý do đó là "sự lười biếng" và "quan liêu" nhưng hãy chờ câu trả lời))


Tôi đã thêm một thuật toán HASH đẹp từ Sách MySQL hiệu suất cao vào cuối câu trả lời của tôi.
RolandoMySQLDBA

Câu trả lời:


16

Nhiều cơ sở dữ liệu không hỗ trợ chỉ số dựa băm ở tất cả .

Để bảng băm hoạt động hiệu quả, bạn cần biết số lượng hàng có khả năng xuất hiện nếu không bảng băm cơ sở sẽ quá lớn (nhiều mục trống, lãng phí không gian và có khả năng IO đĩa) hoặc quá nhỏ Sự gián tiếp thường được sử dụng (có thể là nhiều cấp độ gián tiếp, hoặc thậm chí tệ hơn nếu triển khai hàm băm là một cấp độ mà bạn có thể thực hiện tìm kiếm tuyến tính trên một số lượng hồ sơ hợp lý) tại đó mọi thứ có thể không hiệu quả hơn sau đó dựa trên cây chỉ số nào.

Vì vậy, nói chung là hữu ích (thường là tốt hơn so với giải pháp thay thế), chỉ số thỉnh thoảng cần được xây dựng lại khi dữ liệu tăng (và thu hẹp) có thể thêm một chi phí không liên tục đáng kể. Điều này thường ổn với các bảng dựa trên bộ nhớ vì việc xây dựng lại có thể sẽ khá nhanh (vì dữ liệu luôn nằm trong RAM và không có khả năng lớn trong mọi trường hợp), nhưng việc xây dựng lại một chỉ mục lớn trên đĩa là một Hoạt động rất nặng (và IIRC myQuery không hỗ trợ xây dựng lại chỉ mục trực tiếp để giữ khóa bảng trong quá trình hoạt động).

Do đó, các chỉ mục băm được sử dụng trong các bảng bộ nhớ vì chúng thường có hiệu suất tốt hơn, nhưng các bảng dựa trên đĩa không hỗ trợ chúng vì chúng có thể gây bất lợi cho hiệu suất không phải là phần thưởng. Không có gì để chỉ số băm dừng được làm sẵn có cho các bảng dựa đĩa dĩ nhiên là, không có nghi ngờ một số cơ sở dữ liệu làm hỗ trợ tính năng này, nhưng có lẽ họ không được thực hiện trong ISAM / bảng InnoDB như các nhà bảo trì không xem xét các giá trị tính năng bổ sung (tùy theo từng mã bổ sung để viết và duy trì không có giá trị lợi ích trong một vài trường hợp mà nó tạo ra sự khác biệt đáng kể). Có lẽ nếu bạn hoàn toàn không đồng ý, bạn có thể nói chuyện với họ và đưa ra một trường hợp tốt để thực hiện tính năng này.

Nếu bạn đang lập chỉ mục các chuỗi lớn thì việc triển khai chỉ mục băm giả của riêng bạn (bằng cách lưu trữ giá trị băm của giá trị cũng như giá trị thực và lập chỉ mục có cột) có thể hoạt động, nhưng điều này chỉ hiệu quả hơn đối với các chuỗi lớn (trong đó tính toán giá trị băm và tìm kiếm chỉ mục cây theo giá trị này luôn có khả năng nhanh hơn sau đó chỉ cần tìm kiếm một chỉ mục cây bằng cách sử dụng các giá trị lớn hơn để so sánh và lưu trữ bổ sung được sử dụng sẽ không đáng kể) vì vậy hãy phân tích hiệu suất trước khi thực hiện Điều này trong sản xuất.


Có cách nào để cho phép băm lại (xây dựng lại) được thực hiện song song mà không khóa toàn bộ bảng không?
Pacerier

@Pacerier: không phải tôi biết về MySQL (mặc dù họ có thể đã thêm tính năng này kể từ lần cuối tôi sử dụng nó, vì vậy hãy kiểm tra tài liệu). Ngay cả khi DBMS hỗ trợ tạo / xây dựng lại chỉ mục trực tuyến, nó không phải là tùy chọn mặc định. Những gì bị khóa sẽ khác nhau: một số sẽ giữ khóa ghi trên bàn để các giao dịch khác không bị trì hoãn nếu chúng chỉ đọc, một số DMBS sẽ lấy ra một khóa bảng đầy đủ. Nếu bạn cần xây dựng lại trực tuyến, hãy kiểm tra tài liệu từng DBMS trước khi chọn sử dụng.
David Spillett

Thông thường việc xây dựng lại chỉ cần thiết khi chiều dài dữ liệu được nhân đôi. Họ có thực sự phải lo lắng về việc độ dài dữ liệu sẽ tăng gấp đôi mỗi phút không? (thông thường điều này rất hiếm khi xảy ra khi cơ sở dữ liệu phát triển đủ lớn để điều này trở thành mối quan tâm)
SOFe

6

Trên một lưu ý liên quan, bạn có thể thấy cuộc thảo luận về các loại chỉ mục từ các tài liệu PostgreSQL thú vị. Nó không còn xuất hiện trong các phiên bản gần đây của tài liệu (do tối ưu hóa tiếp theo, tôi lấy nó), nhưng việc mua lại có thể giống với MySQL (và lý do tại sao các chỉ mục băm chỉ được sử dụng cho các bảng heap):

http://www.postgresql.org/docs/8.1/static/indexes-types.html

Lưu ý: Kiểm tra đã cho thấy các chỉ mục băm của PostgreSQL hoạt động không tốt hơn các chỉ mục B-tree và kích thước chỉ mục và thời gian xây dựng cho các chỉ mục băm tồi tệ hơn nhiều. Hơn nữa, các hoạt động chỉ mục băm hiện không được ghi nhật ký WAL, vì vậy các chỉ mục băm có thể cần phải được xây dựng lại với REINDEX sau khi sự cố cơ sở dữ liệu. Vì những lý do này, việc sử dụng chỉ số băm hiện không được khuyến khích. Tương tự, các chỉ mục của cây R dường như không có bất kỳ lợi thế về hiệu suất so với các hoạt động tương đương của các chỉ mục GiST. Giống như các chỉ mục băm, chúng không được ghi nhật ký WAL và có thể cần reindexing sau khi sự cố cơ sở dữ liệu. Mặc dù các vấn đề với chỉ mục băm cuối cùng có thể được khắc phục, có khả năng loại chỉ mục cây R sẽ bị loại bỏ trong phiên bản tương lai. Người dùng được khuyến khích di chuyển các ứng dụng sử dụng chỉ mục R-tree sang chỉ mục GiST.

Một lần nữa, nó (phiên bản lỗi thời) cụ thể của PostgreSQL, nhưng cần gợi ý rằng loại chỉ mục "tự nhiên" sẽ không nhất thiết mang lại hiệu suất tối ưu.


5

Đây là một điều thú vị:

Theo cuốn sách Hướng dẫn nghiên cứu chứng chỉ MySQL 5.0 , Trang 433, Mục 29.5.1

Công cụ MEMORY sử dụng HASH theo thuật toán lập chỉ mục mặc định.

Để cười, tôi đã cố gắng tạo bảng InnoDB và bảng MyISAM với khóa chính bằng HASH trong MySQL 5.5.12

mysql> use test
Database changed
mysql> create table rolando (num int not null, primary key (num) using hash);
Query OK, 0 rows affected (0.11 sec)

mysql> show create table rolando\G
*************************** 1. row ***************************
       Table: rolando
Create Table: CREATE TABLE `rolando` (
  `num` int(11) NOT NULL,
  PRIMARY KEY (`num`) USING HASH
) ENGINE=InnoDB DEFAULT CHARSET=latin1
1 row in set (0.00 sec)

mysql> create table rolando2 (num int not null, primary key (num) using hash) engine=MyISAM;
Query OK, 0 rows affected (0.05 sec)

mysql> show create table rolando2\G
*************************** 1. row ***************************
       Table: rolando2
Create Table: CREATE TABLE `rolando2` (
  `num` int(11) NOT NULL,
  PRIMARY KEY (`num`) USING HASH
) ENGINE=MyISAM DEFAULT CHARSET=latin1
1 row in set (0.00 sec)

MySQL đã không phàn nàn.

CẬP NHẬT

Tin xấu !!! Tôi đã sử dụng CHỈ SỐ SHOW TỪ. Nó nói chỉ số là BTREE.

Các CREATE INDEX cú pháp MySQL Trang khẳng định rằng chỉ có bộ nhớ và công cụ lưu trữ NDB có thể thích ứng với INDEX HASH.

mysql> show indexes from rolando;
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table   | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| rolando |          0 | PRIMARY  |            1 | num         | A         |           0 |     NULL | NULL   |      | BTREE      |         |               |
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
1 row in set (0.00 sec)

mysql> show indexes from rolando2;
+----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table    | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| rolando2 |          0 | PRIMARY  |            1 | num         | A         |           0 |     NULL | NULL   |      | BTREE      |         |               |
+----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
1 row in set (0.00 sec)

mysql> create table rolando3 (num int not null, primary key (num)) ENGINE=MEMORY;
Query OK, 0 rows affected (0.03 sec)

mysql> show create table rolando3\G
*************************** 1. row ***************************
       Table: rolando3
Create Table: CREATE TABLE `rolando3` (
  `num` int(11) NOT NULL,
  PRIMARY KEY (`num`)
) ENGINE=MEMORY DEFAULT CHARSET=latin1
1 row in set (0.00 sec)

mysql> show indexes from rolando3;
+----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table    | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| rolando3 |          0 | PRIMARY  |            1 | num         | NULL      |           0 |     NULL | NULL   |      | HASH       |         |               |
+----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
1 row in set (0.00 sec)

Một số người đề xuất theo ý tưởng trong Trang 102-105 của cuốn sách " MySQL hiệu suất cao: Tối ưu hóa, sao lưu, sao chép và hơn thế nữa " để mô phỏng thuật toán băm.

Trang 105 có thuật toán nhanh và bẩn này mà tôi thích:

SELECT CONV(RIGHT(MD5('whatever value you want'),16),16,10) AS HASH64;

Tạo một cột cho điều này trong bất kỳ bảng nào và lập chỉ mục giá trị này.

Hãy thử một lần !!!


5
Trước khi sử dụng kỹ thuật giả chỉ số băm trong sản xuất, hãy thực hiện một số phân tích hiệu suất trên nó. Đối với các chuỗi lớn, nó có thể tạo ra sự khác biệt lớn nhưng cuối cùng bạn vẫn điều hướng một chỉ mục cây bạn có thêm so sánh để tìm đúng hàng từ các chuỗi được tìm thấy khớp với hàm băm, vì vậy đối với các giá trị nhỏ tính toán các giá trị băm và lưu trữ chúng chỉ là không đáng Đây thực sự không phải là một chỉ số băm, bạn chỉ đơn giản là giảm công việc thực hiện khi đi trên cây (vì mỗi so sánh đang xem xét ít byte hơn, ví dụ so sánh 8 byte INT thay vì chuỗi x00 byte).
David Spillett

@David Spillett Trong việc này, tôi hoàn toàn phải đồng ý với bạn. Các chiến lược lập chỉ mục khác cũng được đề xuất trong cùng một cuốn sách trong Chương 11 "Chiến lược lập chỉ mục cho hiệu suất cao". Như một sự thúc đẩy bổ sung cho câu trả lời của tôi, cuốn sách thực sự đề cập đến việc sử dụng một chỉ mục được nhóm, trong đó lưu trữ hàng và Chỉ số BTree trong cùng một cấu trúc. Điều này có thể được tăng tốc công việc giảm mà bạn đề cập. Thật không may, các vòng bạn phải nhảy qua mà bạn vừa đề cập là hơi khó tránh khỏi. Tuy nhiên, +1 từ tôi về nhận xét của bạn, thưa ông !!! Trong thực tế, +1 cho câu trả lời của bạn là tốt.
RolandoMySQLDBA

@RolandoMySQLDBA Bạn có thể nói rõ hơn về phần "băm tùy chỉnh" không, đoạn cuối dường như không đưa ra nhiều manh mối ...
Pacerier

2

BTree không chậm hơn nhiều so với Hash cho tra cứu một hàng. Vì BTree cung cấp các truy vấn phạm vi rất hiệu quả, tại sao phải bận tâm với bất cứ điều gì khác ngoài BTree.

MySQL thực hiện rất tốt việc lưu trữ các khối BTree, do đó, một truy vấn dựa trên BTree hiếm khi phải thực hiện I / O, đây là công cụ tiêu dùng lớn nhất trong mọi truy vấn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.