Loại bỏ Mapping Type trong Elasticsearch: Lý do và Chiến lược Chuyển đổi

Kể từ phiên bản Elasticsearch 6.0, một thay đổi mang tính bước ngoặt đã được thực hiện: mỗi index chỉ được phép chứa duy nhất một mapping type. Các phiên bản 7.x tiếp tục lộ trình loại bỏ hoàn toàn khái niệm này, và đến phiên bản 8.0, mapping type đã chính thức bị xóa bỏ khỏi các API. Việc hiểu rõ lý do và cách thích nghi với thay đổi này là cực kỳ quan trọng đối với các kỹ sư hệ thống khi làm việc với Elasticsearch.

Mapping Type là gì?

Trong các phiên bản cũ, Mapping Type được sử dụng để phân loại các tài liệu (document) trong cùng một index. Ví dụ, một index có tên blog_system có thể chứa hai loại dữ liệu là authorarticle. Mỗi loại sẽ có tập hợp các trường (field) riêng biệt:

  • Loại author: có các trường full_name, bio, email.
  • Loại article: có các trường title, content, published_date.

Mỗi tài liệu đều có một trường siêu dữ liệu (metadata field) là _type để lưu trữ tên của loại đó. Khi thực hiện tìm kiếm, người dùng có thể lọc theo loại thông qua URL:

GET blog_system/author,article/_search
{
  "query": {
    "match": {
      "username": "hoang_nam"
    }
  }
}

Tại sao Elasticsearch loại bỏ Mapping Type?

Ban đầu, Mapping Type được ví như các "bảng" (table) trong cơ sở dữ liệu quan hệ (SQL), còn Index được ví như "cơ sở dữ liệu" (database). Tuy nhiên, đây là một sự so sánh sai lầm dẫn đến những hiểu lầm về mặt kỹ thuật:

  • Xung đột trường: Trong Lucene (thư viện lõi của ES), các trường có cùng tên trong các Mapping Type khác nhau của cùng một index thực chất được lưu trữ chung trong một cấu trúc dữ liệu duy nhất. Điều này đồng nghĩa với việc nếu hai type có trường cùng tên status, chúng bắt buộc phải có cùng định dạng (mapping). Bạn không thể để status ở type A là integer và ở type B là keyword.
  • Dữ liệu thưa thớt (Sparse Data): Việc lưu trữ các loại thực thể hoàn toàn khác nhau vào cùng một index dẫn đến việc nhiều tài liệu có các trường trống (null), gây ảnh hưởng đến khả năng nén dữ liệu của Lucene và làm giảm hiệu suất tìm kiếm.

Các phương án thay thế Mapping Type

1. Tách biệt Index cho từng loại tài liệu

Thay vì lưu chung vào index blog_system, chúng ta nên tạo các index riêng biệt như blog_authorsblog_articles. Cách tiếp cận này giúp dữ liệu tập trung hơn, tối ưu hóa việc chấm điểm (scoring) và cho phép cấu hình số lượng phân đoạn (shards) phù hợp cho từng loại dữ liệu.

2. Sử dụng trường Type tùy chỉnh

Trong trường hợp số lượng tài liệu quá nhỏ và việc tạo index riêng gây lãng phí tài nguyên phân đoạn, bạn có thể tự định nghĩa một trường để phân loại. Dưới đây là cách chuyển đổi từ cấu trúc cũ sang cấu trúc mới:

Cấu trúc cũ (Trước 6.0):

PUT blog_system
{
  "mappings": {
    "author": {
      "properties": { "name": { "type": "text" } }
    },
    "article": {
      "properties": { "title": { "type": "text" } }
    }
  }
}

Cấu trúc mới (Khuyên dùng):

PUT blog_system_v2
{
  "mappings": {
    "_doc": {
      "properties": {
        "doc_type": { "type": "keyword" },
        "author_name": { "type": "text" },
        "article_title": { "type": "text" },
        "created_at": { "type": "date" }
      }
    }
  }
}

Khi truy vấn, bạn chỉ cần thêm một bộ lọc vào trường doc_type:

GET blog_system_v2/_search
{
  "query": {
    "bool": {
      "filter": [
        { "term": { "doc_type": "article" } }
      ],
      "must": [
        { "match": { "article_title": "Elasticsearch" } }
      ]
    }
  }
}

Lộ trình loại bỏ cụ thể

  • Elasticsearch 5.6: Giới thiệu tùy chọn index.mapping.single_type: true để chuẩn bị cho phiên bản 6.0.
  • Elasticsearch 6.x: Ép buộc mỗi index mới chỉ có một type. Type mặc định thường là _doc. Các index cũ từ 5.x vẫn hoạt động nhưng bị cảnh báo.
  • Elasticsearch 7.x: Deprecated (không khuyến khích) việc chỉ định type trong API. Tham số include_type_name mặc định là false.
  • Elasticsearch 8.x: Loại bỏ hoàn toàn khả năng chỉ định type trong các request.

Hướng dẫn di cư dữ liệu (Migration)

Bạn có thể sử dụng Reindex API để chuyển đổi các index đa type thành đơn type.

Trường hợp tách thành nhiều Index

POST _reindex
{
  "source": {
    "index": "old_blog_index",
    "type": "author"
  },
  "dest": {
    "index": "new_authors_index"
  }
}

Trường hợp gộp vào một Index và thêm trường phân loại

POST _reindex
{
  "source": {
    "index": "old_blog_index"
  },
  "dest": {
    "index": "unified_blog_index"
  },
  "script": {
    "source": """
      ctx._source.category = ctx._type;
      ctx._id = ctx._type + "-" + ctx._id;
      ctx._type = "_doc";
    """
  }
}

Trong đoạn mã trên, chúng ta sử dụng script để lưu giá trị _type cũ vào trường category mới, đồng thời thay đổi _id để tránh xung đột nếu các type khác nhau có cùng ID tài liệu.

Thẻ: Elasticsearch mapping-types lucene reindex-api data-migration

Đăng vào ngày 19 tháng 7 lúc 06:20