Skip to content
Tác giả

1. Secondary Index là gì ?

Secondary Index sẽ cho phép chúng ta truy vấn dữ liệu không phải là một thành phần trong khóa chính (primary key)

Tuy nhiên do kiến chúc phân tán của db Casandra nên các truy vấn secondary index không được tối ưu khi truy vấn trên tệp dữ liệu lớn, truy vấn thường xuyên .

Hiểu đơn giản Secondary là INDEX trong mysql. Tuy nhiên vì cơ chế phân tán của Cassandra, nên nếu chỉ dùng Index mà không dùng Partition Key, Clustering Key thì ảnh hưởng hiệu suất rất nhiều.

Nên sử dụng khi:

  • Truy vấn không xảy ra quá thường xuyên hoặc không tạo ra tải ghi/đọc quá nặng.
  • Cardinality cao: Dữ liệu trong Secondary Index đa dạng dữ liệu, chứa ít dữ liệu lặp lại.
    • Ví dụ: Cột email sẽ là một cardinality cao vì nó sẽ có rất nhiều data khác nhau. Còn age hoặc sex thì chỉ có trong khoảng từ 1 đến 200 cho age hoặc sex thì có nam và nữa và giới tính thứ 3.

Cần cân nhắc:

Với khối lượng dữ liệu lớn hoặc cột có giá trị ít đa dạng, việc sử dụng secondary index có thể gây ra tình trạng hiệu năng giảm sút. Trong những trường hợp này, việc thiết kế lại mô hình dữ liệu theo hướng denormalization hay sử dụng materialized views sẽ hiệu quả hơn.

1.1 Ví dụ về Secondary Index trong Cassandra:

Cho ví dụ với table như sau

sql
CREATE TABLE users
(
  user_id UUID PRIMARY KEY,
  name    TEXT,
  email   TEXT,
  age     INT
);

Trong ví dụ này, user_id là khóa chính của bảng. Nếu bạn muốn tìm kiếm người dùng theo email mà không phải qua user_id, bạn có thể tạo một Secondary Index trên cột email:

sql
CREATE INDEX email_index ON users (email);

Sau khi tạo Secondary Index, bạn có thể thực hiện các truy vấn như sau:

sql
SELECT *
FROM users
WHERE email = '[email protected]';

Cassandra sẽ sử dụng Secondary Index để nhanh chóng tìm kiếm người dùng có email đó, thay vì phải quét toàn bộ bảng.

Vì thực tế địa chỉ email rất ít khi lặp lại, nên việc sử dụng Secondary Index trên cột email sẽ mang lại hiệu suất tốt.

Lưu ý:

Secondary Index có thể làm giảm hiệu suất khi dữ liệu lớn hoặc khi có nhiều ghi/chỉnh sửa trên các cột có chỉ mục. Vì vậy, bạn cần đánh giá kỹ lưỡng khi sử dụng Secondary Index trong các ứng dụng có yêu cầu về hiệu suất cao.

1.2 Cách Secondary Index hoạt động, tìm kiếm dữ liệu và lý do làm giảm hiệu suất

1.2.1. Cách Secondary Index hoạt động trong Cassandra

Khi bạn tạo một Secondary Index trên một cột, Cassandra sẽ tạo một bảng ẩn chứa ánh xạ từ giá trị của cột đó → Partition Key gốc của bảng chính. Theo cơ chế index.

Ví dụ với bảng user

sql
CREATE TABLE users
(
  user_id int PRIMARY KEY,
  name    TEXT,
  email   TEXT,
  age     INT
);

Thực tế ID trong Cassandra thường là UUID, vì là demo nên tôi đã int

Nếu tạo một Secondary Index trên cột email:

sql
CREATE INDEX email_index ON users (email);

Cassandra sẽ tạo một bảng ẩn chứa index ẩn có dạng:

emailuser_id(Partition key)
[email protected]1
[email protected]2
[email protected]3

Khi truy vấn: SELECT * FROM users WHERE email = '[email protected]';

  1. Cassandra tìm giá trị [email protected] trong bảng index.
  2. Tìm thấy [email protected] có partition key user_id = 1.
  3. Truy vấn vào bảng users bằng user_id = 1 để lấy dữ liệu đầy đủ.

1.2.2 Vì sao Secondary Index trong Cassandra làm giảm hiệu suất?

Trong cassandra khi sử dụng Secondary Index sẽ gây ra vấn đề scatter-gather" (phân tán-thu thập dữ liệu).

Secondary Index trong Cassandra được triển khai local của Node. Điều này là nghĩa là mỗi node chỉ lưu trữ Index cho dữ liệu mà nó lưu trữ. Khi bạn tạo một Secondary Index, Cassandra sẽ tạo một bảng ẩn (hidden table) trên mỗi node. Bảng này ánh xạ giá trị của cột được đánh chỉ mục tới Primary Key của (các) hàng chứa giá trị đó trên chính node đó.

Những lý do chính khiến hiệu suất giảm bao gồm:
  1. Query phân tán(Scatter-Gather)):
    • Khi thực hiện một query sử dụng Secondary Index mà không chỉ định partition key, query đó phải được gửi đến tất cả các Node trong cluster để tìm kiếm trong Index local trong các Node.
    • Sau đó, node điều phối(coordinator node) sẽ thực hiện thu thập kết quả ở tất cả các node và trả về cho client. Quá trình này làm tăng đáng kế độ trễ.
    • Nếu giá trị tìm kiếm bằng Secondary Index bị trùng lặp nhiều, việc này sẽ làm tăng số lượng dữ liệu cần quét và trả về trên mỗi node sau khi đã tìm kiếm xong trong Index local, dẫn đến hiệu suất giảm.
uml diagram
  1. Tăng Tải Ghi (Write Amplification)
    • Mỗi khi ghi(Insert) ,Cập nhật(Update )hoặc xóa(Delete) dữ liệu trong bảng chính, Cassandra cũng phải cập nhật bảng Index tương ứng. Điều này làm tăng tải ghi trên hệ thống, đặc biệt là khi có nhiều ghi/chỉnh sửa trên các cột có Index.
    • Điều này làm tăng gấp đôi (hoặc hơn) số lượng thao tác ghi cho mỗi lần thay đổi dữ liệu.
uml diagram
  1. Tăng Tải Đọc (Read Amplification):
    • Ngay khi truy vấn được gửi đến một node, việc đọc từ Secondary Index yêu cầu hai bước: đầu tiên là tìm kiếm trong bảng chỉ mục để lấy Primary Key, sau đó dùng Primary Key đó để đọc dữ liệu từ bảng chính.
    • Nếu một giá trị chỉ mục xuất hiện ở nhiều Record hoặc trên nhiều node, điều này có thể dẫn đến nhiều lần đọc riêng lẻ và giảm hiệu suất.
uml diagram
  1. Vấn Đề với Cardinality:
    • High Cardinality (Độ đa dạng cao): Nếu Column sử dụng để đánh Secondary Index có nhiều giá trị duy nhất(Ví dụ: Email, user_id, ...) bảng Index ẩn có thể sẽ rất lớn, làm tăng dung lượng lưu trữ.
      • Mặc dù vẫn gửi request đến tất cả các node, nhưng việc tìm kiếm trong Index sẽ nhanh hơn so với việc quét toàn bộ bảng chính. Bởi vì nếu tại bước đầu tiên không tìm thấy giá trị trong Index thì sẽ không thực hiện lấy dữ liệu từ bảng chính trong node đó.
    • Low Cardinality (Độ đa dạng thấp): Nếu column có ít giá trị duy nhất (ví dụ: giới tính, trạng thái), bảng Index sẽ chứa nhiều bản ghi trùng lặp. Điều này dẫn đến việc quét hàng triệu bản ghi trên bảng chính để tìm kiếm các bản ghi phù hợp với Index, làm giảm hiệu suất.
uml diagram
Minh họa chi tiết về vấn đề hiệu suất .

Giả sử chúng ta có một bảng users để lưu thông tin người dùng, và chúng ta muốn tìm kiếm người dùng theo thành phố.

Chi tiết

1. Tạo Bảng:

sql
CREATE KEYSPACE my_keyspace WITH replication = {
       'class': 'SimpleStrategy', 'replication_factor': 3
       };

USE my_keyspace;

CREATE TABLE users (
    user_id uuid PRIMARY KEY,
    name text,
    email text,
    city text
);

Trong bảng này, user_idPartition Key. Chúng ta chỉ có thể truy vấn hiệu quả theo user_id. Nếu muốn tìm theo city, chúng ta cần Secondary Index.

2. Tạo Secondary Index:

sql
CREATE INDEX idx_city ON users (city);

Cassandra sẽ tự động tạo một bảng index ẩn trên mỗi node. Bảng này sẽ có cấu trúc tương tự như: idx_city (city_value, user_id).

3. Thêm Dữ Liệu:

sql
INSERT INTO users (user_id, name, email, city) VALUES (uuid(), 'Thành', '[email protected]', 'Hanoi');
INSERT INTO users (user_id, name, email, city) VALUES (uuid(), 'Tuấn', '[email protected]', 'HCM');
INSERT INTO users (user_id, name, email, city) VALUES (uuid(), 'Nam', '[email protected]', 'Hanoi');
  • Khi ghi Thành, Node chịu trách nhiệm sẽ ghi hàng vào users và ghi (Hanoi, Thành_user_id) vào idx_city.
  • Khi ghi Tuấn, Node chịu trách nhiệm sẽ ghi hàng vào users và ghi (HCM, Tuấn_user_id) vào idx_city.
  • Khi ghi Nam, Node chịu trách nhiệm sẽ ghi hàng vào users và ghi (Hanoi, Nam_user_id) vào idx_city.

Lưu ý rằng ThànhNam có thể nằm trên các node khác nhau, vì partition key token khác nhau, và mỗi node đó sẽ có mục nhập Hanoi trong index local của nó.

4. Tìm Kiếm Dữ Liệu:

Bây giờ, chúng ta muốn tìm tất cả người dùng ở Hanoi:

sql
SELECT * FROM users WHERE city = 'Hanoi';

Đây là cách Cassandra xử lý (giả sử có 3 nút N1, N2, N3Thành ở N1, Nam ở N3):

  1. Node điều phối nhận the query(Có thể là 1 trong các node Cassandra, giả sử là N1).
  2. Bởi vì city là một Secondary Index, vì vậy N1 sẽ gửi query đến cả N2N3 để tìm kiếm trong index local của chúng.(Tìm kiếm dữ liệu trên cả 2 Node)
  3. N1: Tìm Hanoi trong idx_city local, thấy Thành_user_id. Đọc Record của Thành từ users.
  4. N2: Tìm Hanoi trong idx_city local, không thấy. Gửi về kết quả rỗng.
  5. N3: Tìm Hanoi trong idx_city local, thấy Nam_user_id. Đọc Record của Nam từ users và gửi về.
  6. N1 thu thập kết quả từ N2N3, trả về cho client.(Thành, Nam)

Như bạn thấy, dù chỉ có hai kết quả, truy vấn đã phải hỏi cả ba Node, gây ra hiện tượng scatter-gather và làm giảm hiệu suất so với việc truy vấn theo user_id.

1.3 Khi nào nên dùng Secondary Index?

  1. Khi thực sự cần truy vấn theo column không phải là partition key và chấp nhận độ trễ cao hơn.
  2. Khi truy vấn kết hợp với Partition Key, giúp giới hạn số lượng nút cần quét.
  3. Khi column có độ đa dạng (cardinality) không quá cao cũng không quá thấp (khó xác định).

Khi nào không nên sử dụng Secondary Index?

  1. Không nên sử dụng Secondary Index khi có thể sử dụng Partition Key để truy vấn.
  2. Không nên sử dụng Secondary Index nếu không kèm theo Partition Key, vì sẽ làm giảm hiệu suất. 2.1 Nếu có kèm theo Partition Key, thì hiệu suất sẽ tốt hơn. Dữ liệu chỉ cần tìm trên 1 node.
  3. Không nên sử dụng Secondary Index trên các cột có độ đa dạng (cardinality) quá thấp, vì sẽ làm tăng số lượng bản ghi cần quét.
Tác giả

DMCA.com Protection Status

Cập nhật lần cuối:

Secondary Indexing has loaded