PHP 中使用 ElasticSearch 的最佳實踐
引言
PHP 開發者其實使用到 ES 的情況並不多,因為開發的大多數項目可能都沒有快速模糊搜索的需求。
即使有這樣的需求,用 MySQL 的 like 查詢,就基本可以搞定需求了。
也就沒有必要殺雞用宰牛刀,使用 ES 了。
正是在這種情況下,導致很多的 PHP 開發者都沒有接觸過 ES。
即使有一些對 ES 有興趣的,也因為 ES 中文文檔的缺乏,而放棄了。
因此,接下來的這篇文章就類比 MySQL 來使用 ES,讓大多數的 PHP 開發者能使用起 ES 來。
注:ElasticSearch 在文中簡稱 ES。
實現思路
我先簡單的描述一下需求:
實現一個商品的搜索功能,只要含有搜索關鍵字詞的就匹配,並且按照發布時間和價格倒序。
我這里已經安裝好 MySQL 和 ES 了。如果沒有安裝的同學,可以先自行安裝。
先在 MySQL 中創建一張數據表 product
,建表語句如下:
CREATE TABLE `product` (
`id` int(11) UNSIGNED PRIMARY KEY AUTO_INCREMENT COMMENT '商品 ID',
`title` varchar(64) NOT NULL DEFAULT '' COMMENT '商品名稱',
`long_title` 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 '商品銷量',
`created_at` datetime COMMENT '創建時間',
`updated_at` 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"
},
"title" : {
"type": "text"
},
"long_title" : {
"type": "text"
},
"sku": {
"type": "text"
},
"price": {
"type": "double"
},
"sales": {
"type": "integer"
},
"created_at": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss || yyyy-MM-dd || epoch_millis"
},
"updated_at": {
"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。