如何使用超过一百万条记录索引MySql表

时间:2017-04-23 21:40:16

标签: php mysql pdo

这个代码在一个小桌子上工作得很好,但是我有一个有180万条记录的表,所以我的搜索时间大约是12秒。我在client_id和customername上通过Phpmyadmin在我的表上创建了一个b-tree索引,速度没有变化。我的表包含116列。我是否需要将表分解为多个表并使用join命令?我见过join命令,但之前从未使用过它。如果是这样,有人可以给出一个简单的例子

$stmt = $conn->prepare("SELECT *, ( 3959 * acos( cos( radians(:currentlat) ) * cos( radians( lat ) ) * cos( radians( longitude ) - radians(:currentlon) ) 
+ sin( radians(:currentlat) ) * sin( radians( lat ) ) ) )   AS distance FROM $live_table

WHERE  (companyname like :name  )

AND

is_active != :active_switch

HAVING distance < :mydistance ORDER BY client_id desc LIMIT $start, $limit");

$stmt->bindValue(':name' , "%$name%", PDO::PARAM_STR);
$stmt->bindValue(':currentlat' , "$currentlat", PDO::PARAM_STR);
$stmt->bindValue(':currentlon' , "$currentlon", PDO::PARAM_STR);
$stmt->bindValue(':mydistance' , "$mydistance", PDO::PARAM_STR); 
$stmt->bindValue(':active_switch' , "$active_switch", PDO::PARAM_STR);

2 个答案:

答案 0 :(得分:0)

查询速度慢的一个重要原因是HAVING DISTANCE < :mydistance子句。这会迫使你的MySQL服务器计算到每一行的距离,然后过滤它们,并可能导致你的megarow表的全表扫描。

您可能应该在WHERE子句中使用边界框计算。我们的想法是从您的桌子中排除位于南,北,东或西太远的行,使其落在:mydistance范围内。这是一个好主意,因为您的查询可以使用lat列上的索引来排除大量行。它看起来像这样:

...
WHERE  (companyname like :name  )
AND
is_active != :active_switch
AND lat  BETWEEN :currentlat - (:mydistance / 69.0 )
             AND :currentlat + (:mydistance / 69.0 )
AND long BETWEEN :currentlong - (:mydistance / (111.045 * COS(RADIANS(latpoint))))
             AND :currentlong + (:mydistance / (111.045 * COS(RADIANS(latpoint))))
HAVING distance < :mydistance ORDER BY client_id desc LIMIT $start, $limit

此边界框搜索的写入是here。幻数69.0来自于每个纬度都有大约69英里的事实。 3959是一个弧度(57.3958度)的英里数。

但是,编写此查询的方式使得很难使用索引来加速它。 It's not sargable as written。如果您可以重新制定标准

 WHERE companyname = :name
   AND is_active = :inactive_switch
   AND lat BETWEEN ...
   AND long BETWEEN ...

然后(companyname, is_active, lat)上的复合索引将在查询性能方面产生奇迹般的改进。查询计划程序可以随机访问索引到适当的起始值lat,然后按顺序扫描到最后一个相关值。

请注意,创建大量单列索引通常会对INSERTUPDATE性能造成影响,并且通常无法帮助您更快地进行SELECT查询。

另请注意,SELECT *对查询效果有害,尤其是在包含许多列且具有ORDER BY ... LIMIT模式的表中。为什么? MySQL必须对一大堆长行进行排序,只是为了丢弃除了少数几个之外的所有行。您最好在SELECT声明中列出实际需要的列。

116列是一个非常大的 - 有些人会说在病理上很大 - 在一个表中有一些列,特别是一个megarow表。如果不了解更多数据,很难为重构数据提出建议。您可能想要调查名为数据库规范化的主题。

答案 1 :(得分:0)

查询需要很长时间的主要原因是在查询中使用了Sin,Cos,ACos函数。

如你所知:

  • 产品操作比总和操作慢约32倍。
  • Sin,Cos,ACos比产品操作慢约50倍,即比总和操作慢约1500倍。

如您所见,这些计算非常耗时。特别是当你为数百万行重复这些计算时。

您需要的是:

  • 为Sin,Cos&amp;定义表格ACos功能结果具有您想要的适当精度。
  • 然后为这些函数定义一些UDF,以根据表中最接近的结果计算这些函数的结果。
  • 使用这些UDF代替Sin,Cos,ACos的内置函数。