Tổng Quan Về Elasticsearch
Trước khi đi sâu vào chi tiết kỹ thuật, chúng ta cần trả lời một số câu hỏi cơ bản: Elasticsearch là gì? Nó giúp ích gì cho tôi? Và tại sao tôi nên sử dụng nó?
Elasticsearch là một search engine và analytics engine mã nguồn mở với khả năng mở rộng cao. Nó cho phép bạn lưu trữ, tìm kiếm và phân tích khối lượng dữ liệu lớn một cách nhanh chóng và gần như real-time. Elasticsearch thường được sử dụng làm công nghệ nền tảng cho các ứng dụng có tính năng tìm kiếm phức tạp.
Elasticsearch được xây dựng dựa trên Apache Lucene, nhưng thay vì yêu cầu thao tác ở mức độ thấp, nó cung cấp giao tiếp thông qua RESTful APIs cùng với các tính năng advanced analytics. Phần RESTful API giúp Elasticsearch trở nên dễ học và sử dụng hơn.
Tại thời điểm viết tài liệu này, phiên bản mới nhất của Elasticsearch là 5.2.0. Nhóm phát triển Elasticsearch đã và đang liên tục phát hành các bản cập nhật nhanh chóng để cải tiến sản phẩm.
Một lợi ích lớn khác của việc Elasticsearch sử dụng RESTful API là tất cả dữ liệu gửi đi hoặc nhận về đều ở dạng JSON, giúp con người dễ đọc và hiểu. Điều này cũng làm cho việc tích hợp Elasticsearch với các ứng dụng khác trở nên dễ dàng hơn.
Để làm cho tài liệu này thực tế hơn, chúng ta sẽ giả định rằng chúng ta đang phát triển một ứng dụng quản lý thư viện sách. Data model của chúng ta sẽ bao gồm:
- Categories (thể loại sách)
- Authors (tác giả)
- Publisher (nhà xuất bản)
- Book details như ngày xuất bản, ISBN, rating
- Description (mô tả ngắn gọn)
Hãy cùng khám phá cách Elasticsearch có thể giúp chúng ta làm cho book catalog này dễ dàng tìm kiếm hơn. Nhưng trước tiên, chúng ta cần làm quen với một số thuật ngữ quan trọng trong Elasticsearch. 🚀
Documents
Trong ngữ cảnh của Elasticsearch, một document là một đơn vị dữ liệu có cấu trúc, có thể là bất kỳ thứ gì có ý nghĩa đối với ứng dụng của bạn, chẳng hạn như người dùng, log, bài viết, sản phẩm, v.v. Đây là đơn vị thông tin cơ bản mà Elasticsearch có thể xử lý.
Mỗi document trong Elasticsearch được lưu trữ dưới dạng JSON, một định dạng dữ liệu dễ đọc và phổ biến. Một document thường chứa nhiều fields, mỗi field có thể có một hoặc nhiều giá trị.
Ví dụ, dưới đây là một document đại diện cho một cuốn sách trong book catalog:
{
"title": "Elasticsearch: The Definitive Guide",
"categories": [
{ "name": "analytics" },
{ "name": "search" },
{ "name": "database store" }
],
"publisher": "O’Reilly",
"description": "A comprehensive guide to Elasticsearch.",
"published_date": "2015-02-07",
"isbn": "978-1449358549",
"rating": 4
}
Document Identification
Mỗi document trong Elasticsearch có một unique identifier, được lưu trữ trong _id field. Bạn có thể tự cung cấp ID khi tải document lên hoặc Elasticsearch sẽ tự động tạo một ID nếu bạn không chỉ định.
Ví dụ, để tải lên document trên với một ID cụ thể (ISBN của cuốn sách), bạn có thể sử dụng lệnh sau:
$ http PUT https://localhost:9200/catalog/books/978-1449358549 < book.json
Khi document được tạo thành công, Elasticsearch sẽ trả về phản hồi như sau:
{
"_id": "978-1449358549",
"_index": "catalog",
"_type": "books",
"_version": 1,
"result": "created"
}
Vậy là chúng ta đã lưu thành công một document vào Elasticsearch index! 🚀
Indices
Trong Elasticsearch, index là một tập hợp các documents có liên quan đến nhau. Mỗi index có một tên duy nhất và được sử dụng để xác định vị trí lưu trữ dữ liệu.
Về cơ bản, một index là một cơ sở dữ liệu trong Elasticsearch, nhưng nó có một số đặc điểm quan trọng:
- Một index có thể chứa nhiều documents thuộc cùng một chủ đề.
- Mỗi index có thể có một hoặc nhiều types, giúp nhóm các documents theo cấu trúc dữ liệu chung.
- Indices có thể được quản lý bằng cách sử dụng Index APIs.
Tạo Một Index Mới
Để tạo một index, bạn có thể sử dụng Index API với một request PUT như sau:
$ http PUT https://localhost:9200/catalog
Nếu muốn chỉ định thêm cài đặt như số lượng shards và replicas, bạn có thể sử dụng:
{
"settings": {
"index": {
"number_of_shards": 5,
"number_of_replicas": 2
}
}
}
Kiểm Tra Các Indices Hiện Có
Để kiểm tra xem Elasticsearch cluster của bạn đã có indices nào chưa, bạn có thể sử dụng Indices Stats API:
$ http https://localhost:9200/_stats
Nếu kết quả trả về rỗng, điều đó có nghĩa là hiện chưa có index nào được tạo.
Xóa Một Index
Nếu bạn muốn xóa một index, sử dụng lệnh:
$ http DELETE https://localhost:9200/catalog
Lệnh này sẽ xóa hoàn toàn index cùng với tất cả documents bên trong nó.
Trong phần tiếp theo, chúng ta sẽ đi sâu hơn vào Index Settings, một khía cạnh quan trọng giúp tối ưu hóa hiệu suất của Elasticsearch! 🚀
Cấu Hình Index (Index Settings)
Mỗi index trong Elasticsearch có thể có các thiết lập riêng ngay từ lúc tạo. Hai thiết lập quan trọng nhất là số lượng shards và replication factor. Hãy cùng tìm hiểu kỹ hơn về chúng.
Sharding - Phân Mảnh Dữ Liệu
Elasticsearch được thiết kế để hoạt động với dữ liệu lớn, vượt quá khả năng lưu trữ của một máy chủ đơn lẻ. Để giải quyết vấn đề này, Elasticsearch sử dụng sharding – cơ chế chia một index thành nhiều mảnh nhỏ hơn gọi là shards và phân phối chúng trên nhiều nodes khác nhau.
Lưu ý quan trọng:
- Một khi số shards được thiết lập, bạn không thể thay đổi nó (mặc dù bạn có thể giảm số shards bằng cách shrink index).
- Số lượng shards quyết định khả năng mở rộng của hệ thống.
Replication - Sao Lưu Dữ Liệu
Mặc dù sharding giúp mở rộng dữ liệu, nó cũng có nguy cơ mất dữ liệu khi một node bị lỗi. Để giải quyết vấn đề này, Elasticsearch hỗ trợ replication, tức là tạo ra nhiều bản sao của mỗi shard trên các nodes khác nhau.
- Primary Shard: Mỗi tài liệu sẽ được lưu trữ trên một primary shard.
- Replica Shard: Elasticsearch sẽ tạo ra replica shards từ primary shard và phân phối chúng trên các nodes khác nhau để đảm bảo tính sẵn sàng (high availability).
Cấu hình số lượng replicas giúp Elasticsearch hoạt động ổn định hơn và hỗ trợ load balancing khi thực hiện tìm kiếm.
Ví dụ, khi tạo một index, bạn có thể định nghĩa số shards và replicas như sau:
{
"settings": {
"index": {
"number_of_shards": 5,
"number_of_replicas": 2
}
}
}
Kiểm Tra Cấu Hình Của Một Index
Sau khi tạo index, bạn có thể kiểm tra index settings bằng cách sử dụng Index Settings API:
$ http https://localhost:9200/catalog/_settings
Kết quả trả về sẽ hiển thị chi tiết settings của index.
Cập Nhật Index Settings
Mặc dù bạn không thể thay đổi số lượng shards, bạn có thể thay đổi số lượng replicas bằng Update Index Settings API:
$ echo ’{"index":{"number_of_replicas":3}}’ | http PUT https://localhost:9200/catalog/_settings
Tóm Lược
- Shards giúp Elasticsearch mở rộng dữ liệu theo chiều ngang.
- Replication đảm bảo dữ liệu không bị mất khi xảy ra lỗi phần cứng.
- Index settings có thể được điều chỉnh nhưng số lượng shards không thể thay đổi sau khi index đã được tạo.
Việc lựa chọn số lượng shards và replicas phù hợp ngay từ đầu là rất quan trọng để đảm bảo hiệu suất và độ tin cậy của Elasticsearch cluster. 🚀
Mappings
Quá trình xác định kiểu dữ liệu cho documents và gán chúng vào một index cụ thể được gọi là index mapping, mapping type hoặc đơn giản là mapping. Xây dựng một mapping type phù hợp là một trong những bài toán thiết kế quan trọng nhất để tối ưu hóa hiệu suất của Elasticsearch.
Mỗi mapping bao gồm:
- Meta-fields (bắt đầu bằng ký tự
_
, chẳng hạn như_index
,_id
,_parent
). - Document fields (còn gọi là properties).
Mỗi field trong Elasticsearch có một data type, thuộc một trong các nhóm sau:
Simple Data Types
- text – Chỉ mục dữ liệu full-text search.
- keyword – Chỉ mục dữ liệu có cấu trúc (không phân tích từ khóa).
- date – Chỉ mục giá trị ngày/thời gian.
- long, integer, short, byte – Chỉ mục số nguyên với độ dài khác nhau (64-bit, 32-bit, 16-bit, 8-bit).
- double, float, half_float, scaled_float – Chỉ mục số thực với độ chính xác khác nhau.
- boolean – Chỉ mục giá trị đúng/sai.
- ip – Chỉ mục địa chỉ IPv4 hoặc IPv6.
- binary – Chỉ mục dữ liệu nhị phân mã hóa dưới dạng Base64.
Composite Data Types
- object – Chỉ mục các inner objects, có thể lồng nhau.
- nested – Một phiên bản đặc biệt của object, cho phép chỉ mục arrays of objects độc lập với nhau.
Specialized Data Types
- geo_point – Chỉ mục tọa độ vĩ độ/kinh độ.
- geo_shape – Chỉ mục các hình dạng địa lý phức tạp (hình chữ nhật, đa giác, v.v.).
Range Data Types
- long_range, double_range, date_range – Chỉ mục khoảng giá trị (ví dụ: khoảng ngày tháng hoặc số thực).
Lựa Chọn Data Type Tối Ưu
Việc chọn data type phù hợp cho các fields trong documents là chìa khóa để có một hệ thống search nhanh chóng và hiệu quả. Tuy nhiên, có một lưu ý quan trọng: Các fields có cùng tên trong một index nhưng thuộc các mapping types khác nhau phải có cùng mapping definition, vì nội bộ chúng được ánh xạ đến cùng một field.
Dựa trên data model của ứng dụng quản lý book catalog, ta có thể định nghĩa một mapping type đơn giản như sau:
Book Catalog Mapping (Lần đầu tiên)
{
"mappings": {
"books": {
"_source": { "enabled": true },
"properties": {
"title": { "type": "text" },
"categories": {
"type": "nested",
"properties": {
"name": { "type": "text" }
}
},
"publisher": { "type": "keyword" },
"description": { "type": "text" },
"published_date": { "type": "date" },
"isbn": { "type": "keyword" },
"rating": { "type": "byte" }
}
}
}
}
Định nghĩa trên khá đơn giản, nhưng vẫn có một số vấn đề cần giải quyết, chẳng hạn như cách lưu trữ authors và categories, vì chúng là các tập hợp giá trị mà Elasticsearch không có kiểu dữ liệu chuyên biệt. Trong phần tiếp theo, chúng ta sẽ tìm hiểu cách sử dụng Advanced Mappings để giải quyết bài toán này một cách tối ưu. 🚀
Advanced Mappings
Mặc dù Elasticsearch không có kiểu dữ liệu chuyên biệt cho array hoặc collection, nhưng mọi field trong Elasticsearch đều có thể chứa zero hoặc nhiều giá trị theo mặc định. Điều này có nghĩa là bạn không cần khai báo trước một field là một array, vì Elasticsearch sẽ tự động xử lý điều đó.
Khi làm việc với cấu trúc dữ liệu phức tạp, Elasticsearch hỗ trợ ba cách tiếp cận chính:
- Object Data Type – Dữ liệu được lưu trữ dưới dạng JSON object, nhưng các nested objects bên trong sẽ không được đánh chỉ mục một cách độc lập.
- Nested Data Type – Tương tự object, nhưng mỗi nested document được đánh chỉ mục riêng biệt.
- Parent-Child Relationship – Các documents riêng biệt được liên kết theo kiểu parent-child, thay vì lồng vào nhau.
Sử Dụng Nested Objects Cho Categories
Trong book catalog, mỗi cuốn sách có thể thuộc nhiều categories, do đó chúng ta có thể sử dụng nested data type để lưu trữ thông tin này:
"categories": {
"type": "nested",
"properties": {
"name": { "type": "text" }
}
}
Điều này giúp Elasticsearch có thể truy vấn các categories độc lập với nhau, thay vì gộp tất cả vào một JSON object thông thường.
Sử Dụng Parent-Child Relationship Cho Authors
Một cuốn sách có thể có nhiều tác giả, và một tác giả có thể viết nhiều sách. Thay vì lưu authors dưới dạng nested objects, chúng ta có thể thiết lập mối quan hệ parent-child bằng cách tách authors thành một mapping type riêng biệt:
"authors": {
"properties": {
"first_name": { "type": "keyword" },
"last_name": { "type": "keyword" }
},
"_parent": { "type": "books" }
}
Với cách này, chúng ta có thể tìm kiếm books dựa trên authors mà không cần phải lặp qua toàn bộ dữ liệu book documents.
Mapping Hoàn Chỉnh Cho Book Catalog
Dưới đây là phiên bản cải tiến của mapping types cho catalog index, kết hợp cả nested objects và parent-child relationships:
{
"mappings": {
"books": {
"_source": { "enabled": true },
"properties": {
"title": { "type": "text" },
"categories": {
"type": "nested",
"properties": {
"name": { "type": "text" }
}
},
"publisher": { "type": "keyword" },
"description": { "type": "text" },
"published_date": { "type": "date" },
"isbn": { "type": "keyword" },
"rating": { "type": "byte" }
}
},
"authors": {
"properties": {
"first_name": { "type": "keyword" },
"last_name": { "type": "keyword" }
},
"_parent": { "type": "books" }
}
}
}
Với advanced mappings, chúng ta có thể cải thiện khả năng tìm kiếm và tối ưu hóa hiệu suất khi làm việc với dữ liệu phức tạp trong Elasticsearch! 🚀
Indexing
Sau khi Elasticsearch có đầy đủ indices và mapping types được xác định (hoặc suy luận thông qua dynamic mapping), nó sẽ sẵn sàng để phân tích và lập chỉ mục documents. Đây là một quá trình khá phức tạp nhưng rất thú vị, bao gồm các thành phần chính như analyzers, tokenizers, token filters và character filters.
Cấu Hình Indexing Và Search
Elasticsearch cung cấp nhiều tham số mapping để điều chỉnh cẩn thận quá trình indexing, analysis và search theo nhu cầu cụ thể của bạn. Ví dụ, mỗi field có thể:
- Sử dụng các index-time và search-time analyzers riêng biệt.
- Hỗ trợ synonyms.
- Áp dụng stemming để chuẩn hóa từ vựng.
- Lọc bỏ stop words để tăng độ chính xác.
Nếu bạn không cần tinh chỉnh chi tiết, bạn có thể sử dụng các thiết lập mặc định, giống như cách chúng ta đã làm trong phần trước. Tuy nhiên, hầu hết các ứng dụng thực tế đều cần hỗ trợ nhiều ngôn ngữ, và Elasticsearch cung cấp nhiều công cụ mạnh mẽ để xử lý vấn đề này.
Lưu Ý Quan Trọng Khi Indexing
Một điểm quan trọng cần nhớ là mapping types trong Elasticsearch hầu như không thể cập nhật sau khi đã được thiết lập. Khi thay đổi mapping, Elasticsearch sẽ giả định rằng tất cả các documents trong collection tương ứng không còn phù hợp và cần phải re-index. Điều này có thể ảnh hưởng đáng kể đến hiệu suất hệ thống, đặc biệt là với các large-scale indices.
Trong phần tiếp theo, chúng ta sẽ tìm hiểu cách tối ưu hóa indexing để hỗ trợ đa ngôn ngữ (i18n) trong Elasticsearch! 🚀
Hỗ Trợ Đa Ngôn Ngữ (i18n)
Quá trình indexing và analyzing documents trong Elasticsearch phụ thuộc rất nhiều vào ngôn ngữ của tài liệu. Mặc định, nếu không có analyzer nào được chỉ định trong mapping types, Elasticsearch sẽ sử dụng standard analyzer. Analyzer này hoạt động tốt với hầu hết các ngôn ngữ, nhưng Elasticsearch cũng cung cấp các dedicated analyzers cho nhiều ngôn ngữ khác nhau như: Arabic, Armenian, Basque, Brazilian, Bulgarian, Czech, Danish, Dutch, English, Finnish, French, German, Greek, Hindi, Hungarian, Indonesian, Irish, Italian, Latvian, Lithuanian, Norwegian, Persian, Portuguese, Romanian, Russian, Spanish, Swedish, Turkish, Thai và một số ngôn ngữ khác.
Chiến Lược Xử Lý Đa Ngôn Ngữ
Có nhiều cách để quản lý indexing của cùng một tài liệu trong nhiều ngôn ngữ khác nhau, tùy thuộc vào mô hình dữ liệu và yêu cầu của hệ thống.
Một Index Riêng Cho Mỗi Ngôn Ngữ
Nếu mỗi tài liệu có sẵn ở nhiều ngôn ngữ dưới dạng bản dịch hoàn chỉnh, phương án tối ưu nhất là tạo một index riêng cho mỗi ngôn ngữ. Ví dụ:
books_en
– Chứa sách bằng tiếng Anh.books_fr
– Chứa sách bằng tiếng Pháp.books_de
– Chứa sách bằng tiếng Đức.
Cách tiếp cận này giúp quản lý dễ dàng hơn nhưng cũng có thể làm tăng kích thước dữ liệu và gây phân mảnh Elasticsearch cluster.
Multi-fields Cho Các Thuộc Tính Quan Trọng
Trong trường hợp các tài liệu chỉ được dịch một phần, Elasticsearch cung cấp một tùy chọn hữu ích gọi là multi-fields. Với multi-fields, cùng một field có thể được index theo nhiều cách khác nhau để phục vụ các mục đích tìm kiếm khác nhau, bao gồm hỗ trợ đa ngôn ngữ.
Ví dụ, trong mapping type của book catalog, thuộc tính "title"
có thể được định nghĩa như sau:
"title": {
"type": "text",
"fields": {
"en": { "type": "text", "analyzer": "english" },
"fr": { "type": "text", "analyzer": "french" },
"de": { "type": "text", "analyzer": "german" }
}
}
Với cách tiếp cận này, mỗi tài liệu chỉ cần một index duy nhất, nhưng người dùng vẫn có thể tìm kiếm bằng nhiều ngôn ngữ khác nhau.
Kết Luận
Hỗ trợ đa ngôn ngữ (i18n) là một trong những tính năng mạnh mẽ của Elasticsearch. Bằng cách sử dụng dedicated analyzers, multi-fields, hoặc index riêng cho từng ngôn ngữ, bạn có thể tối ưu hóa khả năng tìm kiếm trong hệ thống của mình mà không làm giảm hiệu suất. 🚀
All rights reserved