Bài viết được lấy từ nguồn Viblo.asia.
Là một lập trình viên, chắc hẳn không ít thì nhiều, bạn đã từng phải nghe nói đến việc đánh index (chỉ mục) cho table này, table khác trong database. Dù có không hiểu index là gì, thì ắt hẳn bạn cũng phải biết một điều xưa như trái đất, rằng muốn truy vấn cho nhanh, thì phải đánh index. Nhưng do tính chất công việc, liệu rằng trong chúng ta, mấy ai từng được làm việc nhiều với database, để có cơ hội tìm hiểu kĩ về vấn đề này. Tôi tin rằng không ít người, kể cả những lập trình viên vài năm kinh nghiệm, khi được hỏi : index là gì, index hoạt động như thế nào, tại sao đánh index lại khiến tăng tốc độ truy vấn … ? cũng chưa chắc đã có thể trả lời cặn kẽ. Bài viết này, vốn được tìm hiểu vội vàng trong một khoảng thời gian ngắn, hi vọng có thể giúp bạn ít nhiều hiểu rõ hơn một chút về vấn đề này.
Một kịch bản không có index
Để hiểu index là gì, ta hãy thử đặt ra một kịch bản truy vấn đơn giản, giả sử ta muốn tìm trong bảng users
, lấy ra thông tin của tất cả những user có name là DungNQ.
SELECT * FROM USER WHERE NAME = 'DungNQ';
Trong tình huống này, database của chúng ta sẽ phải đọc ra từng record trong bảng users
, so sánh giá trị của trường name
trong record đó với giá trị cần tìm, và thực hiện từ record đầu tiên cho tới record cuối cùng của bảng. Cách làm này gọi là full table search. Dĩ nhiên, ta có thể thấy đây là cách làm chậm nhất, vì phải lọc qua tất cả dữ liệu hiện hữu trong bảng. Từ đó đặt ra vấn đề, muốn tăng tốc độc truy vấn, phải làm sao giảm thiểu số lượng bản ghi/trường cần phải đọc khi thực hiện truy vấn.
Tăng tốc truy vấn bằng cách đánh chỉ mục.
Hãy tưởng tượng, cũng trong kịch bản truy vấn trên, nhưng trong tay ta đang có sẵn một chuỗi, bao gồm tất cả các giá trị của trường name
có trong bảng users
, và các giá trị này đã được sắp xếp theo thứ tự tăng dần. Lúc này, để tìm ra record có giá trị name = 'DungN'
, thay vì lôi lần lượt từ giá trị đầu tiên đến giá trị thứ n cuối cùng ra để so sánh (cần thực hiện n phép so sánh ), ta có thể thực hiện một phép tìm kiếm nhị phân. Rõ ràng việc thực hiện phép tìm kiếm ban đầu đã trở nên dễ dàng hơn và nhanh hơn rất nhiều. Không chỉ số lượng record
ta phải đọc ít đi, mà trong mỗi record
, ta cũng chỉ cần đọc giá trị của đúng trường name
mà thôi. Và giả sử, ứng với mỗi một phần tử trong dãy như trên, ta có thể gán ứng với một giá trị con trỏ, trỏ đến record tương ứng trong bảng users
, ta hoàn toàn có thể truy vấn tất cả các thông tin của user cần tìm, chứ không chỉ giá trị trường name
được lưu trong dãy. Dạng cấu trúc dữ liệu như dãy được mô tả trên, chính là một dạng index, cũng thuộc loại phổ biến bậc nhất, B-tree index.
Một kịch bản khác.
Cũng trong kịch bản truy vấn trên, ta không có một dãy được sắp xếp theo thứ tự sẵn như trên nữa. Thay vào đó, ta có một array các cặp key => value
dạng như 'DungNQ' => 0x28939
trong đó, key
chính là giá trị của trường name
trong bảng users
, còn value
là biến con trỏ chỉ đến record tương ứng trong bảng. Rõ ràng, trong trường hợp này, truy vấn đầu tiên, nếu được thực hiện trong array kia, sẽ nhanh chóng hơn rất nhiều so với thực hiện full table scan trên bảng users. Kiểu cấu trúc dữ liệu giống như mô tả trên cũng là một dạng index tương đối phổ biến, Hash table index.
Index là gì?
Ngoài B-tree index hay Hash table index ra, còn có một số loại index tương đối kém phổ biến hơn , như R-tree index hay Bitmap index. Mỗi loại Index đều có cấu trúc riêng, có ưu và nhược điểm riêng, phù hợp cho từng kiểu truy vấn đặc thù. Ví dụ như Hash table index có lợi thế rất nhanh khi thực hiện các phép truy vấn với toán tử =
hay <>
, tuy nhiên lại bó tay trước các phép truy vấn với toán tử <
hay >
. B-tree index là loại index phổ biến nhất, và là loại index mặc định trong hầu hết các hệ quản trị cơ sở dữ liệu. Tuy index loại này rất mềm dẻo, hữu ích trong rất nhiều tình huống so với Hash table index, nhưng lại chịu chết với các phép so sánh <>
. Tổng kết chung lại , ta có thể có một định nghĩa khá chung và toàn diện cho index:
Index là một dạng cấu trúc dữ liệu, trong đó chứa giá trị của một trường nhất định trong một bảng dữ liệu, đồng thời trỏ đến record dữ liệu tương ứng.
Tổng kết
Lan man một hồi, có lẽ cũng là gọi là tạm đủ để trả lời mấy câu hỏi đặt ra lúc ban đầu : index là gì (khái niệm tự chém kia), index hoạt động như thế nào (giải thích chi tiết hai loại cơ bản nhất là đủ), tại sao đánh index lại làm truy vấn nhanh hơn(xem phần hoạt động). Tuy nhiên, nếu chỉ có nhiêu đó, thì chắc chỉ có ích cho hoạt động duy nhất, đấy là đi phỏng vấn. Nếu bạn là một lập trình viên bình thường, quần đùi chân đất, đọc xong bằng kia chữ, rốt cuộc là ta được cái gì :
-
Thứ nhất là khi nào cần đánh index, và đánh index như thế nào: Qua giải thích chi tiết như trên kia, thì ta có thể thấy là index chỉ hữu ích với những truy vấn có điều kiện tìm kiếm liên quan đến cột mà ta đã đánh index cho nó. Thế thì xét xem cột nào hay dùng nhất thì đánh index vào. Ví dụ như
name
trongusers
, tối ngày cần đến nó, vì lấy ra thông tin kiểu gì chả phải lấy cho nó cái tên, rồi còn check xem cóname
này đã được sử dụng hay chưa, rồi lúc đăng nhập cũng phải truy vấn bằngname
… Vậy thì nên tạo cho nó cái index. -
Index hữu dụng thế, nên trường id ở đa số các hệ quản trị cơ sở dữ liệu đều được tự động đánh Index, khỏi mất công khởi tạo. Để kiểm tra, bạn có thể dùng
SHOW INDEX FROM table_name FROM db_name;
(MyQL) để liệt kê toàn bộ chỉ mục của một table trong database. -
Tạo index cho một trường, là tạo ra một cấu trúc lưu dữ liệu của trường đó, kèm theo một số thứ khác nữa. Thế nên cũng đừng thấy, ví dụ bảng
posts
có trườngcontents
hay được tìm kiếm, lại hăm hở đánh index cho cái trường đấy, mà biết đâu cái index to gần bằng nguyên xi cả bảng. -
Quan trọng nhất là , hiểu được cách hoạt động của từng loại index rồi, thì dùng sao cho hợp lý. Cụ thể hơn, các trường hợp hay gặp nhất là :
-
B-tree index dựa vào việc sắp xếp các value theo thứ tự. Thế nên chẳng hạn đánh index (chỉ khai báo index không, thì database sẽ tự hiểu là mặc định, dùng B-tree index) cho trường
name
, nhưng lại thực hiện toàn các truy vấn kiểuWHERE NAME NOT IN ('foo','bar')
hayWHERE NAME <> 'SPARTANNNNNNNN'
thì index cũng bằng thừa trong các kiểu truy vấn này. -
Cũng về B-tree index, ở trên không nói rõ, tại mình cũng không nắm chắc 100%, nhưng có vẻ như thứ tự sắp xếp trong index là thứ tự tăng dần. Nên nếu dùng index loại này, các truy vấn kiểu như WHERE NAME LIKE ‘DUNG%’ hay thậm chí là WHERE NAME LIKE ‘D%G%’ thì index đều có tác dụng, nhưng với WHERE NAME LIKE ‘%UNG’ thì index có cũng như không.
-
Cuối cùng là vì đặc điểm cấu tạo của B-tree index, index dạng này có tác dụng tăng tốc với các phép truy vấn
ORDER BY
. -
Về Hash table index, do cấu tạo như đã nói, index dạng này chỉ có tác dụng với các truy vấn
=
và<>
hayNOT IN
, nghĩa là các truy vấn chính xác, giống hoặc không giống. Các truy vấn dạng nhưWHERE NAME LIKE 'A%'
, index dạng này cũng không có tác dụng, chứ đừng nói đến các toán tử khác như>
hay<
. Bù lại thì đúng theo tinh thần chuyên môn hóa cao, các phép truy vấn như ví dụ đầu tiênWHERE NAME = 'DungNQ'
thực hiện trên index dạng này lại có tốc độ vô đối. Vì vậy, tùy theo nhu cầu của bạn mà thiết kế cũng như đánh chỉ mục sao cho thật hợp lý.