2

编辑:我现在已经解决了这个问题。感谢里克詹姆斯的帮助!另外:它不是解决方案的一部分,但是您对前缀索引是 100% 正确的。当我把它们拿出来时,性能实际上略有上升。

. . .

我遇到了一个奇怪的数据库问题,我无法解决这个问题,我希望这个聪明的蜂巢思维可以帮助我。简而言之,我发现尽管格式相同,但对我的数据库的一些查询非常慢,而其他查询几乎是即时的。例如,这个查询:

SELECT SQL_NO_CACHE DISTINCT pr.Master_Person_ID
FROM liverpool.person_record pr 
JOIN liverpool.person_property_view ppv1 ON (pr.Master_Person_ID = ppv1.Master_Person_ID)
JOIN liverpool.property_type_class ptc1 ON (ptc1.Property_ID = ppv1.Property_ID)
JOIN liverpool.person_property_view ppv2 ON (pr.Master_Person_ID = ppv2.Master_Person_ID)
JOIN liverpool.property_type_class ptc2 ON (ptc2.Property_ID = ppv2.Property_ID)
WHERE ptc1.Property_Class_ID = 2
AND ppv1.Property_Value = 'Ruth'
AND ptc2.Property_Class_ID = 6
AND ppv2.Property_Value = 'Davies'
ORDER BY pr.Year_From_Origin_Record, pr.Recorded_Date
LIMIT 100000;

在 0.06 秒内返回结果。足以满足我的需求。但是这个查询:

SELECT SQL_NO_CACHE DISTINCT pr.Master_Person_ID
FROM liverpool.person_record pr
JOIN liverpool.person_property_view ppv1 ON (pr.Master_Person_ID = ppv1.Master_Person_ID)
JOIN liverpool.property_type_class ptc1 ON (ptc1.Property_ID = ppv1.Property_ID)
JOIN liverpool.person_property_view ppv2 ON (pr.Master_Person_ID = ppv2.Master_Person_ID)
JOIN liverpool.property_type_class ptc2 ON (ptc2.Property_ID = ppv2.Property_ID)
WHERE ptc1.Property_Class_ID = 2
AND ppv1.Property_Value = 'Edward'
AND ptc2.Property_Class_ID = 6
AND ppv2.Property_Value = 'Abbott'
ORDER BY pr.Year_From_Origin_Record, pr.Recorded_Date
LIMIT 100000;

这里唯一的区别在于搜索参数。但是第二个查询需要9 多分钟才能执行。如果使用“LIKE”而不是“=”则更长。当然,我的数据库中的 'Edward's' 比 'Ruth's' 多,但仅凭这一点肯定不能解释为什么第二个查询比第一个慢几个数量级?如您所见,该查询使用自联接。我很欣赏这些可能不是最有效的方法,但它们可以满足我的需要,并使我的前端代码更简单。大多数时候,它们工作得很好。

这是第一个(快速)查询的解释:

id,select_type,table,partitions,type,possible_keys,key,key_len,ref,rows,filtered,Extra
1,SIMPLE,ptc1,NULL,ref,"PRIMARY,Property_ID_IDX,Property_Class_ID_IDX",Property_Class_ID_IDX,4,const,2,100.00,"Using index; Using temporary; Using filesort"
1,SIMPLE,pt,NULL,eq_ref,PRIMARY,PRIMARY,4,liverpool.ptc1.Property_ID,1,100.00,NULL
1,SIMPLE,rlt,NULL,eq_ref,PRIMARY,PRIMARY,4,liverpool.pt.Record_Link_Type_ID,1,100.00,"Using where; Using index"
1,SIMPLE,prp,NULL,ref,"PRIMARY,Property_Value_IDX,Person_Record_ID_IDX",Property_Value_IDX,23,"const,liverpool.ptc1.Property_ID",13,100.00,"Using where"
1,SIMPLE,pr,NULL,eq_ref,"PRIMARY,Master_Person_ID_IDX,Person_Record_ID_IDX",PRIMARY,4,liverpool.prp.Person_Record_ID,1,100.00,"Using where"
1,SIMPLE,rt,NULL,eq_ref,"PRIMARY,Record_Type_ID",PRIMARY,4,liverpool.pr.Record_Type_ID,1,100.00,"Using index"
1,SIMPLE,pr,NULL,ref,Master_Person_ID_IDX,Master_Person_ID_IDX,17,liverpool.pr.Master_Person_ID,1,100.00,NULL
1,SIMPLE,pr,NULL,ref,"PRIMARY,Master_Person_ID_IDX,Person_Record_ID_IDX",Master_Person_ID_IDX,17,liverpool.pr.Master_Person_ID,1,100.00,Distinct
1,SIMPLE,rt,NULL,eq_ref,"PRIMARY,Record_Type_ID",PRIMARY,4,liverpool.pr.Record_Type_ID,1,100.00,"Using index; Distinct"
1,SIMPLE,ptc2,NULL,ref,"PRIMARY,Property_ID_IDX,Property_Class_ID_IDX",Property_Class_ID_IDX,4,const,5,100.00,"Using index; Distinct"
1,SIMPLE,pt,NULL,eq_ref,PRIMARY,PRIMARY,4,liverpool.ptc2.Property_ID,1,100.00,Distinct
1,SIMPLE,rlt,NULL,eq_ref,PRIMARY,PRIMARY,4,liverpool.pt.Record_Link_Type_ID,1,100.00,"Using where; Using index; Distinct"
1,SIMPLE,prp,NULL,eq_ref,"PRIMARY,Property_Value_IDX,Person_Record_ID_IDX",PRIMARY,8,"liverpool.ptc2.Property_ID,liverpool.pr.Person_Record_ID",1,5.00,"Using where; Distinct"

这是第二个(慢)查询的解释:

id,select_type,table,partitions,type,possible_keys,key,key_len,ref,rows,filtered,Extra
1,SIMPLE,ptc1,NULL,ref,"PRIMARY,Property_ID_IDX,Property_Class_ID_IDX",Property_Class_ID_IDX,4,const,2,100.00,"Using index; Using temporary; Using filesort"
1,SIMPLE,pt,NULL,eq_ref,PRIMARY,PRIMARY,4,liverpool.ptc1.Property_ID,1,100.00,NULL
1,SIMPLE,rlt,NULL,eq_ref,PRIMARY,PRIMARY,4,liverpool.pt.Record_Link_Type_ID,1,100.00,"Using where; Using index"
1,SIMPLE,prp,NULL,ref,"PRIMARY,Property_Value_IDX,Person_Record_ID_IDX",Property_Value_IDX,23,"const,liverpool.ptc1.Property_ID",13,100.00,"Using where"
1,SIMPLE,pr,NULL,eq_ref,"PRIMARY,Master_Person_ID_IDX,Person_Record_ID_IDX",PRIMARY,4,liverpool.prp.Person_Record_ID,1,100.00,"Using where"
1,SIMPLE,rt,NULL,eq_ref,"PRIMARY,Record_Type_ID",PRIMARY,4,liverpool.pr.Record_Type_ID,1,100.00,"Using index"
1,SIMPLE,pr,NULL,ref,Master_Person_ID_IDX,Master_Person_ID_IDX,17,liverpool.pr.Master_Person_ID,1,100.00,NULL
1,SIMPLE,pr,NULL,ref,"PRIMARY,Master_Person_ID_IDX,Person_Record_ID_IDX",Master_Person_ID_IDX,17,liverpool.pr.Master_Person_ID,1,100.00,Distinct
1,SIMPLE,rt,NULL,eq_ref,"PRIMARY,Record_Type_ID",PRIMARY,4,liverpool.pr.Record_Type_ID,1,100.00,"Using index; Distinct"
1,SIMPLE,ptc2,NULL,ref,"PRIMARY,Property_ID_IDX,Property_Class_ID_IDX",Property_Class_ID_IDX,4,const,5,100.00,"Using index; Distinct"
1,SIMPLE,pt,NULL,eq_ref,PRIMARY,PRIMARY,4,liverpool.ptc2.Property_ID,1,100.00,Distinct
1,SIMPLE,rlt,NULL,eq_ref,PRIMARY,PRIMARY,4,liverpool.pt.Record_Link_Type_ID,1,100.00,"Using where; Using index; Distinct"
1,SIMPLE,prp,NULL,eq_ref,"PRIMARY,Property_Value_IDX,Person_Record_ID_IDX",PRIMARY,8,"liverpool.ptc2.Property_ID,liverpool.pr.Person_Record_ID",1,5.00,"Using where; Distinct"

我知道这几乎是不可能阅读的,但我一生都无法弄清楚如何将表格布局中的任何内容粘贴/导入到该站点中...

重要的是,据我所知,这两个 EXPLAIN 显示了功能相同的查询计划!然而,一个比另一个快得多。计划者如何订购这些陈述可能有什么问题吗?我对 SQL 相当有能力,但是这个查询规划器/索引的东西对我来说对黑魔法的研究有点太远了。有人可以帮忙吗?

我试过添加和删除索引。我尝试使用 FORCE INDEX 重写查询,但这只会让它们变慢。我在这里束手无策。

我唯一能想到的可能是,如果自连接的两侧都足够大(即,搜索一个非常常见的名字和一个非常常见的姓氏),那么两者的组合会溢出一些内存缓冲区某处,而是在磁盘上处理。这似乎是唯一会在某些情况下导致如此急剧放缓的事情。因此,这里有一些来自正在搜索的主(即最大)表的指示性相关数字。

在主数据表(EXPLAIN 中别名为 prp)中,有 24,771 条记录,其 Property_Class 对应于“First_Name”,Property_Value 为“Edward”,有 567 条记录,Property_Class 对应于“Last_Name”,Property_Value 为“雅培。搜索这些参数的查询需要很长时间才能执行,并且通常会在完成之前使 Web 服务器超时。

相反,有 916 条 Property_Class 对应于“First_Name”且 Property_Value 为“Ruth”的记录,以及 15,054 条 Property_Class 对应于“Last_Name”且 Property_Value 为“Davies”的记录。搜索这些参数的查询需要 0.6 秒才能执行。

如您所见,这两个查询可能涉及相似数量的交叉匹配(约 14,000,000)。然而,一个是冰川,另一个不是。

无论如何,我已经尝试在 my.ini 中增加任何可能听起来的缓冲区类型变量,看看是否有帮助,但鉴于我真的不知道自己在做什么,我有点不愿意在这方面过于努力地尝试. 我更像是一名编码员而不是数据库服务器管理员!

因此,如果有人对我有一些见解,我会很高兴听到它!

感谢您的时间。

编辑:用于将 Property_Type、Person 和 Property_Value 拼接成一个连贯条目的 VIEW 如下:

CREATE VIEW liverpool.person_property_view AS
SELECT 
prp.Person_Record_ID, 
pr.Record_Of_Origin_ID,
pr.Relationship_To_Origin_Record, 
pr.Recorded_Date,
pr.Year_From_Origin_Record,
pr.Master_Person_ID,
pr.Composite_Record_ID,
pr.Has_Been_Matched,
pr.First_Name,
pr.Other_Names,
pr.Last_Name,
pt.Property_ID,
pt.Property_Type_Name,
pt.Property_Type_Display_Name,
pt.Show_Property,
prp.Property_Value,
prp.Property_Display_Value,
prp.Property_Date_Value,
pt.Is_Downloadable,
pt.Is_Person_Record_Link,
pt.Is_Record_Link,
pt.Display_Only_Once,
pt.Property_Display_Order,
rt.Record_Type_Description,
rt.Record_Type_Sort_Order,
rt.Record_Type_Precedence,
rlt.Record_Link_Type_Code
FROM liverpool.person_record_property_value prp 
JOIN liverpool.person_record pr ON prp.Person_Record_ID = pr.Person_Record_ID
JOIN liverpool.property_type pt ON prp.Property_ID = pt.Property_ID
LEFT OUTER JOIN liverpool.record_link_type rlt ON pt.Record_Link_Type_ID = rlt.Record_Link_Type_ID
LEFT OUTER JOIN liverpool.record_type rt ON rt.Record_Type_ID = pr.Record_Type_ID;

以下是我认为相关表的 CREATE TABLE 语句:

CREATE TABLE liverpool.property_type (
  Property_ID INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
  Property_Type_Name VARCHAR(255) DEFAULT NULL,
  Property_Type_Display_Name VARCHAR(255) DEFAULT NULL,
  Show_Property CHAR(1) DEFAULT 'Y',
  Is_Downloadable CHAR(1) DEFAULT 'Y',
  Is_Person_Record_Link CHAR(1) DEFAULT 'N',
  Is_Record_Link CHAR(1) DEFAULT 'N',
  Record_Link_Type_ID INT(11) DEFAULT NULL,
  Property_Display_Order INT(11) UNSIGNED DEFAULT 99,
  Display_Only_Once CHAR(1) DEFAULT 'N',
  PRIMARY KEY ( Property_ID ),
  INDEX Property_Type_Name_IDX ( Property_Type_Name(16) ASC )
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

CREATE TABLE liverpool.person_record_property_value (
  Property_ID INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
  Property_Value VARCHAR(2048) DEFAULT NULL,
  Property_Display_Value VARCHAR(2048) DEFAULT NULL,
  Property_Date_Value DATE DEFAULT NULL,
  Person_Record_ID INT(11) UNSIGNED NOT NULL,
  PRIMARY KEY ( Property_ID, Person_Record_ID ),
  INDEX Property_Display_Value_IDX ( Property_Display_Value(16) ASC ),
  INDEX Property_Value_IDX ( Property_Value(16) ASC ),
  INDEX Property_Date_Value_IDX ( Property_Date_Value ASC )
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

..

CREATE TABLE liverpool.person_record (
   Person_Record_ID INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
   Composite_Record_ID VARCHAR(14) NULL,
   Record_Type_ID INT(11) UNSIGNED DEFAULT NULL,
   Record_Of_Origin_ID VARCHAR(255) NOT NULL,
   Relationship_To_Origin_Record VARCHAR(255) NOT NULL,
   Year_From_Origin_Record VARCHAR(45),
   Recorded_Date DATE DEFAULT NULL, 
   Master_Person_ID VARCHAR(14) NULL,
   Has_Been_Matched CHAR(1) DEFAULT 'N',
   PRIMARY KEY ( Person_Record_ID )
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

CREATE TABLE liverpool.record_type (
  Record_Type_ID INT(11) UNSIGNED NOT NULL UNIQUE,
  Record_Type_Name VARCHAR(45) NOT NULL,
  Record_Of_Origin_Prefix VARCHAR(255) NOT NULL,
  Relationship_To_Origin_Record VARCHAR(255) NOT NULL,
  Record_Type_Description VARCHAR(255) NOT NULL,
  Record_Type_Sort_Order INT(11) UNSIGNED,
  Record_Type_Precedence INT(11) UNSIGNED,
  PRIMARY KEY ( Record_Type_ID ),
  INDEX Record_Type_Name_IDX ( Record_Type_Name(8) ASC )
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

CREATE TABLE liverpool.record_link_type (
  Record_Type_ID INT(11) UNSIGNED NOT NULL,
  Record_Link_Type_ID INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
  Record_Link_Type_Name VARCHAR(255) DEFAULT NULL,
  Record_Link_Type_Code VARCHAR(45) DEFAULT NULL,
  PRIMARY KEY ( Record_Link_Type_ID ),
  INDEX Record_Link_Type_Name_IDX ( Record_Link_Type_Name(8) ASC )
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

编辑:哎呀......你是对的,里克詹姆斯......这些表定义在一个单独的 SQL 脚本中,所以我忘记了它们。道歉。

CREATE TABLE liverpool.property_class (
  Property_Class_ID INT(11) UNSIGNED NOT NULL,
  Property_Class_Name VARCHAR(255),
  Property_Class_Display_Name VARCHAR(255),
  Is_Searchable CHAR(1) DEFAULT 'Y',
  Metaphone_Level CHAR(16) DEFAULT '',
  Is_Number CHAR(1) DEFAULT 'N',
  Is_Date CHAR(1) DEFAULT 'N',
  Is_Link CHAR(1) DEFAULT 'N',
  Is_Ranged CHAR(8) DEFAULT '',
  Display_Order INT(11),
  PRIMARY KEY ( Property_Class_ID ),
  INDEX Property_Class_Name_IDX ( Property_Class_Name )
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

CREATE TABLE liverpool.property_type_class (
  Property_Class_ID INT(11) UNSIGNED NOT NULL,
  Property_ID INT(11) UNSIGNED NOT NULL,
  PRIMARY KEY ( Property_Class_ID, Property_ID ),
  INDEX Property_ID_IDX ( Property_ID ASC ),
  INDEX Property_Class_ID_IDX ( Property_Class_ID ASC )
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
4

1 回答 1

0

这是“过度标准化”。

它是“EAV”。

把这两者放在一起,你会得到很多JOINs无法优化的东西。处理必须来回很多。

(不,我不知道为什么一个查询比另一个慢得多。)

如需更多帮助,请提供SHOW CREATE TABLESHOW CREATE VIEW

(看过之后CREATE TABLEs

 INDEX Property_Type_Name_IDX ( Property_Type_Name(16) ASC )

“前缀索引”实际上是无用的。删除,(16)因为列不是太大。(这可能无助于手头的问题。)还有其他两个类似的索引,但它们可能需要保持原样,除非您可以将声明的大小缩小到 2048 以下。

还需要更多的表定义。

于 2018-04-08T02:57:13.277 回答