ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

ELK搭建实战:ES+Kibana+Logstash集成IK分词与MySQL同步

ELK搭建实战:ES+Kibana+Logstash集成IK分词与MySQL同步 最近又帮朋友把一个老项目的搜索模块整体重构了一遍核心就是把散落在 MySQL 里的业务数据按照关键词、模糊匹配、拼音首字母这些维度的搜索需求统一收敛到一套 ELK 栈上。这次从零到一搭建下来把 ES、Kibana、Logstash 三件套加上 IK 分词、拼音分词、MySQL 同步到 ES 的完整链路又梳理了一遍。整体踩坑不少但理清楚之后发现这套组合本身并不复杂复杂的是版本匹配、分词器配置、增量同步这些细节。这篇文章就把整个搭建过程、每一步的配置和关键原理都拆开讲清楚适合刚准备上手 ELK、或者已经在用但想加入中文分词和 MySQL 同步的读者参考。1. 动手前先理清这套组合要解决什么问题很多团队一开始接触 ELK都是因为搜索需求从能用变成了要好用。MySQL 里的 LIKE %关键词% 在数据量小的时候还能忍数据量一旦上去全表扫描和无法走索引的问题就会非常明显。更关键的是LIKE 查询对中文的分词处理几乎是零用户搜笔记本匹配不到笔记本电脑而 ES 配合 IK 分词器可以把文本拆成有意义的词项再配合倒排索引搜索体验完全不一样。这次选择 ELK 而不是直接用某个云厂商的托管搜索服务原因也比较实际项目数据和 MySQL 深度绑定团队希望自己掌控索引结构、分词策略和同步频率。ELK 里的 Elasticsearch 负责存储和检索Kibana 负责可视化和管理Logstash 则承担数据管道可以把 MySQL 的数据定时拉出来写入 ES。整套组合开源、免费、社区资料多出了任何问题都能找到对应的解决方案。另外标题里特别提到的 IK 分词和拼音分词也是这次搭建的重点。IK 分词解决中文分词粒度的问题拼音分词解决用户输入拼音或首字母就能搜到中文内容的问题。这两个插件都属于 ES 的第三方分词插件安装不难但版本匹配和自定义配置才是真正坑人的地方。1.1 为什么选择 Logstash 做 MySQL 同步市面上从 MySQL 同步数据到 ES 的方案其实不少有 Canal 监听 binlog 的有 Go-mysql-elasticsearch 这种轻量工具的也有 DataX 这种批量同步框架。这次我选了 Logstash原因主要有三点。第一Logstash 本来就属于 ELK 大家庭它跟 ES 的集成是原生级别的。JDBC input 插件写起来直观不需要额外部署一套同步服务运维成本低。第二Logstash 的 filter 阶段能做很多数据清洗工作比如字段改名、类型转换、字符串拼接、时间格式化这些在同步过程中非常实用。第三Logstash 支持基于递增主键或更新时间的增量同步配合持久化的 last_run_metadata_path 文件每次只拉取新增和修改的数据线上压力小。当然 Logstash 也有不足比如同步实时性不如 Canal但这次业务场景是分钟级数据同步完全可以接受。如果以后实时性要求变高可以考虑在 Logstash 方案外面再加一层 Canal 或者直接改造成 Filebeat Kafka 的架构但那是后话。1.2 版本匹配是第一道生死线这里必须先强调一个原则ES、Kibana、Logstash 三者的主版本号必须完全一致。比如我这次用的是 7.17.x 系列那 Kibana 和 Logstash 也必须用 7.17.x不能一个大版本里混着 7.x 和 8.x。同时IK 分词器和拼音分词器的版本也要跟 ES 版本严格对应IK 官方仓库和拼音官方仓库都会按照 ES 的版本号发布对应插件包下载插件 zip 的时候一定要看清楚文件名里的版本号。注意ES 8.x 之后默认开启了安全认证跟 7.x 的配置方式差异较大。这次选择 7.17.9 是考虑到稳定性和插件的成熟度如果你手头项目对版本没有强要求7.17.x 是一个很稳的选项。8.x 也能装 IK 和拼音但配置安全证书那一步会多出不少工作。版本选错了的典型症状就是 ES 启动直接失败或者插件加载报 incompatible 错误。这个问题在安装插件章节会再细说。2. ES 安装的硬性条件与关键系统参数ES 虽然是一个 Java 应用但它不是解压完就能直接跑的。安装之前有四个系统层面的东西必须处理专用账号、JVM 堆内存、虚拟内存区域映射数、文件句柄数。这四个任何一个出问题ES 都启动不了或者启动后运行不稳定。2.1 用非 root 账号跑 ES这不是洁癖问题ES 出于安全原因禁止使用 root 用户启动这是写死在源码里的检查逻辑。如果你用 root 直接跑bin/elasticsearch会立刻报错并退出。所以安装前先创建一个专用用户useradd -m es_user passwd es_user然后把 ES 的安装目录授权给这个用户chown -R es_user:es_user /usr/local/elasticsearch后续所有 ES 相关的启动命令、插件安装命令都要用 es_user 身份执行。这一步很多人会嫌麻烦想着跳过但真的不要省这是 ES 的硬性校验省不掉的。2.2 elasticsearch.yml 里必须动手改的几项ES 的配置文件在config/elasticsearch.yml默认配置只适合本机测试部署到正式服务器至少要改动以下几项cluster.name: my-es-cluster node.name: node-1 path.data: /data/es/data path.logs: /data/es/logs network.host: 0.0.0.0 discovery.seed_hosts: [127.0.0.1] cluster.initial_master_nodes: [node-1]先说 cluster.name它决定了集群的标识同一个集群的所有节点必须使用相同的 cluster.name。node.name 是节点名单节点环境下随意起一个就行。path.data 和 path.logs 建议单独指定到数据盘不要放在系统盘因为 ES 的索引数据会越来越大系统盘通常是瓶颈。network.host 设为 0.0.0.0 表示监听所有网络接口这样其他机器上的 Kibana、Logstash 才能访问到 ES。如果是纯本机测试可以保持默认的 localhost。discovery.seed_hosts 和 cluster.initial_master_nodes 这两个参数在 7.x 里是必须的即使只有一个节点。前者用于节点发现后者用于集群初始化时选主节点。很多新手会漏掉 cluster.initial_master_nodes然后启动时看到 master not discovered yet 的报错其实就是这个参数没配好。2.3 JVM 堆内存、max_map_count 和文件句柄三个坑一个都别踩ES 默认的 JVM 堆内存是 1GB这在生产环境肯定不够用。堆内存的大小在config/jvm.options文件里设置-Xms4g -Xmx4g注意 -Xms 和 -Xmx 必须设置成相同的值避免运行过程中 JVM 动态调整堆大小带来的性能波动。堆内存建议设置为物理内存的 50% 左右同时不要超过 31GB超过 31GB 之后 JVM 的压缩指针会失效内存利用效率反而下降。比如服务器是 8GB 内存设置 4GB 堆就是比较合理的选择。虚拟内存区域映射数的问题表现为启动时报错max virtual memory areas vm.max_map_count [65530] is too low解决办法是修改系统参数sysctl -w vm.max_map_count262144如果想永久生效需要在 /etc/sysctl.conf 里加上vm.max_map_count262144然后执行sysctl -p。文件句柄数同样有硬性要求。ES 运行时会打开大量文件默认的 1024 远远不够。打开/etc/security/limits.conf追加es_user soft nofile 65536 es_user hard nofile 65536 es_user soft nproc 4096 es_user hard nproc 4096配置完后重新登录 es_user 用户用ulimit -n确认是否生效。这里要提醒一下ES 自身对文件描述符有检查如果检查不通过启动时会直接拒绝运行。完成以上配置后切换为 es_user 用户进入 ES 目录启动su - es_user cd /usr/local/elasticsearch bin/elasticsearch -d -p pid-d表示后台运行-p pid会生成一个 pid 文件。启动完成后验证一下curl http://127.0.0.1:9200返回带 cluster_name 和 version 信息的 JSON 就说明 ES 已经跑起来了。3. Kibana 和 Logstash 的接入配置要点ES 装好只是第一步接下来要把 Kibana 和 Logstash 接入进来。这两个组件的安装包同样是解压即用但配置文件里有一些细节需要特别注意。3.1 Kibana 的配置文件只改三处Kibana 的配置文件是config/kibana.yml核心改动项如下server.port: 5601 server.host: 0.0.0.0 elasticsearch.hosts: [http://127.0.0.1:9200]server.host 设置为 0.0.0.0 是让 Kibana 允许外部访问如果只在本机看可以保持 localhost。elasticsearch.hosts 指向 ES 的地址7.17 默认没开安全认证这里就不需要配置用户名密码。启动 Kibanabin/kibana默认监听 5601 端口浏览器访问http://服务器IP:5601就能看到管理界面。Kibana 刚启动的时候会初始化 index pattern 列表如果 ES 里还没有任何索引界面会显示 Getting Started 的引导页这很正常。Kibana 的 Dev Tools 控制台是后续调试搜索和分词效果的重要工具。在 Dev Tools 里可以直接写 ES 的 REST API比如查看索引结构、执行搜索、测试分词器这些操作在后面的配置验证阶段会频繁用到。3.2 Logstash 安装与 JDBC 驱动前置准备Logstash 的安装包结构跟 ES 类似但有一点必须提前确认Logstash 7.17 运行需要 Java 11 或 17如果你的服务器上有多个 Java 版本一定要在启动前设置好 JAVA_HOME 环境变量。ES 7.17 自带了一个 JDK但 Logstash 不共用这个 JDK所以最稳的做法是服务器上单独装一个 Java 11。从 MySQL 拉数据需要在 Logstash 的 JDBC input 插件里配置数据库驱动。这里有个坑MySQL 8.x 的驱动类名跟旧版本不一样。我用的是 MySQL 8.0对应的驱动 jar 包是mysql-connector-j-8.0.33.jar驱动类名是com.mysql.cj.jdbc.Driver。如果你还在用 MySQL 5.x 的旧驱动类名是com.mysql.jdbc.Driver两个类名写错任何一个都会在 Logstash 启动时报 class not found。驱动 jar 文件建议放到一个集中目录比如/usr/local/logstash/driver/下后续配置 jdbc 插件时直接引用这个路径。Logstash 的启动验证可以先用一个最简单的配置bin/logstash -e input { stdin { } } output { stdout { } }能启动并等待标准输入说明 Logstash 本身没问题。接下来再进入正式的 MySQL 同步配置。4. IK 与拼音分词器的部署和自定义索引模板ES 原生的标准分词器对中文支持很差会把一段中文拆成单个汉字或者根本无法识别词边界。比如中华人民共和国标准分词器会把它当成一整个 token而 IK 分词器可以拆成中华人民共和国、中华人民、共和国等有意义的词项。拼音分词器则是在此之上给每个中文字词生成对应的拼音或拼音首字母让用户输入拼音时也能搜到结果。4.1 安装版本匹配的插件必须是离线 zip不能直接装IK 和拼音分词器都不能通过bin/elasticsearch-plugin install直接拉取远程包需要先从官方 GitHub 仓库下载对应 ES 版本的 zip 包再用离线方式安装。IK 分词器仓库是medcl/elasticsearch-analysis-ik拼音分词器仓库是medcl/elasticsearch-analysis-pinyin。比如我用的是 ES 7.17.9下载的文件就是elasticsearch-analysis-ik-7.17.9.zip和elasticsearch-analysis-pinyin-7.17.9.zip。安装命令# 切到 es_user 用户 bin/elasticsearch-plugin install file:///path/to/elasticsearch-analysis-ik-7.17.9.zip bin/elasticsearch-plugin install file:///path/to/elasticsearch-analysis-pinyin-7.17.9.zip安装完成后会在 ES 的 plugins 目录下生成analysis-ik和analysis-pinyin两个子目录。必须重启 ES 才能生效bin/elasticsearch -d -p pid重启后先验证插件是否加载成功curl http://127.0.0.1:9200/_cat/plugins?v能列出 analysis-ik 和 analysis-pinyin 就说明安装成功。提示如果你在安装时发现版本不匹配最常见的错误是plugin [analysis-ik] is incompatible with version [7.17.9]或者在启动日志里看到AnalysisPlugin加载失败。这时候不需要怀疑配置只需要找到严格对应版本号的 zip 重新装。4.2 IK 的自定义词典和热更新方式IK 分词器默认的词库覆盖了大部分常用中文词汇但业务里一定会遇到一些专属名词。比如我这个项目里有个产品叫极光Pro默认 IK 会把极光和Pro分开搜索极光的时候还好但搜索极光Pro就会出问题。IK 的配置目录在 ES 的config/analysis-ik/IKAnalyzer.cfg.xml里。打开这个文件可以看到以下结构properties commentIK Analyzer 扩展配置/comment entry keyext_dictcustom/mydict.dic/entry entry keyext_stopwordscustom/stopword.dic/entry /properties自定义词典文件放在config/analysis-ik/custom/目录下每行一个词。比如mydict.dic里写入极光Pro 电竞椅 智能手环保存后重启 ES 才能生效。IK 也支持远程词库热更新通过remote_ext_dict配置指向一个 HTTP URL比如放在公司内部的静态资源服务器上。但远程词库的问题在于 IK 不会监听文件修改而是定时拉取一般 60 秒检查一次适合词库变化频繁的业务。考虑到这次词库变化不频繁直接用本地词典就够用了。4.3 组合 IK 和拼音的自定义 analyzer拼音分词器和 IK 分词器可以组合使用。最常用的方式是先让 IK 把中文拆分出词项再让拼音插件为每个词项生成全拼和首字母缩写的 token。但这不是简单的两个插件叠加需要在创建索引时手动指定自定义 analyzer。一个示例性的 analyzer 配置如下{ settings: { analysis: { analyzer: { ik_pinyin_analyzer: { type: custom, tokenizer: ik_max_word, filter: [my_pinyin, lowercase] } }, filter: { my_pinyin: { type: pinyin, keep_full_pinyin: true, keep_first_letter: true, keep_joined_full_pinyin: true, first_letter: prefix, keep_original: true, limit_first_letter_length: 16, lowercase: true } } } } }关键参数的含义tokenizer 使用ik_max_word最大化切分比如中华人民共和国国歌会被拆成尽可能多的合理词项。filter 里的keep_full_pinyin保留完整拼音比如中国 - zhongguo。keep_first_letter保留拼音首字母比如中国 - zg。keep_joined_full_pinyin保留全拼的拼接结果比如中国 - zhongguo。keep_original保留原始输入这样既可以按拼音搜也可以按中文原词搜。自定义 analyzer 之后怎么用它创建索引是一个需要想清楚的问题。最合理的方式是在索引模板里做配置这样在 Logstash 自动创建索引时mapping 会自动带上 analyzer。如果不用模板Logstash 自动创建的索引默认 mapping 是空配置分词器的设置根本不会生效。一个简化版的索引模板示例PUT _index_template/my_template { index_patterns: [my_search_index*], template: { settings: { number_of_shards: 1, number_of_replicas: 0, analysis: { analyzer: { ik_pinyin_analyzer: { type: custom, tokenizer: ik_max_word, filter: [my_pinyin, lowercase] } }, filter: { my_pinyin: { type: pinyin, keep_full_pinyin: true, keep_first_letter: true, keep_joined_full_pinyin: true, first_letter: prefix, keep_original: true, limit_first_letter_length: 16, lowercase: true } } } }, mappings: { properties: { title: { type: text, analyzer: ik_pinyin_analyzer, search_analyzer: ik_smart }, content: { type: text, analyzer: ik_pinyin_analyzer, search_analyzer: ik_smart } } } } }这里有个细节值得注意搜索时用ik_smart而不是ik_max_word。ik_max_word做索引时尽可能细粒度切分提高召回率而搜索时用ik_smart做粗粒度切分避免用户输入的关键词被拆得太散导致结果不准确。5. MySQL 数据同步到 ES 的完整 Logstash 配置前面的组件都准备妥当之后就是这次搭建的核心环节把 MySQL 里的数据同步到 ES。这里不单要讲配置怎么写还要把全量同步、增量同步、数据清洗、删除处理这套逻辑讲清楚。5.1 同步方案选型为什么直接上 JDBC inputJDBC input 是 Logstash 官方的输入插件它做的事情用一句话概括就是定时执行一条 SQL把查询结果转换成事件再交给后面的 filter 和 output 处理。对比 CanalJDBC input 的实时性确实弱一些但它的优势在于配置简单、无侵入。不需要修改 MySQL 的 binlog 配置不需要额外部署 Canal server 和 client只需要一个只读权限的数据库账号加上一条带 where 条件的查询 SQL 就可以跑起来。对于全量数据量和增量更新频率都可控的中小规模业务JDBC input 是最容易落地、最好维护的同步方式。数据库账号的权限建议只给 SELECT 权限。例如CREATE USER es_sync% IDENTIFIED BY your_password; GRANT SELECT ON your_database.* TO es_sync%;5.2 全量 增量同步的配置拆解下面是这次使用的完整 Logstash 配置保存为/usr/local/logstash/config/mysql-sync.confinput { jdbc { jdbc_driver_library /usr/local/logstash/driver/mysql-connector-j-8.0.33.jar jdbc_driver_class com.mysql.cj.jdbc.Driver jdbc_connection_string jdbc:mysql://127.0.0.1:3306/your_database?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalse jdbc_user es_sync jdbc_password your_password jdbc_paging_enabled true jdbc_page_size 1000 schedule */5 * * * * * use_column_value true tracking_column update_time tracking_column_type timestamp record_last_run true last_run_metadata_path /usr/local/logstash/config/last_run_metadata.txt statement SELECT id, title, content, update_time FROM your_table WHERE update_time :sql_last_value ORDER BY update_time ASC } } filter { mutate { remove_field [version, timestamp] } ruby { code event.set(update_time, event.get(update_time).strftime(%Y-%m-%d %H:%M:%S)) } } output { elasticsearch { hosts [http://127.0.0.1:9200] index my_search_index_20240601 document_id %{id} } stdout { codec json_lines } }逐个拆解关键项jdbc_paging_enabled true和jdbc_page_size 1000表示分段拉取数据避免一次性查询百万级数据把内存打爆。Logstash 在分页模式下会使用游标机制对 MySQL 的压力也比较小。schedule参数是 Logstash 的 cron 表达式。这里的写法表示每 5 秒执行一次。cron 的语法和 Linux crontab 基本一致但支持秒级别。如果要改为每小时执行一次可以写成0 * * * * *。use_column_value true意味着用某个字段的值来追踪增量位置tracking_column update_time就是用 update_time 字段记录上次同步到了哪个时间点。tracking_column_type timestamp表示追踪列是时间类型。record_last_run true会把最后一次执行的位置记录到本地文件下次启动不会从头再来。last_run_metadata_path是上次执行的位置记录文件。这个文件非常重要如果被误删Logstash 会把sql_last_value重置为初始值导致全量重新同步。最关键的是 SQL 里的条件WHERE update_time :sql_last_value。:sql_last_value是 Logstash 内置的变量它保存了上次查询的最大 update_time 值。PRIMARY KEY 自增 ID 也是一种常用的追踪方式只要把 tracking_column 改成 idSQL 里用WHERE id :sql_last_value效果类似。但 id 追踪的问题在于如果数据行被修改它的 id 不会变化所以只能新增不能更新。用 update_time 做追踪则可以同时覆盖插入和更新的场景。output里有一个容易被忽略但极其重要的配置document_id %{id}。如果不显式指定 document_idLogstash 每次同步时都会为数据生成一个随机 ID结果就是同样一条数据在 ES 里被插入多次产生大量重复文档。指定了 document_id 之后ES 会依据这个 ID 做 upsert 操作重复执行也不会产生重复数据。5.3 同步性能与字段维度上的设计全量同步的数据量如果比较大比如几十万上百万行除了开启分页之外还需要注意字段裁剪。JDBC input 的 statement 里尽量只 select 需要的字段不要用SELECT *这样可以减少网络传输和 JSON 序列化的开销。另一方面Logstash 把数据写入 ES 时是走 bulk 批量接口的不需要我们在配置里改太多参数但可以关注 Logstash 日志里的 flush 情况如果单批写入量大且 ES 响应慢可以适当调低jdbc_page_size。同步到 ES 之后的字段类型也需要在设计期想清楚。MySQL 里的 int 类型会映射成 ES 的 long 或 integerdatetime 类型会映射成 date。如果业务上需要对数值字段做聚合统计最好在索引模板的 mappings 里预先指定类型。比如价格字段如果不预定义ES 可能默认映射为 text导致后续做 range 查询时出错。在索引模板里加上price: { type: double }比后面再去 reindex 简单得多。5.4 删除数据怎么处理这是 JDBC input 的软肋JDBC input 只能同步查询出来的数据MySQL 中物理删除的数据永远不会出现在查询结果里所以 ES 中对应的文档就会残留成为脏数据。处理逻辑删除常用的有两种做法。第一种是在 MySQL 表里加is_deleted字段删除时标记为 1。同步 SQL 里只查WHERE is_deleted 0但这样只能避免新的同步带进已删除数据对已经同步进 ES 的文档无法做清理。这时需要在 Logstash 的 filter 里根据is_deleted字段动态决定动作配合 output 里的document_id用删除动作处理。具体做法可以这样SQL 里查出的数据包含is_deleted字段然后在 output 里用elasticsearch的action配置。Logstash 的action支持index、delete、update等动作。在 filter 中根据is_deleted给事件打标记filter { if [is_deleted] 1 { mutate { add_field { [metadata][action] delete } } } } output { elasticsearch { hosts [http://127.0.0.1:9200] index my_search_index_20240601 document_id %{id} action %{[metadata][action]} } }但这样会让普通行和删除行混在一起每次同步都会先执行一次查询再判断动作逻辑稍显绕。更简单的方案是单独写一个定时任务在 ES 里执行按条件删除的 API。比如提前在 ES 文档里存一个status字段定时调用curl -X POST http://127.0.0.1:9200/my_search_index_20240601/_delete_by_query -H Content-Type: application/json -d { query: { term: { status: 0 } } }这种方式适合删除频率不高的业务。核心原则是一定要想清楚物理删除怎么处理否则线上会出现大量搜得到但实际已经没了的脏数据。6. 跑通之后会遇到的实际问题记录上了这套 ELK 之后运行了一段时间也遇到了不少问题。整理几个最典型的给后面搭建的朋友做个参考。6.1 第一次全量同步后数据对不上怎么办第一次全量同步时最容易出现的问题是 ES 文档总数和 MySQL 行数对不上。原因可能在几个地方。最容易忽略的是tracking_column_type设置错误。如果表的 update_time 是 datetime 类型但配置里写成了 numericLogstash 在比较:sql_last_value时会把时间值当成数字处理导致第一次同步只拉了最近几秒的数据。解决办法是删掉 metadata 文件把tracking_column_type改为 timestamp再重新同步。另一个原因是重复条目。如果 MySQL 表没有主键或唯一索引Logstash 的document_id无法确定唯一标识ES 里可能出现重复文档。这里还是建议 MySQL 表要有明确的主键字段。排查这类问题最直接的方式是看 Logstash 日志。启动时加上--debug参数能看到每次 JDBC 查询生成的 SQL 语句和返回行数对照 MySQL 实际数据量很快能定位问题在哪一步。6.2 时间字段差 8 小时这是几乎所有 Java 应用同步 MySQL 到 ES 都会遇到的问题。MySQL 驱动在建立连接时如果没有指定serverTimezone会使用 JVM 默认时区国内服务器默认是 Asia/Shanghai但 JVM 的默认时区有时可能是 UTC。结果就是 ES 里存的时间比实际时间少了 8 小时。解决方式有两个层面JDBC 连接串里明确指定时区serverTimezoneAsia/Shanghai。Logstash input 里增加jdbc_default_timezone Asia/Shanghai。两方面都加上ES 里的时间字段就不会再出现 8 小时偏差。6.3 服务器内存高怎么办ES 是内存大户如果服务器内存不够经常会看到 RSS(驻留内存) 比 JVM 堆内存还高的情况。这通常不是因为 JVM 堆设置太大而是因为 ES 的操作系统文件缓存。ES 会尽量利用操作系统缓存来加速搜索这些缓存不归 JVM 管但会占用物理内存。如果内存长期不足优先检查 JVM 堆设置是否合理。建议把 -Xmx 控制在总内存的 50% 以内。同时可以观察 ES 的 fielddata 和 segment memory 使用情况如果是大量聚合查询导致 fielddata 内存过高可以调整 fielddata circuit breaker或者优化查询方式。还可以定期执行 segment force merge减少段的数量释放部分内存。6.4 分词效果不理想时怎么调分词效果是个需要不断打磨的事情。IK 的默认词库再丰富也一定覆盖不了所有业务场景里的专有名词和品牌词。遇到分词不理想优先走自定义词典的路线而不是换分词器。另外要注意改自定义词典之后已有的索引数据不会自动使用新词库重新分词。必须对存量数据做一次 reindex或者在测试阶段先删掉索引重建让新词库生效。如果是生产环境不想删索引就要用_update_by_query触发一次全量更新但这种方式效率较低建议在数据量可控的情况下用索引模板配合 reindex 的方式重建。还有一个容易被忽视的问题搜索时和索引时的 analyzer 必须一致。很多人在索引时用ik_max_word搜索时也直接用ik_max_word结果搜索中华人民共和国时会因为查询词被拆成多个小词项导致匹配到的文档数量异常多。合理的做法是索引时用ik_max_word保证完整词项搜索时用ik_smart粗粒度切分让匹配更精准。这个在之前的索引模板配置里已经写了这里再拿出来强调一下因为大家真的很容易在这上面绕弯。最后再分享一点个人体会。这套 ELK 组合的难点其实不在 ES 本身而是在于跟其他系统的连接处。ES 的安装、查询、聚合都有非常成熟的文档可以查真正让人花时间的往往是版本匹配、JDBC 驱动、时区问题、增量字段选择这些细节。如果在这个项目里让我重新选一次我还是会选 Logstash 做同步因为它的代码量最小、部署最简单而且 JDBC input 的增量机制足够应对大多数业务场景。下一步我可能会考虑把同步频率从秒级降到分钟级再加上索引模板的按月滚动策略让索引的维护成本更低。希望这篇文章能让准备入坑 ELK 的读者少走一些弯路。
返回列表