MYSQL Огромные записи и найти каждую ближайшую точку - PullRequest
0 голосов
/ 28 сентября 2018

У меня возникают проблемы, когда я сталкиваюсь с MySQL.У меня есть таблица deviceLog , в которой хранится журнал транспортного средства, состоящий из: 1. DeviceID 2. dateTime 3. Широта 4. Долгота

Устройство будет хранить журнал в базе данных каждую минуту.Это означает 1440 записей в день для одного транспортного средства.
Предполагается, что у меня есть 5000 транспортных средств, что будет составлять примерно 7,2 миллиона строк данных журнала в таблицу каждый день.

Каждый месяц мне нужно генерироватьОтчет о местонахождении устройства каждого транспортного средства.Относится к другому имени таблицы как POI (точка интереса), в которой хранятся: 1. LocationName 2. Широта 3. Долгота

Окончательный результат должен быть: DeviceID, DateTimer, LocationName(На основе Широты, Долготы, предоставленной deviceLog )

Для LocationName я создал функцию, которая вызывает хранимую процедуру, чтобы получить ее, отправив строку широты и долготы, она будетвернуть LocationName из POI таблица

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 голосов
/ 04 октября 2018

Под «выделенными стеками» они подразумевают множество серверов.Подумайте о стоимости.

Есть несколько вещей, которые можно сделать без использования оборудования.

Пожалуйста, укажите SHOW CREATE TABLE для каждой таблицы;Между тем, я предполагаю, что у вас нет (или бесполезных) индексов.Я проверю типы данных, чтобы увидеть, что может быть сжато - чтобы сэкономить дисковое пространство и некоторое время.

Мне не нравится широкий диапазон используемой точности - DOUBLE имеет 16 значащих цифр;69 имеет только 2. Рассмотрим 69.172.См. Функцию RADIAN вместо 8-значного числа pi / 180.

dist/abs(cos(radians(xLat))*69) можно оценить один раз (для небольшого ускорения)

ABS(), вероятно, не требуется.

Без индекса запрос будет сканировать всю таблицу.По крайней мере, есть INDEX(latitude) и INDEX(longitude).Это изменило бы усилия с 550К тестов до 2К.Чтобы сократить его до, возможно, 30, вам потребуется значительное переписывание, например, http://mysql.rjweb.org/doc.php/latlng

Вероятно, в половине случаев «устройство» находится в «том же« месте ».(Это особенно верно для транспортных средств.) В этом случае, запустите , увидев, не было ли устройство перемещено с момента его последнего обнаружения.

Это поднимает еще одну проблему - несохранить местоположение, если оно не переместилось значительно.Это сэкономило бы половину дискового пространства.

Еще одна мысль - изменить ожидания клиента.Не размещайте устройство каждую минуту, а только каждые 10 минут.Это само по себе изменит время вычислений с 1 дня до 2,4 часов.

Комментарии к схеме:

  • FLOAT занимает 4 байта;их можно превратить в какой-то меньший тип данных?широта / долгота несовместимы.См. this для некоторых вариантов выбора.
  • Что такое 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 столбца.Какой основной запрос?Каким образом несколько столбцов необходимы для его поддержки?То есть построить up таблицу из 0 столбцов;Вы добьетесь большего прогресса быстрее, чем попытаетесь сократить число столбцов в 24 раза.

Одна строка каждые 15 секунд.Даже когда «устройство» выключено?Это огромная экономия.

Пересчитать название города, в котором находится устройство?Но это обычно в том же городе, что и в прошлый раз.Проверьте это first .Это должно сэкономить много процессорного времени.

Использовать 3-байтовый MEDIUMINT UNSIGNED для 'city'.Вот что должно быть poiID, а не 4 байта INT SIGNED.JOIN будет достаточно дешевым при отображении имени.

Старение.Конечно, клиенту нужны вчерашние детали.Но, может быть, данные за прошлый месяц могут быть лучше?А прошлогодние еще менее подробные - возможно, даже брошенные?

Если вы будете отбрасывать «старые» данные, сейчас самое время PARTITION таблицы.так что очистка будет «мгновенной».

и т. д.И т.д.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...