成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

ES+MySQL 搞模糊搜索能多秀?這套操作直接看呆!

數(shù)據(jù)庫(kù) MySQL
ES 和 MySQL 結(jié)合起來(lái)搞模糊搜索,那可真是強(qiáng)強(qiáng)聯(lián)手。MySQL 負(fù)責(zé)存儲(chǔ)完整的業(yè)務(wù)數(shù)據(jù),保證數(shù)據(jù)的完整性和一致性;ES 作為專業(yè)的搜索引擎,提供高效的模糊搜索、分詞、拼寫(xiě)糾錯(cuò)、相關(guān)性排序等功能,讓搜索體驗(yàn)更上一層樓。

兄弟們,在咱們程序員的世界里,搜索功能那可是相當(dāng)常見(jiàn)的需求。就說(shuō)電商網(wǎng)站吧,用戶想找 “白色運(yùn)動(dòng)鞋”,可能輸入 “白鞋”“運(yùn)動(dòng)鞋白”,甚至拼寫(xiě)錯(cuò)誤輸成 “白運(yùn)鞋”,這時(shí)候就需要模糊搜索來(lái)大顯身手了。而在眾多實(shí)現(xiàn)模糊搜索的技術(shù)中,Elasticsearch(以下簡(jiǎn)稱 ES)和 MySQL 是比較常用的,它們搭配起來(lái)能玩出什么花活呢?咱們今天就好好嘮嘮。

一、先聊聊 MySQL 的模糊搜索

咱先從大家熟悉的 MySQL 說(shuō)起。MySQL 里實(shí)現(xiàn)模糊搜索,最常用的就是 LIKE 關(guān)鍵字了。比如說(shuō),我們有一個(gè) products 表,里面有個(gè) name 字段,想搜索名字里包含 “手機(jī)” 的產(chǎn)品,就可以用 SELECT * FROM products WHERE name LIKE '%手機(jī)%'。這看起來(lái)挺簡(jiǎn)單的,對(duì)吧?

但是呢,LIKE 操作在使用的時(shí)候可是有不少講究的。如果我們用 LIKE '關(guān)鍵詞%',也就是前綴匹配,這時(shí)候 MySQL 是可以利用索引的,因?yàn)樗饕前凑兆址樞虼鎯?chǔ)的,前綴匹配可以快速定位到以關(guān)鍵詞開(kāi)頭的記錄。但如果是 LIKE '%關(guān)鍵詞%',也就是全模糊匹配,這時(shí)候索引就大概率失效了,會(huì)進(jìn)行全表掃描。要是表的數(shù)據(jù)量小,全表掃描還能接受,可要是數(shù)據(jù)量達(dá)到百萬(wàn)級(jí)、千萬(wàn)級(jí),那查詢速度可就慘不忍睹了,可能得好幾秒甚至更長(zhǎng)時(shí)間才能返回結(jié)果,用戶體驗(yàn)?zāi)鞘窍喈?dāng)差。

而且,MySQL 的模糊搜索還有一個(gè)問(wèn)題,就是對(duì)中文的分詞支持不太友好。比如說(shuō) “智能手機(jī)”,用戶搜索 “智能” 或者 “手機(jī)”,用 LIKE '%智能%' 或者 LIKE '%手機(jī)%' 能找到,但如果用戶搜索 “智能手”,就找不到了,因?yàn)?MySQL 不會(huì)把 “智能手機(jī)” 拆分成 “智能”“手機(jī)”“智能手” 等詞匯,它只是簡(jiǎn)單地進(jìn)行字符串匹配。

二、再看看 ES 的模糊搜索魔法

那 ES 為啥在模糊搜索方面表現(xiàn)出色呢?這就得從它的底層原理說(shuō)起了。ES 是基于 Lucene 實(shí)現(xiàn)的,而 Lucene 使用的是倒排索引。倒排索引和我們平時(shí)用的字典很像,字典是根據(jù)字的順序來(lái)查找對(duì)應(yīng)的解釋,倒排索引則是根據(jù)關(guān)鍵詞來(lái)查找包含這個(gè)關(guān)鍵詞的文檔。

ES 在處理文本的時(shí)候,會(huì)先對(duì)文本進(jìn)行分詞。分詞器就像是一個(gè)文本切割機(jī),把一段文本切成一個(gè)個(gè)的詞(術(shù)語(yǔ))。比如對(duì)于 “智能手機(jī)是一種智能的移動(dòng)設(shè)備” 這句話,分詞器可能會(huì)切成 “智能”“手機(jī)”“是”“一種”“智能”“的”“移動(dòng)”“設(shè)備” 等詞。然后,ES 會(huì)把這些詞和對(duì)應(yīng)的文檔 ID 存儲(chǔ)到倒排索引中。當(dāng)我們進(jìn)行模糊搜索時(shí),ES 會(huì)根據(jù)輸入的關(guān)鍵詞,在倒排索引中找到所有相關(guān)的詞,然后找到對(duì)應(yīng)的文檔。

ES 支持多種模糊搜索的方式,比如 fuzzy 查詢、match 查詢、query_string 查詢等。fuzzy 查詢可以允許關(guān)鍵詞有一定的拼寫(xiě)錯(cuò)誤,比如搜索 “phne”,它可能會(huì)匹配到 “phone”。match 查詢則會(huì)對(duì)輸入的關(guān)鍵詞進(jìn)行分詞,然后在倒排索引中查找匹配的詞。而且 ES 還支持分詞器的自定義,我們可以根據(jù)不同的語(yǔ)言、不同的業(yè)務(wù)需求,選擇合適的分詞器,比如中文分詞器有 ik 分詞器、jieba 分詞器等,ik 分詞器還支持自定義詞典,我們可以把一些專業(yè)術(shù)語(yǔ)、品牌名稱等添加到詞典中,讓分詞更準(zhǔn)確。

三、ES + MySQL 雙劍合璧

既然 MySQL 和 ES 各有優(yōu)缺點(diǎn),那咱們能不能把它們結(jié)合起來(lái),讓模糊搜索既高效又準(zhǔn)確呢?答案是肯定的。

(一)適用場(chǎng)景劃分

一般來(lái)說(shuō),對(duì)于數(shù)據(jù)量較小、實(shí)時(shí)性要求不高、對(duì)搜索精度要求不是特別高的場(chǎng)景,我們可以直接使用 MySQL 的模糊搜索。比如一些小型的企業(yè)官網(wǎng),產(chǎn)品數(shù)量不多,用戶搜索頻率也不高,這時(shí)候用 MySQL 就足夠了。

而對(duì)于數(shù)據(jù)量大、搜索頻率高、對(duì)搜索功能要求比較復(fù)雜(比如支持分詞、拼寫(xiě)糾錯(cuò)、相關(guān)性排序等)的場(chǎng)景,比如電商平臺(tái)、搜索引擎、新聞網(wǎng)站等,ES 就派上大用場(chǎng)了。但是呢,我們的業(yè)務(wù)系統(tǒng)往往不會(huì)只使用 ES 或者只使用 MySQL,而是兩者結(jié)合,MySQL 作為數(shù)據(jù)源,存儲(chǔ)完整的業(yè)務(wù)數(shù)據(jù),ES 作為搜索引擎,提供高效的搜索服務(wù)。

(二)數(shù)據(jù)同步

既然要結(jié)合使用,那數(shù)據(jù)同步就是一個(gè)關(guān)鍵的問(wèn)題了。我們需要把 MySQL 中的數(shù)據(jù)實(shí)時(shí)或者定時(shí)同步到 ES 中。數(shù)據(jù)同步的方式有很多種,比如通過(guò)應(yīng)用層同步、通過(guò)數(shù)據(jù)庫(kù)觸發(fā)器同步、通過(guò)中間件同步等。這里咱們重點(diǎn)說(shuō)一下通過(guò) Canal 中間件來(lái)實(shí)現(xiàn)數(shù)據(jù)同步。

Canal 是阿里巴巴開(kāi)源的一個(gè)分布式數(shù)據(jù)庫(kù)同步工具,它模擬 MySQL 主從復(fù)制的原理,監(jiān)聽(tīng) MySQL 的 binlog 文件,獲取數(shù)據(jù)的變更事件,然后將這些事件發(fā)送給消費(fèi)者,消費(fèi)者再將數(shù)據(jù)同步到 ES 中。

具體的實(shí)現(xiàn)步驟大概是這樣的:首先,我們需要在 MySQL 中開(kāi)啟 binlog 功能,并且配置好主從復(fù)制。然后,安裝 Canal 服務(wù)端,配置好要監(jiān)聽(tīng)的 MySQL 實(shí)例信息。接著,開(kāi)發(fā) Canal 客戶端,也就是消費(fèi)者,用來(lái)接收 Canal 服務(wù)端發(fā)送過(guò)來(lái)的數(shù)據(jù)變更事件。在客戶端中,我們需要解析這些事件,判斷是插入、更新還是刪除操作,然后根據(jù)操作類型對(duì) ES 中的數(shù)據(jù)進(jìn)行相應(yīng)的處理。

比如說(shuō),當(dāng) MySQL 中有一條新的產(chǎn)品數(shù)據(jù)插入時(shí),Canal 會(huì)捕獲到這個(gè)插入事件,客戶端接收到后,會(huì)從事件中獲取到新插入的數(shù)據(jù),然后將這條數(shù)據(jù)按照 ES 的文檔格式,插入到 ES 的索引中。當(dāng) MySQL 中的數(shù)據(jù)發(fā)生更新時(shí),客戶端會(huì)根據(jù)更新后的數(shù)據(jù),更新 ES 中對(duì)應(yīng)的文檔。當(dāng)數(shù)據(jù)被刪除時(shí),客戶端會(huì)刪除 ES 中對(duì)應(yīng)的文檔。

(三)搜索實(shí)現(xiàn)

在搜索的時(shí)候,我們的應(yīng)用程序會(huì)先向 ES 發(fā)送搜索請(qǐng)求,ES 處理搜索請(qǐng)求,返回相關(guān)的文檔 ID 和排序結(jié)果等信息。然后,應(yīng)用程序再根據(jù)這些文檔 ID 到 MySQL 中查詢完整的業(yè)務(wù)數(shù)據(jù),這樣就可以避免在 ES 中存儲(chǔ)過(guò)多的非搜索相關(guān)的數(shù)據(jù),減輕 ES 的存儲(chǔ)壓力。

比如說(shuō),用戶在電商平臺(tái)搜索 “筆記本電腦”,應(yīng)用程序會(huì)向 ES 發(fā)送搜索請(qǐng)求,ES 根據(jù)分詞器將 “筆記本電腦” 拆分成 “筆記本”“電腦” 等詞,然后在倒排索引中找到包含這些詞的文檔 ID,并且根據(jù)相關(guān)性算法對(duì)這些文檔進(jìn)行排序,返回給應(yīng)用程序。應(yīng)用程序拿到這些文檔 ID 后,再到 MySQL 中查詢對(duì)應(yīng)的產(chǎn)品詳情、價(jià)格、庫(kù)存等完整數(shù)據(jù),展示給用戶。

四、實(shí)戰(zhàn)案例走一波

咱們假設(shè)現(xiàn)在要開(kāi)發(fā)一個(gè)電商平臺(tái)的搜索模塊,商品表 products 中有 id、name、description、price、stock 等字段。我們需要實(shí)現(xiàn)對(duì)商品名稱和描述的模糊搜索,同時(shí)支持價(jià)格和庫(kù)存的過(guò)濾,還要根據(jù)相關(guān)性對(duì)搜索結(jié)果進(jìn)行排序。

(一)MySQL 表結(jié)構(gòu)設(shè)計(jì)

CREATE TABLE products (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(200) NOT NULL,
    description TEXT,
    price DECIMAL(10, 2) NOT NULL,
    stock INT NOT NULL,
    create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

(二)ES 索引設(shè)計(jì)

我們創(chuàng)建一個(gè)名為 products 的索引,設(shè)置合適的映射(Mapping)。對(duì)于 name 和 description 字段,我們使用 text 類型,并指定分詞器為 ik 分詞器,同時(shí)為了支持精確查詢,再添加一個(gè) keyword 子字段。

{
  "mappings": {
    "properties": {
      "id": { "type": "keyword" },
      "name": {
        "type": "text",
        "analyzer": "ik_max_word",
        "fields": {
          "keyword": { "type": "keyword" }
        }
      },
      "description": {
        "type": "text",
        "analyzer": "ik_max_word"
      },
      "price": { "type": "scaled_float", "scaling_factor": 100 },
      "stock": { "type": "integer" },
      "create_time": { "type": "date" },
      "update_time": { "type": "date" }
    }
  }
}

(三)數(shù)據(jù)同步代碼(以 Java 為例)

這里使用 Canal 的 Java 客戶端來(lái)實(shí)現(xiàn)數(shù)據(jù)同步。首先引入 Canal 客戶端的依賴:

<dependency>
    <groupId>com.alibaba.otter</groupId>
    <artifactId>canal.client</artifactId>
    <version>1.1.5</version>
</dependency>

然后編寫(xiě) Canal 客戶端代碼:

public class CanalClient {
    private static final String SERVER_IP = "127.0.0.1";
    private static final int PORT = 11111;
    private static final String DESTINATION = "example";
    public static void main(String[] args) {
        CanalConnector connector = CanalConnectors.newClusterConnector(SERVER_IP, PORT, DESTINATION, "", "");
        connector.connect();
        connector.subscribe(".*\\..*");
        connector.rollback();
        while (true) {
            Message message = connector.get(100);
            if (message.getEntries().isEmpty()) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                continue;
            }
            for (CanalEntry.Entry entry : message.getEntries()) {
                if (entry.getEntryType() == CanalEntry.EntryType.TRANSACTIONBEGIN || entry.getEntryType() == CanalEntry.EntryType.TRANSACTIONEND) {
                    continue;
                }
                CanalEntry.RowChange rowChange;
                try {
                    rowChange = CanalEntry.RowChange.parseFrom(entry.getStoreValue());
                } catch (Exception e) {
                    throw new RuntimeException("解析 rowChange 失敗", e);
                }
                for (CanalEntry.RowData rowData : rowChange.getRowDatasList()) {
                    if (rowChange.getEventType() == CanalEntry.EventType.INSERT || rowChange.getEventType() == CanalEntry.EventType.UPDATE || rowChange.getEventType() == CanalEntry.EventType.DELETE) {
                        // 處理數(shù)據(jù)變更
                        handleRowData(rowData, rowChange.getEventType());
                    }
                }
            }
        }
    }
    private static void handleRowData(CanalEntry.RowData rowData, CanalEntry.EventType eventType) {
        // 獲取表名
        String tableName = rowData.getTable();
        if (!"products".equals(tableName)) {
            return;
        }
        // 根據(jù)事件類型處理數(shù)據(jù)
        if (eventType == CanalEntry.EventType.INSERT || eventType == CanalEntry.EventType.UPDATE) {
            // 插入或更新數(shù)據(jù),獲取新數(shù)據(jù)
            List<CanalEntry.Column> columnsList = rowData.getAfterColumnsList();
            Map<String, Object> data = new HashMap<>();
            for (CanalEntry.Column column : columnsList) {
                data.put(column.getName(), column.getValue());
            }
            // 將數(shù)據(jù)同步到 ES
            syncToES(data, eventType == CanalEntry.EventType.UPDATE);
        } else if (eventType == CanalEntry.EventType.DELETE) {
            // 刪除數(shù)據(jù),獲取舊數(shù)據(jù)中的 id
            List<CanalEntry.Column> columnsList = rowData.getBeforeColumnsList();
            String id = null;
            for (CanalEntry.Column column : columnsList) {
                if ("id".equals(column.getName())) {
                    id = column.getValue();
                    break;
                }
            }
            if (id != null) {
                // 從 ES 中刪除對(duì)應(yīng)文檔
                deleteFromES(id);
            }
        }
    }
    private static void syncToES(Map<String, Object> data, boolean isUpdate) {
        // 這里編寫(xiě)將數(shù)據(jù)同步到 ES 的代碼,使用 Elasticsearch Java 客戶端
        // 例如,構(gòu)建一個(gè) Document,設(shè)置 ID 為 data 中的 id
        // 如果是更新,使用 update 方法;如果是插入,使用 index 方法
        // 這里只是一個(gè)示例,實(shí)際代碼需要根據(jù)具體的 ES 客戶端版本和配置來(lái)編寫(xiě)
        String id = data.get("id").toString();
        // 創(chuàng)建客戶端連接 ES
        RestHighLevelClient client = EsClientFactory.getClient();
        try {
            IndexRequest request = new IndexRequest("products");
            request.id(id);
            // 將 data 轉(zhuǎn)換為 JSON 對(duì)象
            JSONObject jsonObject = new JSONObject(data);
            request.source(jsonObject.toJSONString(), XContentType.JSON);
            if (isUpdate) {
                // 更新操作,這里其實(shí)應(yīng)該用 UpdateRequest,但為了簡(jiǎn)單示例,先這樣寫(xiě),實(shí)際需要根據(jù)業(yè)務(wù)調(diào)整
                client.index(request, RequestOptions.DEFAULT);
            } else {
                client.index(request, RequestOptions.DEFAULT);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 關(guān)閉客戶端,這里簡(jiǎn)化處理,實(shí)際應(yīng)使用連接池等方式管理客戶端
            try {
                client.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    private static void deleteFromES(String id) {
        // 編寫(xiě)從 ES 中刪除文檔的代碼
        RestHighLevelClient client = EsClientFactory.getClient();
        try {
            DeleteRequest request = new DeleteRequest("products", id);
            client.delete(request, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                client.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

(四)搜索接口實(shí)現(xiàn)

在應(yīng)用程序中,我們編寫(xiě)一個(gè)搜索接口,接收用戶的搜索關(guān)鍵詞、價(jià)格范圍、庫(kù)存狀態(tài)等參數(shù),然后構(gòu)建 ES 查詢請(qǐng)求。

public class SearchService {
    public List<Product> search(String keyword, double minPrice, double maxPrice, int minStock) {
        RestHighLevelClient client = EsClientFactory.getClient();
        List<Product> products = new ArrayList<>();
        try {
            // 構(gòu)建布爾查詢
            BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
            // 模糊搜索名稱和描述
            MultiMatchQueryBuilder multiMatchQuery = QueryBuilders.multiMatchQuery(keyword, "name", "description")
               .field("name", 2) // 給名稱字段更高的權(quán)重
               .analyzer("ik_max_word")
               .minimumShouldMatch("75%"); // 至少匹配 75% 的分詞
            boolQuery.must(multiMatchQuery);
            // 價(jià)格過(guò)濾
            if (minPrice > 0 || maxPrice > 0) {
                RangeQueryBuilder priceRangeQuery = QueryBuilders.rangeQuery("price")
                   .from(minPrice).to(maxPrice);
                boolQuery.filter(priceRangeQuery);
            }
            // 庫(kù)存過(guò)濾
            if (minStock > 0) {
                RangeQueryBuilder stockRangeQuery = QueryBuilders.rangeQuery("stock")
                   .from(minStock).to(Integer.MAX_VALUE);
                boolQuery.filter(stockRangeQuery);
            }
            // 構(gòu)建搜索請(qǐng)求
            SearchRequest searchRequest = new SearchRequest("products");
            searchRequest.source(new SearchSourceBuilder()
               .query(boolQuery)
               .sort("score", SortOrder.DESC) // 根據(jù)相關(guān)性得分排序
               .size(100)); // 返回前 100 條結(jié)果
            SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
            // 解析搜索結(jié)果
            for (SearchHit hit : searchResponse.getHits().getHits()) {
                String id = hit.getId();
                Map<String, Object> sourceAsMap = hit.getSourceAsMap();
                Product product = new Product();
                product.setId(Long.parseLong(id));
                product.setName((String) sourceAsMap.get("name"));
                product.setDescription((String) sourceAsMap.get("description"));
                product.setPrice((Double) sourceAsMap.get("price"));
                product.setStock((Integer) sourceAsMap.get("stock"));
                products.add(product);
            }
            // 根據(jù)文檔 ID 到 MySQL 中查詢完整數(shù)據(jù),這里簡(jiǎn)化處理,假設(shè) ES 中已經(jīng)存儲(chǔ)了完整數(shù)據(jù),實(shí)際應(yīng)根據(jù)業(yè)務(wù)需求調(diào)整
            // 這里只是示例,實(shí)際項(xiàng)目中可能需要根據(jù) ID 到 MySQL 中查詢更詳細(xì)的信息,比如庫(kù)存是否有變化等
            // 但為了搜索效率,通常會(huì)在 ES 中存儲(chǔ)需要展示的基本信息,完整數(shù)據(jù)在需要詳情時(shí)再?gòu)?MySQL 中查詢
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                client.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return products;
    }
}

五、踩坑指南

(一)分詞器選擇不當(dāng)

在 ES 中,分詞器的選擇非常重要。如果選擇了不適合中文的分詞器,比如默認(rèn)的標(biāo)準(zhǔn)分詞器,對(duì)中文的分詞效果就會(huì)很差,可能會(huì)把 “智能手機(jī)” 分成 “智能”“手機(jī)”,也可能分成 “智”“能”“手”“機(jī)”,這就會(huì)影響搜索的準(zhǔn)確性。所以一定要根據(jù)業(yè)務(wù)需求選擇合適的分詞器,比如 ik 分詞器,并且可以自定義詞典,把一些品牌名、專業(yè)術(shù)語(yǔ)等添加進(jìn)去,讓分詞更準(zhǔn)確。

(二)數(shù)據(jù)同步延遲

使用 Canal 進(jìn)行數(shù)據(jù)同步時(shí),可能會(huì)存在一定的延遲。比如 MySQL 中數(shù)據(jù)已經(jīng)更新了,但 ES 中還沒(méi)有同步過(guò)來(lái),這時(shí)候用戶搜索可能就會(huì)找不到最新的數(shù)據(jù)。為了解決這個(gè)問(wèn)題,我們可以在業(yè)務(wù)允許的范圍內(nèi),設(shè)置合適的同步延遲容忍時(shí)間,或者在一些對(duì)實(shí)時(shí)性要求非常高的場(chǎng)景,采用雙寫(xiě)的方式,即在更新 MySQL 數(shù)據(jù)的同時(shí),同步更新 ES 數(shù)據(jù),但雙寫(xiě)要注意事務(wù)的一致性,避免出現(xiàn)數(shù)據(jù)不一致的問(wèn)題。

(三)ES 集群配置不合理

如果 ES 集群的節(jié)點(diǎn)數(shù)量、分片數(shù)、副本數(shù)等配置不合理,可能會(huì)導(dǎo)致搜索性能下降、集群不穩(wěn)定等問(wèn)題。比如分片數(shù)設(shè)置過(guò)多,會(huì)增加集群的管理成本和資源消耗;分片數(shù)設(shè)置過(guò)少,會(huì)影響搜索的并發(fā)處理能力。所以需要根據(jù)數(shù)據(jù)量、查詢并發(fā)量等因素,合理配置 ES 集群的參數(shù)。

(四)MySQL 索引優(yōu)化不足

雖然我們?cè)?ES 中進(jìn)行模糊搜索,但 MySQL 作為數(shù)據(jù)源,在查詢完整數(shù)據(jù)時(shí),如果表的索引設(shè)計(jì)不合理,也會(huì)影響查詢速度。比如在根據(jù)文檔 ID 到 MySQL 中查詢數(shù)據(jù)時(shí),如果 ID 字段沒(méi)有建立索引,或者其他常用查詢字段沒(méi)有建立索引,就會(huì)導(dǎo)致查詢緩慢。所以要對(duì) MySQL 的表進(jìn)行合理的索引優(yōu)化,確保常用的查詢操作能夠利用索引快速執(zhí)行。

六、總結(jié)

ES 和 MySQL 結(jié)合起來(lái)搞模糊搜索,那可真是強(qiáng)強(qiáng)聯(lián)手。MySQL 負(fù)責(zé)存儲(chǔ)完整的業(yè)務(wù)數(shù)據(jù),保證數(shù)據(jù)的完整性和一致性;ES 作為專業(yè)的搜索引擎,提供高效的模糊搜索、分詞、拼寫(xiě)糾錯(cuò)、相關(guān)性排序等功能,讓搜索體驗(yàn)更上一層樓。

當(dāng)然,在實(shí)際應(yīng)用中,我們需要根據(jù)業(yè)務(wù)場(chǎng)景的特點(diǎn),合理劃分兩者的使用場(chǎng)景,處理好數(shù)據(jù)同步、搜索實(shí)現(xiàn)等關(guān)鍵問(wèn)題,同時(shí)也要注意各種可能出現(xiàn)的坑,做好優(yōu)化和監(jiān)控。

通過(guò)這樣的組合,我們可以實(shí)現(xiàn)一個(gè)既高效又準(zhǔn)確的模糊搜索系統(tǒng),讓用戶在搜索時(shí)能夠快速找到想要的內(nèi)容,提升用戶體驗(yàn)。而且,隨著業(yè)務(wù)的發(fā)展和數(shù)據(jù)量的增長(zhǎng),這種架構(gòu)也具有一定的擴(kuò)展性和靈活性,可以方便地進(jìn)行集群擴(kuò)展和功能升級(jí)。

所以,下次再遇到模糊搜索的需求,別再糾結(jié)只用 MySQL 還是只用 ES 了,試試它們的組合,說(shuō)不定會(huì)有驚喜哦!

責(zé)任編輯:武曉燕 來(lái)源: 石杉的架構(gòu)筆記
相關(guān)推薦

2020-04-13 15:25:01

MySQL數(shù)據(jù)庫(kù)模糊搜索

2023-04-06 10:08:33

AI哈利波特

2017-02-07 15:55:24

4G網(wǎng)速運(yùn)營(yíng)商

2024-04-26 11:16:28

MySQL數(shù)據(jù)庫(kù)

2023-12-22 08:00:00

2024-03-12 08:10:53

MySQLPostgreSQL呆瓜模式

2023-06-24 10:44:34

Linux文件搜索

2023-10-10 13:42:30

美圖秀秀AI工作流

2025-05-07 09:32:00

2020-05-13 14:15:13

戴爾

2025-05-23 09:30:57

2018-08-20 16:34:23

2024-01-25 10:37:33

MySQL數(shù)據(jù)庫(kù)ES

2022-09-30 19:32:36

ES面試查詢

2022-08-02 18:51:13

數(shù)據(jù)產(chǎn)品MySQL宕機(jī)

2015-10-19 17:33:15

樂(lè)視云

2021-08-02 09:01:29

PythonMySQL 數(shù)據(jù)庫(kù)

2023-03-29 09:53:32

5G網(wǎng)絡(luò)

2017-11-01 21:50:30

iPhone

2022-11-07 09:25:02

Kafka存儲(chǔ)架構(gòu)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 另类视频区 | 国产精品v | 成人激情视频免费在线观看 | 最新超碰 | 欧美区日韩区 | 中文字幕亚洲一区二区三区 | 久草网在线视频 | 国产男女猛烈无遮掩视频免费网站 | 精品国产乱码久久久久久闺蜜 | 国产精品一区久久久 | 色网站入口 | 亚洲成人在线网 | 精品国产18久久久久久二百 | 欧美精品在线观看 | 草草草久久久 | 精品av| 欧美精品综合在线 | 成人性生交大免费 | 日韩免费网 | 免费成人在线网站 | 亚洲影音 | 日本国产高清 | 国产一区二区在线91 | 日本 欧美 三级 高清 视频 | 日韩综合网 | 欧美在线高清 | 国产美女在线播放 | 精品国产乱码久久久久久久久 | 国产成人久久精品一区二区三区 | 精精国产xxxx视频在线播放7 | 四虎最新 | 97avcc| 国产精品亚洲欧美日韩一区在线 | 精品综合视频 | 九九免费在线视频 | 欧美一区二区三区 | 成人在线小视频 | 欧美成人免费在线视频 | 久草久| 福利视频日韩 | 欧美激情综合 |