如何加快 MarkLogic 数据中心的协调
How to speed up Harmonization in MarkLogic Data Hub
我目前正在进行数据协调方面的开发工作。
我注意到协调的完成很慢。
我对 NoSQL / MarkLogic 开发比较陌生,不确定要遵循哪些最佳实践才能顺利、更快地协调。
以下是一些事实:
数据加载:
- 暂存中加载的数据来自使用关系数据库的 ERP 系统。数据被提取成 CSV 并加载到 MarkLogic
- 每个关系 table 数据都被提取到一个 CSV 文件中。每个 table 都在单独的实体中表示。
Post协调:
- 227,826 条记录用了大约 66 分钟来完成协调
- 1074151 条记录用了大约 4 小时 19 分钟来完成协调
协调代码片段:
- 有很多日期计算逻辑(示例如下)
function getScheduleWindowEnd(businessUnit,targetDateString,schEndDateString)
{
var scheduleWindowEnd = new String();
var preferredDate = new Date();
var startDayOfWeek = getBUStartDayOfWeek(businessUnit);
if (fn.empty(targetDateString) || targetDateString == null || targetDateString == "" ||
fn.empty(schEndDateString) || schEndDateString == null || schEndDateString == "")
{
tempScheduleWindowEnd = "";
return "";
}
else
{
targetDateString = fn.replace(targetDateString, "/", "-") ;
schEndDateString = fn.replace(schEndDateString,"/","-");
var targetDate = xs.date(targetDateString);
var schEndDate = xs.date(schEndDateString);
// Get preferred date
if (fn.empty(schEndDate))
{
preferredDate = targetDate;
}
else
{
preferredDate = schEndDate;
}
//get target day of week
var scheduledDayOfWeek = xdmp.weekdayFromDate(preferredDate);
if (scheduledDayOfWeek < startDayOfWeek)
{
scheduleWindowEnd = fn.string(addDays(preferredDate,(startDayOfWeek-scheduledDayOfWeek)));
}
else
{
scheduleWindowEnd = fn.string(addDays(preferredDate,(startDayOfWeek-scheduledDayOfWeek+7)));
}
scheduleWindowEnd = fn.replace(fn.substring(scheduleWindowEnd, 1, 10), "-", "/");
tempScheduleWindowEnd = scheduleWindowEnd;
}
return scheduleWindowEnd
}
- 主实体从其他实体中获取一些元素数据(在下面的示例中,Table2 是另一个实体)
<StatusDescription>${fn.normalizeSpace(getUDCDescription("00", "SS", fn.normalizeSpace(hl.elementText(source, "WASRST", true))))}</StatusDescription>
function getUDCDescription(drsy,drrt,drky) {
let udcRecord = cts.search(cts.andQuery([
cts.collectionQuery("ERPSystemSource"),
cts.collectionQuery("Table2"),
cts.elementWordQuery(xs.QName("DRSY"), drsy),
cts.elementWordQuery(xs.QName("DRRT"), drrt),
cts.elementWordQuery(xs.QName("DRKY"), drky)
]))
let docXML = new String();
for (const item of udcRecord) {
docXML += hl.encodeXml(fn.normalizeSpace(hl.elementText(item, "DRDL01", true)))
}
return docXML;
}
- 一些协调数据是一对一的(直接获取)。请参阅下面的示例:
<Element1>${hl.elementText(source, "WADOCO", true)}</Element1>
<Element2>${fn.normalizeSpace(hl.elementText(source, "WAMCU", true))}</Element2>
- 有很多for循环调用(非嵌套),大约20个调用。上面 #2 中的示例:
如果可能,我强烈建议您考虑与 MarkLogic 代表合作解决此问题。提高软件性能可能很复杂,最好与可以与您交流的人建立工作关系。
我经常问的第一个问题是:嗯,您期望的 SLA 是什么?在您对您认为的性能应该是什么样子有一个明确的期望之前,我不能告诉您这是慢的还是快的,或者您的期望是现实的还是不现实的。
根据我的经验,性能问题往往属于以下两类之一:软件或基础设施瓶颈。由于从 200k 到 1m 记录的时间推断似乎是线性的,我希望你的瓶颈不是严重的软件问题。
我要做的第一件事是检查 MarkLogic 监控历史并确定您是否充分利用了您的基础架构。如果不是,请尝试增加协调工作负载的线程数和批处理大小,以便充分利用您的基础架构。
如果您正在充分利用您的基础架构,您可以升级您的基础架构,或者您可以开始考虑改进您的软件。
根据您的代码,您可以参考以下几条建议来改进您的软件:
- 不过滤您的 cts.search 来电(如果可能)
- 尽可能限制 cts.search 通话。我觉得你可能每份工作做的不只是一个。
- 如果您只需要从文档中提取一个元素,请考虑使用 cts.elementValues 代替 cts.search
- 规范化 space 和其他字符串函数对大文本字符串的处理量可能很大。如果您使用的是大字符串,请考虑是否可以减少使用它们的次数。
- 我建议在文档中为您引入的每种 table 类型实现一个唯一的 XML 名称space,这样您就不需要集合查询了。
除了 Rob 的建议之外,您可能还想检查 harmonize 中的收集器步骤。看起来您正在将多个记录折叠到一个实体实例中,如果您不迭代所有记录,效果最好。迭代记录 uris 是默认的收集器实现。您可能希望将其替换为例如您正在创建的实体的某个唯一 ID 上的 cts.values。
如果您真的在迭代 uris,则可能是您重新创建同一个实体实例的次数与构成它的记录的次数一样多,这意味着会浪费很多时间。在 运行 从干净的数据库协调后,您可以通过查看您的最终数据库是否包含大量已删除的片段来检查这一点。
HTH!
我目前正在进行数据协调方面的开发工作。
我注意到协调的完成很慢。
我对 NoSQL / MarkLogic 开发比较陌生,不确定要遵循哪些最佳实践才能顺利、更快地协调。
以下是一些事实:
数据加载:
- 暂存中加载的数据来自使用关系数据库的 ERP 系统。数据被提取成 CSV 并加载到 MarkLogic
- 每个关系 table 数据都被提取到一个 CSV 文件中。每个 table 都在单独的实体中表示。
Post协调:
- 227,826 条记录用了大约 66 分钟来完成协调
- 1074151 条记录用了大约 4 小时 19 分钟来完成协调
协调代码片段:
- 有很多日期计算逻辑(示例如下)
function getScheduleWindowEnd(businessUnit,targetDateString,schEndDateString)
{
var scheduleWindowEnd = new String();
var preferredDate = new Date();
var startDayOfWeek = getBUStartDayOfWeek(businessUnit);
if (fn.empty(targetDateString) || targetDateString == null || targetDateString == "" ||
fn.empty(schEndDateString) || schEndDateString == null || schEndDateString == "")
{
tempScheduleWindowEnd = "";
return "";
}
else
{
targetDateString = fn.replace(targetDateString, "/", "-") ;
schEndDateString = fn.replace(schEndDateString,"/","-");
var targetDate = xs.date(targetDateString);
var schEndDate = xs.date(schEndDateString);
// Get preferred date
if (fn.empty(schEndDate))
{
preferredDate = targetDate;
}
else
{
preferredDate = schEndDate;
}
//get target day of week
var scheduledDayOfWeek = xdmp.weekdayFromDate(preferredDate);
if (scheduledDayOfWeek < startDayOfWeek)
{
scheduleWindowEnd = fn.string(addDays(preferredDate,(startDayOfWeek-scheduledDayOfWeek)));
}
else
{
scheduleWindowEnd = fn.string(addDays(preferredDate,(startDayOfWeek-scheduledDayOfWeek+7)));
}
scheduleWindowEnd = fn.replace(fn.substring(scheduleWindowEnd, 1, 10), "-", "/");
tempScheduleWindowEnd = scheduleWindowEnd;
}
return scheduleWindowEnd
}
- 主实体从其他实体中获取一些元素数据(在下面的示例中,Table2 是另一个实体)
<StatusDescription>${fn.normalizeSpace(getUDCDescription("00", "SS", fn.normalizeSpace(hl.elementText(source, "WASRST", true))))}</StatusDescription>
function getUDCDescription(drsy,drrt,drky) {
let udcRecord = cts.search(cts.andQuery([
cts.collectionQuery("ERPSystemSource"),
cts.collectionQuery("Table2"),
cts.elementWordQuery(xs.QName("DRSY"), drsy),
cts.elementWordQuery(xs.QName("DRRT"), drrt),
cts.elementWordQuery(xs.QName("DRKY"), drky)
]))
let docXML = new String();
for (const item of udcRecord) {
docXML += hl.encodeXml(fn.normalizeSpace(hl.elementText(item, "DRDL01", true)))
}
return docXML;
}
- 一些协调数据是一对一的(直接获取)。请参阅下面的示例:
<Element1>${hl.elementText(source, "WADOCO", true)}</Element1>
<Element2>${fn.normalizeSpace(hl.elementText(source, "WAMCU", true))}</Element2>
- 有很多for循环调用(非嵌套),大约20个调用。上面 #2 中的示例:
如果可能,我强烈建议您考虑与 MarkLogic 代表合作解决此问题。提高软件性能可能很复杂,最好与可以与您交流的人建立工作关系。
我经常问的第一个问题是:嗯,您期望的 SLA 是什么?在您对您认为的性能应该是什么样子有一个明确的期望之前,我不能告诉您这是慢的还是快的,或者您的期望是现实的还是不现实的。
根据我的经验,性能问题往往属于以下两类之一:软件或基础设施瓶颈。由于从 200k 到 1m 记录的时间推断似乎是线性的,我希望你的瓶颈不是严重的软件问题。
我要做的第一件事是检查 MarkLogic 监控历史并确定您是否充分利用了您的基础架构。如果不是,请尝试增加协调工作负载的线程数和批处理大小,以便充分利用您的基础架构。
如果您正在充分利用您的基础架构,您可以升级您的基础架构,或者您可以开始考虑改进您的软件。
根据您的代码,您可以参考以下几条建议来改进您的软件:
- 不过滤您的 cts.search 来电(如果可能)
- 尽可能限制 cts.search 通话。我觉得你可能每份工作做的不只是一个。
- 如果您只需要从文档中提取一个元素,请考虑使用 cts.elementValues 代替 cts.search
- 规范化 space 和其他字符串函数对大文本字符串的处理量可能很大。如果您使用的是大字符串,请考虑是否可以减少使用它们的次数。
- 我建议在文档中为您引入的每种 table 类型实现一个唯一的 XML 名称space,这样您就不需要集合查询了。
除了 Rob 的建议之外,您可能还想检查 harmonize 中的收集器步骤。看起来您正在将多个记录折叠到一个实体实例中,如果您不迭代所有记录,效果最好。迭代记录 uris 是默认的收集器实现。您可能希望将其替换为例如您正在创建的实体的某个唯一 ID 上的 cts.values。
如果您真的在迭代 uris,则可能是您重新创建同一个实体实例的次数与构成它的记录的次数一样多,这意味着会浪费很多时间。在 运行 从干净的数据库协调后,您可以通过查看您的最终数据库是否包含大量已删除的片段来检查这一点。
HTH!