elasticsearch执行增删改document的内部原理

  客户端访问es的过程如下:

(1)当用户发送一个请求到任意一个节点,该节点就会成为coordinating node(协调节点)

(2)协调节点,对document进行路由,将请求发送到对应的primary shard 上的node

      document路由过程:

    ①客户端增删改一个document时,会同步带过来一个routing number,默认情况下时document的_id(可手动指定,也可自东生成)

    ②node会将这个routing,传入到一个hash函数中,产出一个routing的hash值,然后将hash函数产生的值对index对应primary shard的数据取余。计算出最终的primary shard

       路由算法:shard = hash(routing) % number_of_primary_shards

    ③注意:routing的值可以进行指定,比如说put /index/type/id?routing=user_id,即可以将某一类的document路由到一个shard上去,在后续进行应用级别的负载均衡,可以提升批量读取数据的性能。

   

(3)实际的node将document写完成后,同步数据到对应的replica shard

(4)coordinating node,如果发现primary shard和对应的replica shard 数据执行完成后,就返回相应结果给客户端

 

注意:任意一个节点都知道任意一个document在哪个node上。

 

 

 

### Elasticsearch 全量同步与增量同步 #### 一、概念区分 全量同步指的是将源数据库中的所有数据一次性迁移到Elasticsearch中。这种方式适用于初次构建索引或当数据量不大时的情况[^3]。 增量同步则是指只迁移自上次成功传输之后发生变化的数据条目(即新插入、更新过的记录以及被标记为已删除的项)。这种方法能够显著减少资源消耗并提高效率,尤其是在处理大规模动态变化的数据集上表现优异[^2]。 #### 二、实现方式对比 对于**全量同步**而言: - 可以利用Spring Data Elasticsearch框架完成较为复杂的数据映射关系定义,并且整个过程完全由应用程序控制而不依赖额外组件支持[^1]。 ```java // 使用 Spring Data Elasticsearch 进行全量导入的一个简单例子 @Autowired private ElasticsearchOperations operations; public void bulkImport() { List<YourEntity> entities = yourRepository.findAll(); BulkRequestBuilder bulkRequest = new BulkRequest(); for (YourEntity entity : entities) { IndexRequest indexRequest = new IndexRequest("your_index"); indexRequest.source(JSON.toJSONString(entity), XContentType.JSON); bulkRequest.add(indexRequest); } try { BulkResponse responses = bulkRequest.get(); if (!responses.hasFailures()) { System.out.println("Bulk import succeeded."); } else { System.err.println(responses.buildFailureMessage()); } } catch (Exception e) { throw new RuntimeException(e); } } ``` 而针对**增量同步**: - 阿里巴巴开发了一个名为Canal的工具来监听MySQL binlog事件流,从而捕捉到任何DDL/DML语句所引起的变化;随后这些更改会被转发至目标存储系统如Elasticsearch之中。 另一种常见的做法是在原生的关系型数据库内部设置触发器机制配合专门的日志表用于追踪变动情况,再借助于后台作业定期读取该日志并将相应改动应用到远端集群内。 ```sql CREATE TRIGGER after_insert_trigger AFTER INSERT ON `source_table` FOR EACH ROW BEGIN INSERT INTO change_log (`operation`, `table_name`, `id`) VALUES ('insert', 'source_table', NEW.id); END; ``` ```sql CREATE TRIGGER after_update_trigger AFTER UPDATE ON `source_table` FOR EACH ROW BEGIN IF NOT ((OLD.col1 <=> NEW.col1 AND OLD.col2 <=> NEW.col2)) THEN INSERT INTO change_log (`operation`, `table_name`, `id`) VALUES ('update', 'source_table', NEW.id); END IF; END; ``` ```sql CREATE TRIGGER after_delete_trigger AFTER DELETE ON `source_table` FOR EACH ROW BEGIN INSERT INTO change_log (`operation`, `table_name`, `id`) VALUES ('delete', 'source_table', OLD.id); END; ``` ```java // Java 定时任务执行变更同步逻辑片段 @Scheduled(cron="*/5 * * * * ?") // 每隔五秒运行一次 public void syncChangesToEs(){ List<Map<String, Object>> changes = getUnprocessedChangeLogs(); // 获取未处理的日志 for(Map<String,Object> logEntry : changes){ String operationType = (String)logEntry.get("operation_type"); switch(operationType.toLowerCase()){ case "insert": performInsertOperation(logEntry); break; case "update": performUpdateOperation(logEntry); break; case "delete": performDeleteOperation(logEntry); break; default: logger.warn("Unknown operation type encountered: "+operationType); } markLogAsProcessed((Integer)logEntry.get("id")); // 将此条日志状态设为已处理 } } private void performInsertOperation(Map<String, Object> entry){ YourEntity entity = convertMapToEntity(entry); IndexRequest request = new IndexRequest("target_index").id(String.valueOf(entry.get("id"))); request.source(convertObjectToJsonBytes(entity), XContentType.JSON); try{ client.index(request, RequestOptions.DEFAULT); }catch(Exception ex){ handleIndexingError(ex.getMessage(),entry); } } private void performUpdateOperation(Map<String, Object> entry){ UpdateRequest updateReq = new UpdateRequest("target_index", String.valueOf(entry.get("id"))) .doc(convertObjectToJsonBytes(entry),XContentType.JSON) .upsert(new BytesArray("{}"),XContentType.JSON); try{ client.update(updateReq,RequestOptions.DEFAULT); }catch(Exception ex){ handleErrorDuringUpdatingRecord(ex.getMessage(),entry); } } private void performDeleteOperation(Map<String, Object> entry){ DeleteRequest deleteReq = new DeleteRequest("target_index", String.valueOf(entry.get("id"))); try{ client.delete(deleteReq,RequestOptions.DEFAULT); }catch(Exception ex){ handleErrorWhileDeletingDocument(ex.getMessage(),entry); } } ``` #### 三、增删改查操作指南 无论是采用哪种同步策略,在日常维护过程中都需要掌握基本的操作技能以便及时响应业务需求: - **创建/添加文档** 可以通过发送HTTP POST请求向指定索引下新增加一份JSON格式的内容作为新的文档实例[^4]。 ```bash curl -XPOST 'http://122.51.134.139:9200/myindex/_doc' -d' { "title": "Test Document", "content": "This is a test document." }' ``` - **检索文档** 要查找特定条件下的多份或多类别的资料,则可通过GET方法携带查询字符串参数或者更高级别的DSL语法结构发起搜索命令。 ```bash curl -XGET 'http://122.51.134.139:9200/myindex/_search?q=title:test&size=10' ``` - **修改现有文档** 如果只是部分字段需要调整的话推荐使用PATCH动词加上对应的路径表达式来进行局部刷新;而对于整体替换的情形则更适合PUT动作。 ```bash # 局部更新 curl -XPATCH 'http://122.51.134.139:9200/myindex/_doc/doc_id/_update?pretty=true' -H 'Content-Type: application/json' -d ' {"script":{"inline":"ctx._source.counter += params.count","lang":"painless","params":{"count":1}}}' # 整体覆盖 curl -XPUT 'http://122.51.134.139:9200/myindex/_doc/doc_id?pretty=true' -H 'Content-Type: application/json' -d '{...}' ``` - **移除单个或批量文档** 最后就是关于清理不再需要的信息点了,这同样很简单只需要调用DELETE接口即可达成目的。 ```bash # 删除单一文件 curl -XDELETE 'http://122.51.134.139:9200/myindex/_doc/doc_id' # 批量清除满足一定规则的结果集合 curl -XPOST 'http://122.51.134.139:9200/myindex/_delete_by_query?conflicts=proceed' -H 'Content-Type: application/json' -d ' { "query": { "match_all": {} }}' ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值