elasticsearch 从两个大集合中删除所有的java集合的最快方法是什么

pdtvr36n  于 2023-04-20  发布在  ElasticSearch
关注(0)|答案(3)|浏览(107)

我在一个MSSQL(主数据)中有大约600万条记录
在一个elasticsearch中约有600万条记录
我想从elasticsearch中删除数据库中不存在的记录。
当我在java中使用两个ArrayList时,它非常慢。在左手边还有一个TreeMap。我已经将超时时间增加到10分钟,但它不会在此期间内完成。在java中有什么更快的选项吗?
如果没有更快的选择,可以为此创建一个mssql表,让mssql处理diff操作。
如果一个elasticsearch有600万个clauses,可以使用“delete by query”吗?我可以只为一个query增加很多clauses吗?最好是通过url query param或者在请求体中?

c7rzv4ha

c7rzv4ha1#

在ES中执行可能是最快的,而数据库也能够非常快地执行此操作,您必须通过网络多次发送数据。
当在Java中这样做时,你需要考虑所有方法和你使用的数据结构的复杂性(因为两个列表的大小相当,为了简单起见,我假设它们的大小n相同):

2使用removeAll()或类似简单方法的列表

使用2个列表(这里甚至不讨论ArrayLists)意味着时间复杂度为O(n^n)。

DB数据的TreeMap

如果对DB数据使用TreeMap,对ES数据使用列表,那么时间复杂度为O(n * log(n))

  • 时间复杂度:O(n)
  • 遍历列表并检查TreeMap中的值的时间复杂度为O(n * log(n))

然而,列表结构在这里也发挥了作用:从ArrayList中删除元素会导致数据移动,即使你向后迭代。然而,你并不需要快速随机访问该列表,所以LinkedList会更好。
另外,使用HashMap而不是TreeMap可以改善时间,因为这基本上将构建Map变成O(n),并且也将查找变成O(n)(如果Map配置了正确的大小,则列表迭代为O(n),查找为O(1))。它将使用更多的内存,并且不允许重复(但TreeMap也不允许),因此需要考虑这一点。
示例:

List<DBRecord> dbRecords = loadFromDB();
List<ESRecord> esRecords = loadFromES();

Map<Key, DBREcord> dbMap = new HashMap<>();
dbRecords.forEach(r -> dbMap.put(r.getKey(), r));

//not needed if esRecords is a LinkedList already and you want to modify it directly
List<ESRecord> resultList = new LinkedList<>(esRecords);
for(Iterator<ESRecord> itr = resultList.iterator(); itr.hasNext(); ) {
  ESRecord esRecord = itr.next();
  DBRecord dbRecord = dbMap.get(esRecord.getKey());

  if(dbRecord != null) {
    //found a match so remove it - what's left are the records to remove
    itr.remove();
  }
}

使用2个排序列表

如果你不能使用HashMap,你可以尝试使用2个排序列表,并同时推进它们。如果你可以从ES和DB中获得预排序的列表,那么这将具有O(n)的复杂度,否则你需要首先对它们进行排序,这将使其变成O(n * log(n))。
当你对列表进行排序时,你基本上是同时迭代它们两个,应用以下逻辑:

  • 如果当前游标(例如迭代器或索引)上的元素不同,则使用较小的值推进列表的游标
  • 如果两个元素相等,你就找到了一个匹配项,那么把它从一个列表中删除,或者把它收集到某个结果列表中。

示例:

//Assumption: the lists you get are already sorted and contain at least 1 element
List<DBRecord> dbRecords = loadFromDB();
List<ESRecord> esRecords = loadFromES();

Iterator<DBRecord> dbItr = dbRecords.iterator();
Iterator<ESRecord> esItr = esRecords.iterator();

//get the first elements
DBRecord dbCurrent = dbItr.next();
ESRecord esCurrent = esItr.next();

while( dbItr.hasNext() && esItr.hasNext()) {
  int comparison = dbCurrent.getKey().compareTo(esCurrent.getKey());

  if( comparison < 0) {
    //the db record's key was smaller
    dbCurrent = dbItr.next();
  } else if ( comparison > 0) {
    //the es record's key was smaller - if you collect records to remove, do it here
    esCurrent = esItr.next();
  } else {
    //found a match

    //we're removing matches in this example
    esItr.remove();

    dbCurrent = dbItr.next();
    esCurrent = esItr.next();
  }
}

//when you're here you might not have looked at all es records but those that remain are no matches
//if you're collecting differences, keep using esItr until there are no more records
pengsaosao

pengsaosao2#

潜在解决方案:mssql DB触发器,将物理删除记录的ID放入新表+某个日期。
然后,由企业集成模式“轮询消费者”+ REST API触发的新作业将从DB中收集接下来的60.000个(默认最大子句为65xxx)ID,并通过delete by query从elasticsearch中删除它们。

ccrfmcuu

ccrfmcuu3#

使用ArrayLists或TreeMaps比较内存中的两个大型数据集可能会很慢且效率低下。提高性能的一个选项是使用数据库连接操作来比较记录。您可以在SQL Server中创建一个临时表并将Elasticsearch数据加载到其中,然后与主数据表执行连接以识别Elasticsearch表中不存在的记录。一旦识别出要删除的记录,你可以使用Elasticsearch“delete by query”API来删除它们。
如果你想用600万个子句执行“delete by query”操作,这可能不是一个可行的解决方案,因为它会导致性能问题,并可能导致Elasticsearch集群崩溃。相反,你可以将删除操作分解成更小的批次,并使用Elasticsearch Scroll API一次检索记录的子集并删除它们。
总的来说,使用SQL Server和Elasticsearch操作的组合可以帮助您有效地比较和删除两个数据集之间的记录。

相关问题