在 Solr 上快速读取或更新数亿个关系
Read or update quickly hundred millions of relations on Solr
第一 post : SOLR
我和我的团队在使用 Solr 时遇到了问题,我们未能找到解决方案,但我们对此进行了很多思考。所以我们决定在这里问。
有问题:读取和更新 Element 与几百万(可能更多)Sub 之间的关系非常快-元素关系。
目前我们有这样的结构:
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
}
(我用big ID是因为符合我们的需求)
通过这种方法,我们可以很容易地得到属于元素的每个子元素(非常快,比如10-20毫秒得到 100000 子元素)。
但是,以上面的例子为例,删除每个 子元素 上的 Element "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来解决读取问题。它做到了!但是我们没有解决 insert/update 性能问题。
更新 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>
我知道这是一个复杂的问题。在我的 post 中,我搜索了一些关于这个数据系统的思考,或者对一个概念错误做出反应。不一定是完整的解决方案。
谢谢!
您从错误的角度看待这个问题。您需要在 Solr 中为 search 组织数据,而不仅仅是尝试映射您的结构。
您很可能会将您的结构扁平化(非规范化)为平面记录。而且,如果您有非常复杂的结构,您最终可能会将原始信息存储在一个单独的(图形?)数据库中,并仅从 Solr 返回 ID。原始结构越复杂,越有可能。
第一 post : SOLR
我和我的团队在使用 Solr 时遇到了问题,我们未能找到解决方案,但我们对此进行了很多思考。所以我们决定在这里问。
有问题:读取和更新 Element 与几百万(可能更多)Sub 之间的关系非常快-元素关系。
目前我们有这样的结构:
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
}
(我用big ID是因为符合我们的需求)
通过这种方法,我们可以很容易地得到属于元素的每个子元素(非常快,比如10-20毫秒得到 100000 子元素)。 但是,以上面的例子为例,删除每个 子元素 上的 Element "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来解决读取问题。它做到了!但是我们没有解决 insert/update 性能问题。
更新 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>
我知道这是一个复杂的问题。在我的 post 中,我搜索了一些关于这个数据系统的思考,或者对一个概念错误做出反应。不一定是完整的解决方案。
谢谢!
您从错误的角度看待这个问题。您需要在 Solr 中为 search 组织数据,而不仅仅是尝试映射您的结构。
您很可能会将您的结构扁平化(非规范化)为平面记录。而且,如果您有非常复杂的结构,您最终可能会将原始信息存储在一个单独的(图形?)数据库中,并仅从 Solr 返回 ID。原始结构越复杂,越有可能。