1. 程式人生 > 實用技巧 >PHP 中使用 ElasticSearch 的最佳實踐(一)

PHP 中使用 ElasticSearch 的最佳實踐(一)

PHP 中使用 ElasticSearch 的最佳實踐

引言

PHP 開發者其實使用到 ES 的情況並不多,因為開發的大多數專案可能都沒有快速模糊搜尋的需求。
即使有這樣的需求,用 MySQL 的 like 查詢,就基本可以搞定需求了。
也就沒有必要殺雞用宰牛刀,使用 ES 了。
正是在這種情況下,導致很多的 PHP 開發者都沒有接觸過 ES。
即使有一些對 ES 有興趣的,也因為 ES 中文文件的缺乏,而放棄了。
因此,接下來的這篇文章就類比 MySQL 來使用 ES,讓大多數的 PHP 開發者能使用起 ES 來。

注:ElasticSearch 在文中簡稱 ES。

實現思路

我先簡單的描述一下需求:
實現一個商品的搜尋功能,只要含有搜尋關鍵字詞的就匹配,並且按照發布時間和價格倒序。

我這裡已經安裝好 MySQL 和 ES 了。如果沒有安裝的同學,可以先自行安裝。

先在 MySQL 中建立一張資料表 product,建表語句如下:

CREATE TABLE `product` (
	`product_id` int(11) UNSIGNED PRIMARY KEY AUTO_INCREMENT COMMENT '商品 ID',
	`name` varchar(64) NOT NULL DEFAULT '' COMMENT '商品名稱',
	`sku` varchar(32) NOT NULL DEFAULT '' COMMENT '商品 SKU',
	`price` decimal(4, 2) NOT NULL DEFAULT 0 COMMENT '商品價格',
	`sales` int(11) NOT NULL DEFAULT 0 COMMENT '商品銷量',
	`date_added` datetime COMMENT '建立時間',
	`date_modified` datetime COMMENT '修改時間'
) ENGINE = InnoDB CHARACTER SET = utf8mb4;

然後對應在 ES 中建立索引 product

// 建立索引並定義屬性
PUT http://127.0.0.1/product

{
 "settings": {
     "number_of_shards": 1,
     "number_of_replicas": 1
 },
 "mappings": {
     "properties": {
         "product_id": {
             "type": "integer"
         },
         "name" : {
             "type": "text"
         },
         "sku": {
             "type": "text"
         },
         "price": {
             "type": "double"
         },
         "sales": {
             "type": "integer"
         },
         "date_added": {
             "type": "date",
             "format": "yyyy-MM-dd HH:mm:ss || yyyy-MM-dd || epoch_millis"
         },
         "date_modified": {
             "type": "date",
             "format": "yyyy-MM-dd HH:mm:ss || yyyy-MM-dd || epoch_millis"
         }
     }
 }
}

建立好索引之後,我們就從 MySQL 將資料同步到 ES,同步的方案有如下三種:
1、可以直接在儲存入 MySQL 之後,就直接寫入 ES。
2、通過 Logstash 定時,從 MySQL 資料庫中拉取資料同步到 ES。
3、可以通過第三方中介軟體(例如:canal、go-mysql-elasticsearch),拉取 MySQL 的 binlog 日誌,之後中介軟體解析日誌將資料同步到 ES。

建立好索引和新增資料之後,在業務的發展過程中,可能會增加欄位。
這裡增加一個 description 欄位。

現在 MySQL 中增加欄位到 product 資料表。

alter table `product` add column description varchar(255) default "" comment "商品描述";

然後向 ES 中的 product 索引,增加屬性欄位。

// 增加對映欄位
// http://127.0.0.1:9200/product/_mapping
{
	"properties": {
		"description": {
			"type": "text"
		}
	}
}   

業務發展到中後時期的時候,可能發現欄位越來越多了,這個時候想要刪除一些欄位。
但是,在 ES 中的 Mapping 中是不能直接刪除欄位的,只能重新建立。
很多情況,我們還是不建議去刪除欄位,因為這會增加很多不必要的成本以及帶來的風險。
如果,為了節省儲存空間,Boss 一定要刪除欄位。那就按照下面的方法,也是可以實現的。

1、建立一個新的索引
2、建立新的對映關係 mapping
3、將原索引的資料到入到新索引
4、新索引建立原索引一致的別名
5、刪除原索引

下一篇文章,我將會介紹如何在 laravel 框架中使用 ES。