mysql记录大量数据并找到每个最近的点

busg9geu  于 2021-06-19  发布在  Mysql
关注(0)|答案(1)|浏览(316)

我遇到mysql时遇到了一些麻烦。我有一个表devicelog,其中存储车辆日志包括:1。设备ID 2。日期时间3。纬度4。经度
设备每分钟都会在数据库中存储一个日志。这意味着一辆车每天有1440条记录。
假设我有5000辆车,这将总计约720万行日志数据进入表中每天。
每个月我都需要生成每辆车的设备位置报告。它与另一个表名poi(兴趣点)相关,该表名存储:1。位置名称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
bprjcwpo

bprjcwpo1#

“专用栈”指的是大量服务器。想想成本。
有几件事是可以做到的,而不必投入硬件。
请提供 SHOW CREATE TABLE 每张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字节;它们能转换成更小的数据类型吗?lat/lng不一致。看看这个有什么选择。
是什么 geoFences 以及 resol ?
不要将(m,n)与float一起使用(例如float(10,7))。
如果要一次获取一个设备的所有数据,请更改

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

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

这将更好地利用“集群”。但您也必须更改为innodb。
当设备停止时,您需要消除“重复”条目。否则,您将有磁盘空间问题(和性能问题)。
不像youtube
youtube有不同的问题;其他大人物也是如此。不用费心研究了。
我建议你的首要问题是数据量。
更少的列。
行数更少。
总结信息。
24列--其中一些在几分钟内或一整天都不会改变。所以,不要一直储存它们。
把24列分开。主要问题是什么?需要多少列来支持它?也就是说,从0列构建表;你将取得更多的进展比试图削减从24列。
每15秒一排。即使“设备”已关闭?节省了很多钱。
重新计算设备所在城市的名称?但通常和上次在同一个城市。先检查一下。这将节省大量cpu时间。
使用3字节 MEDIUMINT UNSIGNED 为了“城市”。那是什么 poiID 应该是,而不是4字节 INT SIGNED . 这个 JOIN 当你显示名字的时候就足够便宜了。
衰老。当然客户需要昨天的细节。但上个月的数据可能是courser?而去年更不详细的——甚至可能被扔了?
如果你要扔掉“旧”数据,现在是时候 PARTITION table。所以吹扫是“瞬时的”。
等等等等。

相关问题