【问题标题】:Read or update quickly hundred millions of relations on Solr在 Solr 上快速阅读或更新数以亿计的关系
【发布时间】:2017-05-04 18:38:22
【问题描述】:

第一篇文章:SOLR

我和我的团队在使用 Solr 时遇到了问题,我们未能找到解决方案,但我们对此进行了很多思考。所以我们决定在这里问。

有问题:非常快速地读取和更新具有数百万(甚至更多)子元素关系的元素之间的关系。 p>

目前我们有这样的结构:

ELEMENT 
{ 
    id: 1, 
    name : element1 
}, 
{ 
    id: 2, 
    name : element2 
}, 
[...]
{ 
    id: 10000, 
    name : element10000 
}

SUBELEMENT
{ 
    id: 1, 
    ids_elements : 1117|165|27|32|4577
    name : subelement1 
}, 
{ 
    id: 2, 
    ids_elements : 1117|416|278
    name : subelement2 
}, 
[...]
{ 
    id: 15000000, 
    ids_elements : 1117|2428|3457|5475|32|958
    name : subelement15000000 
}

(我使用大ID是因为它符合我们的需求)

通过这个方法,我们可以很容易的得到每个子元素属于一个元素(非常快,比如10-20毫秒就可以得到100000个子-元素)。 但是,以上面的示例为例,删除每个 子元素上的 元素“1117”,更新时间太长(如 10 秒更新 100000 次)。

我想要这样的结构:

ELEMENT 
{ 
    id: 1, 
    name : element1,
    ids_subelements : 1|2|3|...12458765|12458766... (few millions)
}, 
{ 
    id: 2, 
    name : element2 
    ids_subelements : 1|2|3|...12458765|12458766... (few other millions)
}, 
[...]
{ 
    id: 10000, 
    name : element10000 
    ids_subelements : 1|2|3|...12458765|12458766... (few other millions)
}

但我们超出了 Solr 的限制。

以前有人解决过这个问题吗?或者有一个想法来获得 Solr 的正确数据结构?

Solr 是这种交互的最佳解决方案吗?

感谢您的宝贵时间!

更新 1:SQL

它已经在 Solr 中非规范化了。在 Sql 规范化数据库中,我们有这样的东西(我解释它是为了更好地理解问题):

ELEMENT(id_element, name)
ELEMENT_OWN_SUBELEMENT(id_element, id_subelement)
SUBELEMENT(id_subelement, name_subelement)

所以在 Sql 中我们需要这样更新数据:

DELETE FROM element_own_subelement 
    WHERE id_element = :id_element 
    AND id_subelement = :id_subelement; 
-- (one row concerned);

DELETE FROM element_own_subelement 
    WHERE id_element = :id_element; 
-- (potentially there are millions data concerned)

DELETE FROM element_own_subelement 
    WHERE id_subelement = :id_subelement; 
-- potentially there are thousands data concerned)

插入Sql 请求也是一样。我们需要在几秒钟内在 element_own_subelement 中插入数百万条数据。

我们求助于 Solr 来解决阅读问题。它做到了!但是我们没有解决插入/更新性能问题。

更新 2:具体案例

我试图用一个具体的案例来解释我们的问题:

SQL:

-- Main data
POINT_OF_INTEREST (id_poi, name, [...])

-- Datasets & filtering
DATASET(id_dataset, name, [...])
DATASET_OWN_POINT_OF_INTEREST(id_dataset, id_poi)
FILTERING(id_filtering, id_dataset)

-- Data displaying
MODULE (id_module, name, [...])
MODULE_OWN_POINT_OF_INTEREST(id_module, id_poi)

所以:

  • 我们将数据分组到数据集中
  • 我们将数据集与模块相关联
  • 可以为模块过滤数据集
  • 我们根据过滤后的数据集定义模块的显示数据 => 只是此时我们将文档插入到 SOLR 核心中。

我们的 SOLR 核心文档示例:

SOLR core "POINT_OF_INTEREST": 
{
    id_poi : 13,
    ids_modules : 1|5|8|7|24, /* Results of the filtering */
    name : "POI thirteen",
    ids_datasets : 25|5|7
}

要从模块“24”获取数据,我必须像这样请求“point_of_interest”核心:ids_modules:24。真的很快。

但是,如果我保存一个新的过滤,我必须在我的 SOLR 核心中执行这些操作:

  • 删除“ids_modules”字段中与所有不在过滤数据结果中的文档相关的数字“24”
  • 在与过滤数据结果中的所有文档相关的字段“ids_modules”中添加数字“24”。 考虑到我们每天可能有 1 / 2 百万数据要更新多次,此操作非常缓慢。

我想解决这两个问题:

  • 如果我可以用 SOLR 比 SQL 更快地完成复杂的过滤操作
  • 如果有一种方法可以让我的数据系统正常工作,而无需系统地对 SOLR 核心进行数十万次更新

更新 3:来自 SOLR 数据库的示例

{
    "votes_moins": "",
    "id_module": "957654|1445047|1485752|1445843|1854865|1491371|1445993|1478994|1965107|1755131|1725201|1785227|1564235|1585245|1545261|1255272|1542273|1585349|1545434|1585444|1115583|1225671|1585672|1588712|1545730|1775826|1596666|1555988|1675344|1256417|16456683|1856983|1757004|12571566|1715593|1457200|1757218|1777428|172455|1845053|1058425|108594|1885677|1748751|14874647|184817|1955120|1569536|1945635|1259825|2120353|2075726|2850779|2221051|2121129|2421264|2331600|28561607|27771611|2562107|2782553|2225916|2663224|2653225|2235717|2442252|249491|2251440|265069|2365104|2687789|275048|4270620|275092|270278|65273947|257425|274451|7275509|2275811|272605|4527690|279721|2277630|2754808|278038|5280652|2080935|280599|2481710|8281161|328211145|2815958|285219|22823435|2686666|2885978|289807|294024|729044|2292156|2892216|2902128|1029256|2932089|2954401|290488|289934|306105|304509|307616|380725|3907598|3208855|3059794|3310714|311079|3151060|315536|351598",
    "adresse.altitude": 0,
    "id_objet": "1886416_0",
    "id_flux": "101|11158|32548|10365460|104686456|1024537|1024568|1054686|1844859|1986559",
    "adresse.ville": "Varangéville",
    "id_categories": "",
    "type": 5,
    "difficulte": "16|17",
    "id_circuit": "124785_0",
    "utilisateur": "u47852;PPDA Tourisme",
    "id_sous_categories": "",
    "publication_cirkwi": 1,
    "description_fr_FR": "Le Lorem Ipsum est simplement du faux texte employé dans la composition et la mise en page avant impression. Le Lorem Ipsum est le faux texte standard de l'imprimerie depuis les années 1500, quand un peintre anonyme assembla ensemble des morceaux de texte pour réaliser un livre spécimen de polices de texte. Il n'a pas fait que survivre cinq siècles, mais s'est aussi adapté à la bureautique informatique, sans que son contenu n'en soit modifié. Il a été popularisé dans les années 1960 grâce à la vente de feuilles Letraset contenant des passages du Lorem Ipsum, et, plus récemment, par son inclusion dans des applications de mise en page de texte, comme Aldus PageMaker.",
    "date_creation": 1478509817,
    "date_modification": 1473033625,
    "adresse.cp": "87456",
    "adresse.rue": "",
    "langue": "fr_FR",
    "id_locomotions": "13|48|74|18",
    "adresse.numero_rue": "",
    "votes_plus": "7852",
    "distance": 189654,
    "publication_reseau": 1,
    "pseudo_utilisateur": "PPDA1",
    "id_utilisateur_auteur": "u47852",
    "publication_prive": 0,
    "latlng": "49.1234,7.1234",
    "geo": [
      "49.1234,7.1234"
    ],
    "url_image": "[...]/mypicture.jpg",
    "stats_consultation": 20568,
    "titre_fr_FR": "Example",
    "titre_fr_FR_tri": "Example",
    "_version_": "155642045896312192"
  }

(值是假的)

性能最差的键是“id_module”和“id_flux”。 data-config.xml中的字段声明

<field column="id_module" name="id_module" />
<field column="id_flux" name="id_flux" />

myschema.xml 中的字段声明

<field name="id_module" type="splitPipeTexte" indexed="true" />
<field name="id_flux" type="splitPipeTexte" indexed="true" />

“splitPipeTexte”声明如下:

<fieldType name="splitPipeTexte" class="solr.TextField">
    <analyzer>
        <tokenizer class="solr.PatternTokenizerFactory" pattern="\|"/>
    </analyzer>
</fieldType>

我知道这是一个复杂的问题。在我的帖子中,我搜索了一些关于这个数据系统的思考,或者对概念错误做出反应。不一定是完整的解决方案。

谢谢!

【问题讨论】:

  • 您可以进行一些映射,请参阅:wiki.apache.org/solr/DataImportHandler 但是,我不确定这就是您所追求的。您能否澄清它是基础索引结构、要索引的实际数据结构还是要重新构建索引的数据视图。谢谢
  • 这看起来更像是一个经典的数据库作业,而不是 Solr 作业。您似乎没有任何文本搜索功能,那么您希望通过使用 Solr 来完成什么?
  • @LeftyGBalogh :主要问题是在 SOLR 中更新 100000 个文档的时间。我们需要另一种观点,也许这就是我们需要改变的数据结构。这就像对 Mongo link 的“一对多”/“一对多”/“一对多”的解释。

标签: solr database-performance bigdata database nosql


【解决方案1】:

你从错误的一端来看这个问题。您需要在 Solr 中组织数据以进行 搜索,而不仅仅是尝试映射您的结构。

您很可能会将您的结构扁平化(非规范化)为扁平记录。而且,如果您有非常复杂的结构,您最终可能会将原始信息存储在一个单独的(图形?)数据库中,并仅返回来自 Solr 的 ID。原始结构越复杂,这种情况的可能性就越大。

【讨论】:

  • 我编辑了最初的主题来回答你。谢谢你的意见!这真的很有帮助;)
  • 目前还不清楚您搜索的内容。你真的搜索所有你不断修改的元素吗?为什么他们需要在 Solr 中?另外,您是否测试了从头开始重新生成这些记录而不是更新它们的速度?因为更新记录需要首先从 - 相对低效的 - 存储表示中重建它。更新文档实际上是一个新文档替换旧文档
  • 所有元素都需要在 SOLR 中,因为对它们进行了复杂的搜索。我们无法在 SQL 中以适当的响应时间进行的搜索。我用新的部分更新了最初的帖子以完成我的解释。我将测试从头开始重新生成记录是否是一个好的解决方案。谢谢=)
  • 可能你的表达水平太高了。将非常具体的点存在表示为文档并复制记录怎么样?或者将一个轴(不太常见的一个)作为分层父子表示,然后将另一个轴作为单独的文档出现。不是子子子,因为整个记录树必须一起重新生成。然后您可以使用连接查询。实际上,您似乎是在手动复制 Lucene 本身的发布列表。
  • 但是这种结构能解决您的重新索引速度问题吗?因为这就是这个问题的意义所在。如果是,那么您可以进一步迭代搜索,例如尝试数字数据类型而不是字符串,可能与 docValues 结合使用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-26
  • 1970-01-01
  • 2018-03-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多