在当今的数字化时代,很多企业都有着多个业务同时开展的情况。不同的业务可能有不同的数据需求和安全要求,这就需要一种有效的方式来隔离不同业务的数据。OpenSearch 作为一款强大的开源搜索和分析引擎,就可以实现多租户功能,帮助我们很好地隔离不同业务数据。下面就来详细说说 OpenSearch 多租户实现的相关内容。

一、什么是 OpenSearch 多租户

OpenSearch 多租户其实就是让多个用户或者组织能够在同一个 OpenSearch 集群中独立地使用资源,同时保证他们的数据是相互隔离的。这就好比是一个大公寓,里面有很多个房间,每个房间住着不同的住户,住户之间的生活和物品都是相互独立的,不会相互干扰。

示例说明(Java 技术栈)

假设我们有一个电商平台,有服装业务和电子产品业务。我们可以为每个业务创建一个独立的租户。

import org.opensearch.client.opensearch.OpenSearchClient;
import org.opensearch.client.opensearch._types.mapping.TypeMapping;
import org.opensearch.client.opensearch.indices.CreateIndexRequest;
import org.opensearch.client.opensearch.indices.CreateIndexResponse;
import org.opensearch.client.transport.rest_client.RestClientTransport;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;

import java.io.IOException;

public class OpenSearchMultiTenancyExample {
    public static void main(String[] args) throws IOException {
        // 创建 OpenSearch 客户端
        RestClient restClient = RestClient.builder(
                new HttpHost("localhost", 9200, "http")).build();
        RestClientTransport transport = new RestClientTransport(
                restClient, new org.opensearch.client.json.jackson.JacksonJsonpMapper());
        OpenSearchClient client = new OpenSearchClient(transport);

        // 为服装业务创建索引(租户 1)
        CreateIndexRequest clothingIndexRequest = new CreateIndexRequest.Builder()
               .index("clothing_tenant")
               .mappings(new TypeMapping.Builder()
                       .putProperties("product_name", new org.opensearch.client.opensearch._types.mapping.TextProperty.Builder().build())
                       .putProperties("price", new org.opensearch.client.opensearch._types.mapping.DoubleProperty.Builder().build())
                       .build())
               .build();
        CreateIndexResponse clothingResponse = client.indices().create(clothingIndexRequest);
        System.out.println("Clothing index created: " + clothingResponse.acknowledged());

        // 为电子产品业务创建索引(租户 2)
        CreateIndexRequest electronicsIndexRequest = new CreateIndexRequest.Builder()
               .index("electronics_tenant")
               .mappings(new TypeMapping.Builder()
                       .putProperties("product_name", new org.opensearch.client.opensearch._types.mapping.TextProperty.Builder().build())
                       .putProperties("price", new org.opensearch.client.opensearch._types.mapping.DoubleProperty.Builder().build())
                       .build())
               .build();
        CreateIndexResponse electronicsResponse = client.indices().create(electronicsIndexRequest);
        System.out.println("Electronics index created: " + electronicsResponse.acknowledged());

        // 关闭客户端
        transport.close();
        restClient.close();
    }
}

注释:

  • 这段代码通过 Java 代码实现了在 OpenSearch 中为两个不同的业务(服装和电子产品)创建独立的索引,每个索引就相当于一个租户,它们的数据是相互隔离的。
  • 首先创建了 OpenSearch 客户端,然后分别为服装业务和电子产品业务创建索引并输出创建结果,最后关闭客户端。

二、应用场景

1. 企业级多业务平台

很多大型企业都有多个不同的业务部门,每个部门的数据都有不同的安全级别和使用需求。比如金融企业,有信贷业务、理财业务等。这些业务的数据敏感性不同,需要隔离存储,通过 OpenSearch 多租户就可以为每个业务部门创建独立的租户,保证数据的安全性和独立性。

2. SaaS 应用

SaaS(软件即服务)提供商通常会为多个客户提供服务,每个客户的数据都需要相互隔离。例如,一个在线办公 SaaS 平台,不同的企业客户使用这个平台,每个企业的文档、数据等都应该是相互隔离的,使用 OpenSearch 多租户可以轻松实现这一点。

3. 多租户数据分析平台

数据分析公司可能会为多个客户提供数据分析服务,每个客户的数据都有自己的特点和隐私要求。通过 OpenSearch 多租户,可以为每个客户创建独立的分析环境,确保数据的保密性和准确性。

三、技术优缺点

优点

1. 数据隔离

不同租户的数据存储在不同的索引或者集群中,相互之间不会受到影响。即使某个租户的数据出现问题,也不会影响其他租户的正常使用。就像开头说的公寓里的住户,一家失火了不会殃及其他家。

2. 资源共享

多个租户可以共享同一个 OpenSearch 集群的资源,这样可以降低硬件成本和维护成本。比如一个企业的多个业务部门可以共同使用一个 OpenSearch 集群,而不需要为每个部门单独搭建一套系统。

3. 灵活性高

可以根据不同租户的需求,灵活地分配资源。比如对于数据量较大、查询频繁的租户,可以分配更多的计算资源和存储资源。

缺点

1. 管理复杂度增加

需要对多个租户进行管理,包括用户权限管理、资源分配管理等。例如,要确保每个租户只能访问自己的数据,这就需要设置复杂的权限控制规则。

2. 性能影响

当多个租户同时使用集群资源时,可能会出现资源竞争的情况,从而影响性能。比如多个租户同时进行大量的数据查询,可能会导致集群响应变慢。

四、实现方式

1. 基于索引的多租户

这种方式是为每个租户创建一个独立的索引。每个索引有自己的存储和查询规则,租户之间的数据完全隔离。

示例(Java 技术栈)

import org.opensearch.client.opensearch.OpenSearchClient;
import org.opensearch.client.opensearch._types.mapping.TypeMapping;
import org.opensearch.client.opensearch.indices.CreateIndexRequest;
import org.opensearch.client.opensearch.indices.CreateIndexResponse;
import org.opensearch.client.transport.rest_client.RestClientTransport;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;

import java.io.IOException;

public class IndexBasedMultiTenancy {
    public static void main(String[] args) throws IOException {
        // 创建 OpenSearch 客户端
        RestClient restClient = RestClient.builder(
                new HttpHost("localhost", 9200, "http")).build();
        RestClientTransport transport = new RestClientTransport(
                restClient, new org.opensearch.client.json.jackson.JacksonJsonpMapper());
        OpenSearchClient client = new OpenSearchClient(transport);

        // 为租户 1 创建索引
        String tenant1Index = "tenant1_index";
        CreateIndexRequest tenant1Request = new CreateIndexRequest.Builder()
               .index(tenant1Index)
               .mappings(new TypeMapping.Builder()
                       .putProperties("data", new org.opensearch.client.opensearch._types.mapping.TextProperty.Builder().build())
                       .build())
               .build();
        CreateIndexResponse tenant1Response = client.indices().create(tenant1Request);
        System.out.println("Tenant 1 index created: " + tenant1Response.acknowledged());

        // 为租户 2 创建索引
        String tenant2Index = "tenant2_index";
        CreateIndexRequest tenant2Request = new CreateIndexRequest.Builder()
               .index(tenant2Index)
               .mappings(new TypeMapping.Builder()
                       .putProperties("data", new org.opensearch.client.opensearch._types.mapping.TextProperty.Builder().build())
                       .build())
               .build();
        CreateIndexResponse tenant2Response = client.indices().create(tenant2Request);
        System.out.println("Tenant 2 index created: " + tenant2Response.acknowledged());

        // 关闭客户端
        transport.close();
        restClient.close();
    }
}

注释:

  • 此代码通过 Java 为两个不同的租户创建了独立的索引。每个索引的名称不同,数据存储在不同的索引中,实现了数据的隔离。
  • 先创建 OpenSearch 客户端,然后分别为两个租户创建索引并输出创建结果,最后关闭客户端。

2. 基于集群的多租户

这种方式是为每个租户创建一个独立的 OpenSearch 集群。每个集群有自己独立的硬件资源和配置,租户之间的隔离更加彻底,但成本也更高。

3. 基于文档级的多租户

在这种方式下,所有租户的数据存储在同一个索引中,但是通过在文档中添加租户标识字段来区分不同租户的数据。查询时,通过过滤租户标识字段来获取特定租户的数据。

示例(Java 技术栈)

import org.opensearch.client.opensearch.OpenSearchClient;
import org.opensearch.client.opensearch._types.StructuredQuery;
import org.opensearch.client.opensearch._types.query_dsl.TermQuery;
import org.opensearch.client.opensearch.core.SearchRequest;
import org.opensearch.client.opensearch.core.SearchResponse;
import org.opensearch.client.opensearch.core.search.Hit;
import org.opensearch.client.transport.rest_client.RestClientTransport;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;

import java.io.IOException;
import java.util.List;

public class DocumentLevelMultiTenancy {
    public static void main(String[] args) throws IOException {
        // 创建 OpenSearch 客户端
        RestClient restClient = RestClient.builder(
                new HttpHost("localhost", 9200, "http")).build();
        RestClientTransport transport = new RestClientTransport(
                restClient, new org.opensearch.client.json.jackson.JacksonJsonpMapper());
        OpenSearchClient client = new OpenSearchClient(transport);

        // 构造查询特定租户数据的请求
        StructuredQuery query = TermQuery.of(t -> t
               .field("tenant_id")
               .value("tenant1")
        )._toQuery();
        SearchRequest searchRequest = new SearchRequest.Builder()
               .index("shared_index")
               .query(query)
               .build();

        // 执行查询
        SearchResponse<Object> searchResponse = client.search(searchRequest, Object.class);
        List<Hit<Object>> hits = searchResponse.hits().hits();
        for (Hit<Object> hit : hits) {
            System.out.println("Tenant 1 data: " + hit.source());
        }

        // 关闭客户端
        transport.close();
        restClient.close();
    }
}

注释:

  • 这段代码通过 Java 实现了文档级的多租户查询。在一个共享的索引中,通过过滤租户标识字段“tenant_id”来获取特定租户(这里是“tenant1”)的数据。
  • 先创建 OpenSearch 客户端,然后构造查询请求,执行查询并输出结果,最后关闭客户端。

五、注意事项

1. 权限管理

要严格设置每个租户的访问权限,确保租户只能访问自己的数据。比如,对于基于索引的多租户,要设置不同租户对不同索引的读写权限。

2. 资源监控

要实时监控集群的资源使用情况,及时调整资源分配。当某个租户的资源使用过高时,要及时进行限制或者分配更多资源。

3. 数据备份

要定期对每个租户的数据进行备份,以防数据丢失。可以使用 OpenSearch 提供的备份和恢复功能,也可以使用第三方工具。

4. 安全审计

要对租户的操作进行安全审计,记录每个租户的访问行为和操作记录。这样可以及时发现异常行为并进行处理。

六、文章总结

OpenSearch 多租户是一种非常有效的隔离不同业务数据的解决方案。它适用于企业级多业务平台、SaaS 应用和多租户数据分析平台等多种场景。它具有数据隔离、资源共享和灵活性高的优点,但也存在管理复杂度增加和可能影响性能的缺点。实现方式有基于索引、基于集群和基于文档级三种。在使用过程中,要注意权限管理、资源监控、数据备份和安全审计等问题。通过合理使用 OpenSearch 多租户,企业可以更好地管理和保护不同业务的数据,提高数据的安全性和可用性。