Bối cảnh
Câu hỏi này liên quan đến các chi tiết triển khai cấp thấp của các chỉ mục trong cả hai hệ thống cơ sở dữ liệu SQL và NoQuery. Cấu trúc thực tế của chỉ mục (cây B +, hàm băm, SSTable, v.v.) không liên quan vì câu hỏi liên quan cụ thể đến các khóa được lưu trữ bên trong một nút của bất kỳ triển khai nào.
Lý lịch
Trong SQL (ví dụ như MySQL) và NoSQL (CouchDB, MongoDB, vv) cơ sở dữ liệu, khi bạn xây dựng một chỉ mục trên một cột hoặc lĩnh vực tài liệu JSON dữ liệu, những gì bạn đang thực sự gây ra cơ sở dữ liệu để làm là tạo cơ bản một danh sách được sắp xếp tất cả những giá trị đó cùng với một tệp được bù vào tệp dữ liệu chính trong đó bản ghi liên quan đến giá trị đó tồn tại.
(Để đơn giản, tôi có thể vẫy tay với các chi tiết bí truyền khác của các hàm ý cụ thể)
Ví dụ SQL cổ điển đơn giản
Hãy xem xét một bảng SQL tiêu chuẩn có khóa chính int 32 bit đơn giản mà chúng ta tạo ra một chỉ mục, chúng ta sẽ kết thúc với một chỉ mục trên đĩa của các khóa số nguyên được sắp xếp và liên kết với phần bù 64 bit vào tệp dữ liệu kỷ lục cuộc sống, ví dụ:
id | offset
--------------
1 | 1375
2 | 1413
3 | 1786
Biểu diễn trên đĩa của các khóa trong chỉ mục trông giống như thế này:
[4-bytes][8-bytes] --> 12 bytes for each indexed value
Bám sát các quy tắc chuẩn về tối ưu hóa I / O của đĩa với các hệ thống tệp và hệ thống cơ sở dữ liệu, giả sử bạn lưu trữ các khóa trong các khối 4KB trên đĩa, có nghĩa là:
4096 bytes / 12 bytes per key = 341 keys per block
Bỏ qua cấu trúc tổng thể của chỉ mục (cây B +, hàm băm, danh sách được sắp xếp, v.v.) chúng ta đọc và ghi các khối 341 phím cùng một lúc vào bộ nhớ và quay trở lại đĩa khi cần.
Ví dụ truy vấn
Sử dụng thông tin từ phần trước, giả sử một truy vấn xuất hiện cho "id = 2", việc tra cứu chỉ số DB cổ điển diễn ra như sau:
- Đọc thư mục gốc của chỉ mục (trong trường hợp này là 1 khối)
- Nhị phân tìm kiếm khối đã sắp xếp để tìm khóa
- Lấy tập tin dữ liệu bù từ giá trị
- Tra cứu bản ghi trong tệp dữ liệu bằng cách sử dụng offset
- Trả lại dữ liệu cho người gọi
Cài đặt câu hỏi ...
Ok, đây là nơi câu hỏi đến với nhau ...
Bước # 2 là phần quan trọng nhất cho phép các truy vấn này thực hiện trong thời gian O (logn) ... thông tin phải được sắp xếp, NHƯNG bạn phải có khả năng duyệt qua danh sách theo cách sắp xếp nhanh chóng ... hơn nữa cụ thể, bạn phải có khả năng chuyển sang các độ lệch được xác định rõ theo ý muốn để đọc giá trị khóa chỉ mục ở vị trí đó.
Sau khi đọc trong khối, bạn phải có thể nhảy đến vị trí thứ 170 ngay lập tức, đọc giá trị khóa và xem liệu thứ bạn đang tìm kiếm là GT hay LT ở vị trí đó (vân vân và vân vân ...)
Cách duy nhất bạn có thể nhảy xung quanh dữ liệu trong khối như vậy là nếu các kích thước giá trị khóa đều được xác định rõ, như ví dụ của chúng tôi ở trên (4 byte rồi 8 byte mỗi khóa).
CÂU HỎI
Ok, đây là nơi tôi đang bị mắc kẹt với thiết kế chỉ mục hiệu quả ... cho các cột varchar trong cơ sở dữ liệu SQL hoặc cụ thể hơn, các trường dạng hoàn toàn tự do trong cơ sở dữ liệu tài liệu như CouchDB hoặc NoQuery, trong đó bất kỳ trường nào bạn muốn lập chỉ mục đều có thể chiều dài làm thế nào để bạn thực hiện các giá trị chính bên trong các khối của cấu trúc chỉ mục mà bạn xây dựng các chỉ số của mình?
Ví dụ: giả sử bạn sử dụng bộ đếm tuần tự cho ID trong CouchDB và bạn đang lập chỉ mục các tweet ... bạn sẽ có các giá trị từ "1" đến "100.000.000" sau vài tháng.
Giả sử bạn xây dựng chỉ mục trên cơ sở dữ liệu vào ngày 1, khi chỉ có 4 tweet trong cơ sở dữ liệu, CouchDB có thể bị cám dỗ sử dụng cấu trúc sau cho các giá trị chính bên trong các khối chỉ mục:
[1-byte][8-bytes] <-- 9 bytes
4096 / 9 = 455 keys per block
Tại một số điểm này phá vỡ và bạn cần một số byte khác nhau để lưu trữ giá trị khóa của bạn trong các chỉ mục.
Điểm thậm chí còn rõ ràng hơn nếu bạn quyết định lập chỉ mục một trường có độ dài thực sự thay đổi như "tweet_message" hoặc một cái gì đó.
Với bản thân khóa có độ dài thay đổi hoàn toàn và cơ sở dữ liệu không có cách nào để đoán một cách thông minh ở một số "kích thước khóa tối đa" khi chỉ mục được tạo và cập nhật, các khóa này thực sự được lưu trữ bên trong các khối biểu thị các phân đoạn của các chỉ mục trong các cơ sở dữ liệu này như thế nào ?
Rõ ràng nếu các khóa của bạn có kích thước thay đổi và bạn đọc trong một khối các khóa, bạn không chỉ không biết có bao nhiêu khóa thực sự trong khối, mà bạn không biết làm thế nào để nhảy vào giữa danh sách để thực hiện nhị phân tìm kiếm trên chúng
Đây là nơi tôi đang nhận được tất cả tăng gấp ba.
Với các trường được nhập tĩnh trong cơ sở dữ liệu SQL cổ điển (như bool, int, char, v.v.) Tôi hiểu chỉ mục có thể chỉ định trước độ dài khóa và bám vào nó ... nhưng trong thế giới lưu trữ dữ liệu tài liệu này, tôi bối rối làm thế nào họ mô hình hóa dữ liệu này trên đĩa một cách hiệu quả để nó vẫn có thể được quét trong thời gian O (logn) và sẽ đánh giá cao bất kỳ sự làm rõ nào ở đây.
Xin vui lòng cho tôi biết nếu cần làm rõ!
Cập nhật (Câu trả lời của Greg)
Xin vui lòng xem ý kiến của tôi kèm theo câu trả lời của Greg. Sau một tuần nghiên cứu, tôi nghĩ rằng anh ấy đã thực sự vấp phải một gợi ý đơn giản và hiệu quả tuyệt vời rằng trong thực tế là rất dễ thực hiện và sử dụng trong khi mang lại hiệu quả lớn trong việc tránh việc loại bỏ các giá trị chính mà bạn không quan tâm.
Tôi đã xem xét 3 triển khai DBMS riêng biệt (CouchDB, kivaloo và InnoDB) và tất cả chúng đều xử lý vấn đề này bằng cách khử lưu lượng toàn bộ khối vào cấu trúc dữ liệu bên trong trước khi tìm kiếm các giá trị bên trong môi trường thực thi của chúng (erlang / C).
Đây là những gì tôi nghĩ là rất xuất sắc về đề nghị của Greg; kích thước khối bình thường là 2048 thường sẽ có 50 điểm bù hoặc ít hơn, dẫn đến một khối số rất nhỏ cần phải đọc.
Cập nhật (nhược điểm tiềm năng đối với đề xuất của Greg)
Để tiếp tục tốt nhất với hộp thoại này với chính mình, tôi đã nhận ra những nhược điểm sau đây ...
Nếu mọi "khối" đều hướng đến dữ liệu bù, bạn không thể cho phép điều chỉnh kích thước khối trong cấu hình sau này vì bạn có thể sẽ đọc dữ liệu không bắt đầu bằng tiêu đề chính xác hoặc khối mà chứa nhiều tiêu đề.
Nếu bạn đang lập chỉ mục các giá trị khóa lớn (giả sử ai đó đang cố gắng lập chỉ mục một cột char (8192) hoặc blob (8192)), có thể các khóa không khớp trong một khối và cần được tràn qua hai khối cạnh nhau . Điều này có nghĩa là khối đầu tiên của bạn sẽ có một tiêu đề bù và khối thứ hai sẽ ngay lập tức bắt đầu với dữ liệu chính.
Giải pháp cho tất cả điều này là có kích thước khối cơ sở dữ liệu cố định không thể điều chỉnh và phát triển cấu trúc dữ liệu khối tiêu đề xung quanh nó ... ví dụ: bạn sửa tất cả các kích thước khối thành 4KB (thường là tối ưu nhất) và viết rất nhỏ tiêu đề khối bao gồm "loại khối" ở đầu. Nếu đó là một khối bình thường, thì ngay sau tiêu đề khối sẽ là tiêu đề bù đắp. Nếu đó là loại "tràn", thì ngay sau tiêu đề khối là dữ liệu khóa thô.
Cập nhật (tiềm năng tuyệt vời)
Sau khi khối được đọc thành một chuỗi byte và phần bù được giải mã; về mặt kỹ thuật, bạn có thể chỉ cần mã hóa khóa mà bạn đang tìm kiếm thành byte thô và sau đó thực hiện so sánh trực tiếp trên luồng byte.
Khi tìm thấy khóa bạn đang tìm kiếm, con trỏ có thể được giải mã và theo dõi.
Một tác dụng phụ tuyệt vời khác của ý tưởng của Greg! Tiềm năng tối ưu hóa thời gian của CPU ở đây đủ lớn để thiết lập kích thước khối cố định có thể đáng giá chỉ để đạt được tất cả điều này.