Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/email/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Php 需要MySQL优化以在EAV结构化数据上进行复杂搜索_Php_Mysql_Sql_Database Design_Entity Attribute Value - Fatal编程技术网

Php 需要MySQL优化以在EAV结构化数据上进行复杂搜索

Php 需要MySQL优化以在EAV结构化数据上进行复杂搜索,php,mysql,sql,database-design,entity-attribute-value,Php,Mysql,Sql,Database Design,Entity Attribute Value,我有一个包含EAV结构化数据的大型数据库,该数据库必须是可搜索和可分页的。我尝试了书中的每一个技巧,以使它足够快,但在某些情况下,它仍然无法在合理的时间内完成 这是我的表格结构(仅适用于相关部分,如果需要更多,请询问): 这是一个示例对象。我的数据库中大约有100万个这样的人。每个对象可能具有不同属性id的不同属性数 INSERT INTO `owner` (`owner_id`, `uid`, `status`, `created`, `updated`) VALUES (1, 'cwnzrd

我有一个包含EAV结构化数据的大型数据库,该数据库必须是可搜索和可分页的。我尝试了书中的每一个技巧,以使它足够快,但在某些情况下,它仍然无法在合理的时间内完成

这是我的表格结构(仅适用于相关部分,如果需要更多,请询问):

这是一个示例对象。我的数据库中大约有100万个这样的人。每个对象可能具有不同属性id的不同属性数

INSERT INTO `owner` (`owner_id`, `uid`, `status`, `created`, `updated`) VALUES (1, 'cwnzrdxs4dzxns47xs4tx', 'Green', NOW(), NOW());
INSERT INTO `object` (`object_id`, `type_id`, `owner_id`, `created`, `status`) VALUES (1, 1, 1, NOW(), NOW());
INSERT INTO `value` (`value_id`, `owner_id`, `attribute_id`, `object_id`, `type_id`, `value`) VALUES (1, 1, 1, 1, 1, 'Munich');
INSERT INTO `value` (`value_id`, `owner_id`, `attribute_id`, `object_id`, `type_id`, `value`) VALUES (2, 1, 2, 1, 1, 'Germany');
INSERT INTO `value` (`value_id`, `owner_id`, `attribute_id`, `object_id`, `type_id`, `value`) VALUES (3, 1, 3, 1, 1, '123');
INSERT INTO `value` (`value_id`, `owner_id`, `attribute_id`, `object_id`, `type_id`, `value`) VALUES (4, 1, 4, 1, 1, '2012-01-13');
INSERT INTO `value` (`value_id`, `owner_id`, `attribute_id`, `object_id`, `type_id`, `value`) VALUES (5, 1, 5, 1, 1, 'A cake!');

现在来看看我目前的机制。我的第一次尝试是Mysql的典型方法。在我需要的任何东西上执行一个包含大量连接的大型SQL。完全脱盐!由于内存耗尽,PHP和MySQL服务器需要很长时间才能加载,甚至崩溃

因此,我将查询分为几个步骤:

1确定所有需要的属性标识。

我可以在另一个引用对象的type_id的表中查找它们。结果是属性ID的列表。(此表与性能关系不大,因此不包括在我的示例中。)

:type_id包含我要包含在搜索中的任何对象的所有type_id。我已经在我的申请中得到了这个信息。所以这很便宜

SELECT * FROM attribute WHERE form_id IN (:type_id)
结果是一个类型为_id的整数数组

2搜索匹配对象 编译一个大的SQL查询,为我想要的每个条件添加一个内部联接。这听起来很可怕,但最终,这是最快的方法:(

一个典型的生成查询可能如下所示。限制是必要的,否则我可能会得到太多ID,结果数组会使PHP在下一个查询中爆炸或中断IN语句:

SELECT DISTINCT `version`.object_id FROM `version`
INNER JOIN `version` AS condition1 
        ON `version`.version_id = condition1.version_id 
       AND condition1.created = '2012-03-04' -- Filter by version date
INNER JOIN `value` AS condition2 
        ON `version`.version_id = condition2.version_id
       AND condition2.type_id IN (:type_id) -- try to limit joins to object types we need
       AND condition2.attribute_id = :field_id2 -- searching for a value in a specific attribute
       AND condition2.value = 'Munich' -- searching for the value 'Munich'
INNER JOIN `value` AS condition3 
        ON `version`.version_id = condition3.version_id
       AND condition3.type_id IN (:type_id) -- try to limit joins to object types we need
       AND condition3.attribute_id = :field_id3 -- searching for a value in a specific attribute
       AND condition3.value = 'Green' -- searching for the value 'Green'
WHERE `version`.type_id IN (:type_id) ORDER BY `version`.version_id DESC LIMIT 10000
结果将包含我可能需要的任何对象的所有对象ID。我选择的是对象ID,而不是版本ID,因为我需要所有版本的匹配对象,无论哪个版本匹配

3排序和页面结果 接下来,我将创建一个查询,该查询按某个属性对对象进行排序,然后分页生成的数组

SELECT DISTINCT object_id
FROM value
WHERE object_id IN (:foundObjects)
AND attribute_id = :attribute_id_to_sort
AND value > ''
ORDER BY value ASC LIMIT :limit OFFSET :offset
结果是前一次搜索中的对象ID的排序和分页列表

4获取我们的完整对象、版本和属性 在最后一步中,我将为前面的查询找到的任何对象和版本选择所有值

SELECT `value`.*, `object`.*, `version`.*, `type`.*
`object`.status AS `object.status`,
`object`.flag AS `object.flag`,
`version`.created AS `version.created`,
`version`.status AS `version.status`,
FROM version
INNER JOIN `type` ON `version`.form_id = `type`.type_id
INNER JOIN `object` ON `version`.object_id = `object`.object_id
LEFT JOIN value ON `version`.version_id = `value`.version_id
WHERE version.object_id IN (:sortedObjectIds) AND `version.type_id IN (:typeIds)
ORDER BY version.created DESC
然后,结果将通过PHP编译成尼斯对象->版本->值数组结构


现在问题是:

  • 整个混乱局面能以任何方式加速吗
  • 我可以从我的搜索查询中删除10000个限制吗
如果所有这些都失败了,也许可以切换数据库技术?请参阅我的其他问题:


真实生活样本

表大小:对象-193801行,版本-193841行,值-1053928行

SELECT * FROM attribute WHERE attribute_id IN (30)

SELECT DISTINCT `version`.object_id
FROM version  
INNER JOIN value AS condition_d4e328e33813 
     ON version.version_id = condition_d4e328e33813.version_id
    AND condition_d4e328e33813.type_id IN (30)
    AND condition_d4e328e33813.attribute_id IN (377) 
    AND condition_d4e328e33813.value LIKE '%e%'  
INNER JOIN value AS condition_2c870b0a429f 
     ON version.version_id = condition_2c870b0a429f.version_id
    AND condition_2c870b0a429f.type_id IN (30)
    AND condition_2c870b0a429f.attribute_id IN (376) 
    AND condition_2c870b0a429f.value LIKE '%s%' 
WHERE version.type_id IN (30) 
ORDER BY version.version_id DESC LIMIT 10000 -- limit to 10000 or it breaks!
说明:

id  select_type  table                   type      possible_keys                key         key_len ref                               rows      Extra   
1   SIMPLE       condition_2c870b0a429f  ref       field_id,action_id,form_id   field_id    4       const                             178639    Using where; Using temporary; Using filesort
1   SIMPLE       action                  eq_ref    PRIMARY                      PRIMARY     8       condition_2c870b0a429f.action_id  1         Using where
1   SIMPLE       condition_d4e328e33813  ref       field_id,action_id,form_id   action_id   8       action.action_id                  11        Using where; Distinct
对象搜索已完成(峰值RAM:5.91MB,时间:4.64s)

已完成对象排序(峰值RAM:6.68MB,时间:0.352s)

对象加载查询已完成(峰值RAM:6.68MB,时间:0.083s)

对象编译到数组已完成(峰值RAM:6.68MB,时间:0.007s)

只需在搜索查询之前添加解释:

EXPLAIN SELECT DISTINCT `version`.object_id FROM `version`, etc ...
然后检查“额外”列中的结果,它将为您提供一些加快查询速度的线索,比如在正确的字段中添加索引


有时,您还可以删除Inner JOIN,在Mysql响应中获得更多结果,并通过使用PHP循环进行处理来过滤大数组。

我会首先尝试使用覆盖索引(即:所有列与您查询的条件相匹配,甚至作为结果拉出)。这样,引擎就不必返回原始页面数据

由于您需要版本中的“object\u id”,并使用“version\u id”作为其他表的连接基础。您的版本表在类型\u id上也有WHERE子句,因此我会在

版本表--(对象id、版本id、类型id)

对于您的“值”表,也要匹配其中的条件


值表--(版本id、类型id、属性id、值、已创建)

大概
value\u id
没有任何意义-您可以像PK一样轻松地使用(对象id、属性id)?并且给定对象的所有者id和类型始终相同(所以在
value
表中是多余的?value\u id没有任何意义。它只是一种习惯,总是添加一个id列。它会减慢任何速度吗?很清楚,在当前的机制下,速度是公平的。问题是我将搜索结果限制为10000个返回的id。速度只是一个问题,如果我构建了一个多功能查询,但从MySQL不能做EAV数据立方体,我无论如何也做不到。至少我知道。我会用Explain添加一些现实生活中的选择。我想我添加了所有需要的索引,我是否遗漏了什么?(Explain紧跟着shorty…)无论如何,当前机制的问题不是速度,而是10000个结果的限制。
SELECT DISTINCT object_id
FROM version
WHERE object_id IN (193793,193789, ... ,135326,135324) -- 10000 ids in here!
ORDER BY created ASC
LIMIT 50 OFFSET 0                                                  
SELECT `value`.*, object.*, version.*, type.*,
    object.status AS `object.status`,
    object.flag AS `object.flag`,
    version.created AS `version.created`,
    version.status AS `version.status`,
    version.flag AS `version.flag`
FROM version
INNER JOIN type ON version.type_id = type.type_id
INNER JOIN object ON version.object_id = object.object_id
LEFT JOIN value ON version.version_id = `value`.version_id
WHERE version.object_id IN (135324,135326,...,135658,135661) AND version.type_id IN (30)
ORDER BY quality DESC, version.created DESC 
EXPLAIN SELECT DISTINCT `version`.object_id FROM `version`, etc ...