MYSQL巨大的记录并找到每个最近的点

时间:2018-09-28 09:38:27

标签: c# mysql performance bigdata latitude-longitude

遇到MySQL时遇到一些麻烦。 我有一个 deviceLog 表,该表存储了以下车辆日志: 1. DeviceID 2. dateTime 3.纬度 4.经度

设备将每分钟将日志存储到数据库中。 平均每天一辆车有1440条记录。
假设我有5000辆车,每天将总计720万行的日志数据记录到表中。

每个月我都需要生成每辆车的设备位置报告。与另一个名为 POI (兴趣点)的表名称相关,该表名称存储: 1. LocationName 2.纬度 3.经度

的最终输出应为: DeviceID,DateTimer,LocationName(基于 deviceLog 提供的纬度,经度)

对于LocationName,我创建了一个函数,该函数调用存储过程以通过发送行的纬度和经度来检索它,它将从 POI

中返回LocationName。
CREATE DEFINER=`root`@`localhost` PROCEDURE `SPGetGeoName`(IN `xLat` DOUBLE, IN `xLon` DOUBLE, OUT `xLocationName` NVARCHAR(1500))
BEGIN

declare lon1 float; declare lon2 float;
    declare lat1 float; declare lat2 float;
    declare dist float; declare pi float;
    set pi = 3.1415926;
    set dist=1.9;
    set lon1 = xLon-dist/abs(cos(radians(xLat))*69);
    set lon2 = xLon+dist/abs(cos(radians(xLat))*69);
    set lat1 = xLat-(dist/69); set lat2 = xLat+(dist/69);

SET xLocationName = (SELECT locationName FROM poiTest 
                WHERE longitude BETWEEN lon1 AND lon2 AND 
                      latitude BETWEEN lat1 AND lat2 AND
                      3956 * 2 * ASIN(SQRT( POWER(SIN((xLat-latitude)* pi/180 / 2), 2) +COS(xLat*pi/180) * COS(latitude*pi/180) *POWER(SIN((xLon-longitude) * pi /180 / 2), 2) )) < dist 
                      ORDER BY 3956 * 2 * ASIN(SQRT( POWER(SIN((xLat-latitude)* pi/180 / 2), 2) +COS(xLat*pi/180) * COS(latitude*pi/180) *POWER(SIN((xLon-longitude) * pi /180 / 2), 2) )) ASC limit 1);


END

结果是每辆车15秒钟,持续1个月,粗略计算大约需要1天才能生成整个报告。

总有办法解决这个问题吗?

CREATE TABLE `deviceLog` (
   `tripID` int(11) NOT NULL AUTO_INCREMENT,
   `latitude` float NOT NULL,
   `longitude` double NOT NULL,
   `rssi` smallint(6) NOT NULL,
   `speed` float NOT NULL,
   `course` float NOT NULL,
   `hdop` float NOT NULL,
   `dateTimer` datetime NOT NULL,
   `gpsStat` tinyint(4) NOT NULL,
   `unitStat` varchar(12) NOT NULL,
   `battVolt` varchar(6) NOT NULL,
   `fuelLevel` varchar(6) NOT NULL DEFAULT '0',
   `fuelData` varchar(6) NOT NULL DEFAULT '0',
   `ignVolt` varchar(6) NOT NULL,
   `odoMeter` decimal(10,2) NOT NULL,
   `deviceID` varchar(16) NOT NULL,
   `chksum` varchar(2) NOT NULL,
   `resol` varchar(1024) DEFAULT NULL,
   `driverID` varchar(20) DEFAULT NULL,
   `geoFences` varchar(255) DEFAULT NULL,
   `poiLoc` varchar(255) DEFAULT NULL,
   `eventStat` varchar(2) DEFAULT NULL,
   `IOStat` varchar(4) DEFAULT NULL,
   `groupID` varchar(2) DEFAULT NULL,
   PRIMARY KEY (`tripID`),
   KEY `deviceID` (`deviceID`),
   KEY `dateTimer` (`dateTimer`)
 ) ENGINE=MyISAM AUTO_INCREMENT=3423023 DEFAULT CHARSET=latin1


CREATE TABLE `poi` (
   `poiID` int(11) NOT NULL AUTO_INCREMENT,
   `type` varchar(50) NOT NULL,
   `locationName` varchar(200) NOT NULL,
   `state` varchar(50) NOT NULL,
   `city` varchar(50) NOT NULL,
   `longitude` float(10,7) DEFAULT NULL,
   `latitude` float DEFAULT NULL,
   PRIMARY KEY (`poiID`),
   KEY `lat` (`longitude`,`latitude`)
 ) ENGINE=MyISAM AUTO_INCREMENT=683606 DEFAULT CHARSET=latin1 ROW_FORMAT=DYNAMIC

1 个答案:

答案 0 :(得分:2)

通过“专用堆栈”,它们意味着很多服务器。考虑成本。

可以完成很多事情而又不给硬件扔东西。

请为每个表格提供SHOW CREATE TABLE;同时,我将假设您没有索引(或无用的索引)。我将检查数据类型以查看可以缩小的内容-以节省磁盘空间和一些时间。

我不喜欢使用各种各样的精度-DOUBLE有16位有效数字; 69只有2个。考虑69.172。请参见RADIAN函数代替8位pi / 180。

dist/abs(cos(radians(xLat))*69)可以被评估一次(以极小的加速)

ABS()可能是不必要的。

没有索引,查询将扫描整个表。至少具有INDEX(latitude)INDEX(longitude)。这会将工作量从550K测试更改为2K。要将其缩小到30,您需要进行大量重写,例如http://mysql.rjweb.org/doc.php/latlng

“设备”位于相同“位置”的时间大概是一半。 (对于车辆尤其如此。)在这种情况下,通过查看设备自上次定位以来是否没有移动来开始

这带来了另一个问题-除非位置已显着移动,否则不要存储位置。这样可以节省一半的磁盘空间。

另一种想法-改变客户的期望。不要每分钟定位一次设备,而是每10分钟定位一次。仅此一项,就可以将计算时间从1天更改为2.4小时。

对架构的评论:

  • FLOAT占用4个字节;可以将它们转换为较小的数据类型吗?纬度/经度不一致。有关某些选择,请参见this
  • 什么是geoFencesresol
  • 不要将(m,n)与FLOAT一起使用(例如float(10,7))。

如果您一次要获取一个设备的所有数据,请更改

PRIMARY KEY (`tripID`),
KEY `deviceID` (`deviceID`),

PRIMARY KEY (`deviceID`, tripID),
KEY (`tripID`),

这将更好地利用“聚类”的优势。但是您还必须更改为InnoDB。

设备停止时,您将需要消除“重复”条目。否则,您将遇到磁盘空间问题(和性能问题)。

不像YouTube

YouTube存在不同的问题;与其他大多数笨蛋一样。不要去研究它们。

我建议您的头号问题是数据量。

  • 更少的列。
  • 行较少。
  • 汇总信息。

24列-其中一些不会持续几分钟或整天更改。因此,不要一直存储它们。

拆分24列。什么是主要查询?需要多少 列来支持它?也就是说,从0列构建 up 表格;您将比尝试减少24列的进度更快。

每15秒一行。即使关闭“设备”?节省了很多。

重新计算设备所在城市的名称?但这通常和上次在同一座城市。首先检查该 。那应该节省很多CPU时间。

对“城市”使用3字节的MEDIUMINT UNSIGNED。这就是poiID,而不是4字节的INT SIGNED。显示名称时,JOIN足够便宜。

正在老化。确保客户需要昨天的详细信息。但是,也许上个月的数据可能会变化?去年的详细程度甚至更低-甚至可以扔掉?

如果您要抛弃“旧”数据,现在该PARTITION了。这样清除将是“瞬时的”。

等等等等