javascript — эффективный способ рисовать графику amCharts со слишком большим количеством данных

Я создал систему, описанную ниже:

Есть некоторые устройства, работающие с Linux. Эти устройства непрерывно отправляют в базу данных некоторую информацию, такую ​​как средний объем загрузки, объем свободной памяти, серийный номер, время безотказной работы (каждые 30 секунд). А потом я получаю эти данные, показываю на веб-странице и создаю диаграммы. Это простая система. Я использовал php для веб-страницы, PDO для операций с базами данных, amCharts для диаграмм. Я нарисовал графики только для свободной памяти и загрузил средние данные. Но есть проблема с производительностью. Я объясню проблему на примере. Давайте посмотрим на данные свободной памяти:

Каждые 30 секунд устройства отправляют данные в базу данных. Предположим, что данные отправляются в течение 7 дней непрерывно. Это означает (60 / 30) * 60 * 24 * 7 ~ 20000 строки должны быть извлечены из базы данных, чтобы нарисовать диаграмму свободной памяти отдельного устройства через 7 дней. При каждой перезагрузке мне приходилось собирать все 20000 строк вместе. Поскольку у меня более 10 устройств, перезагрузка страницы занимает слишком много времени, а производительность очень низкая.

Вот как выглядит график с 20000 строками:
диаграмма с 20000 строками

Что я делаю, это:

Извлечь все данные свободной памяти из базы данных в виде массива в файле getchartdata.php

if (isset($_POST["SerialNumber"]) && isset($_POST["Type"])) {
$serialNumber = $_POST["SerialNumber"];
$type = $_POST["Type"];
if ($type == "LoadAverage") {
$loadavg =$crud->getLoadAvg($serialNumber);
echo json_encode($loadavg);
}
elseif ($type == "Free") {
$freemem =$crud->getFreeMem($serialNumber);
echo json_encode($freemem);
}
}

В index.php я использовал ajax для получения данных из getchartdata.php в javascript. Я использовал функцию обратного вызова, чтобы использовать данные после их получения. И затем я заполняю каждое погружение графиками.

$(document).ready(function(){
$(".device").each(function() {
//In index.php, I use other php functions to get serial numbers and fill divs with each device's serial number.
serialNumber = $(this).text().trim();

//I use local storage to keep "active" tab info. This way when I click the related div, it will get active and this code block will be executed.
if (localStorage.getItem("active") == serialNumber) {
$(this).addClass('active');
$("#" + serialNumber).addClass("active");
getChartInfo(serialNumber, "LoadAverage", createChart);
getChartInfo(serialNumber, "Free", createChart);
}
else if (localStorage.getItem("active") == null || localStorage.getItem("active") == "" ) {
$('.nav-tabs li:first').addClass('active');
$('.tab-pane:first-child').addClass('active');
serialNumber = $('.nav-tabs li:first').text().trim();
getChartInfo(serialNumber, "LoadAverage", createChart);
getChartInfo(serialNumber, "Free", createChart);
}
});

$(".device a").click(function() {
getChartInfo($(this).text(), createChart);
if ($(".device").hasClass('active')) {
localStorage.setItem("active", $(this).text())
$("#status-" + $(this).text()).addClass("active");
}
});

function getChartInfo(SerialNumber, Type) {
//Types: LoadAverage, Free

var postData = 'SerialNumber='+SerialNumber+'&Type='+Type;
divId = Type + "-" + SerialNumber

$.ajax({
type:'POST',
url:'getchartdata.php',
data:postData,
success:function(ajaxResponse){
///console.log(ajaxResponse);
createChart(ajaxResponse, Type, SerialNumber);
},
dataType:"json"});
}

function createChart(ajaxResponse, Type, SerialNumber) {
//console.log(ajaxResponse);
var chartData = [];

for(var a in ajaxResponse) {
date1 = new Date(a);

chartData.push({
date: date1,
loadavg: ajaxResponse[a]
});
}
console.log(chartData);
console.log(divId+"-first");

if (Type == "LoadAverage") {
console.log("loadaverage");
divId = Type + "-" + SerialNumber
console.log(divId+"-load");
var chart = AmCharts.makeChart(divId, {
"type": "serial",
"theme": "light",
"marginRight": 80,
"dataProvider": chartData,
"valueAxes": [{
"position": "left",
"title": "Load Average"}],
"graphs": [{
"id": "g1",
"fillAlphas": 0.4,
"valueField": "loadavg",
"balloonText": "<div style='margin:5px; font-size:19px;'><b>[[value]]</b></div>"}],
"chartScrollbar": {
"graph": "g1",
"scrollbarHeight": 80,
"backgroundAlpha": 0,
"selectedBackgroundAlpha": 0.1,
"selectedBackgroundColor": "#888888",
"graphFillAlpha": 0,
"graphLineAlpha": 0.5,
"selectedGraphFillAlpha": 0,
"selectedGraphLineAlpha": 1,
"autoGridCount": true,
"color": "#AAAAAA"},
"chartCursor": {
"categoryBalloonDateFormat": "JJ:NN, DD MMMM",
"cursorPosition": "mouse"},
"categoryField": "date",
"categoryAxis": {
"minPeriod": "mm",
"parseDates": true
},
"export": {
"enabled": true,
"dateFormat": "YYYY-MM-DD HH:NN:SS"}
});
chart.addListener("dataUpdated", zoomChart);
chart.zoomToIndexes(chartData.length - 1000, chartData.length);

}

if (Type == "Free") {
console.log("free");
divId = Type + "-" + SerialNumber
console.log(divId+"-free")
var chart1 = AmCharts.makeChart(divId, {
"type": "serial",
"theme": "light",
"marginRight": 80,
"dataProvider": chartData,
"valueAxes": [{
"position": "left",
"title": "Free Memory"}],
"graphs": [{
"id": "g1",
"fillAlphas": 0.4,
"valueField": "loadavg",
"balloonText": "<div style='margin:5px; font-size:19px;'><b>[[value]]</b></div>"}],
"chartScrollbar": {
"graph": "g1",
"scrollbarHeight": 80,
"backgroundAlpha": 0,
"selectedBackgroundAlpha": 0.1,
"selectedBackgroundColor": "#888888",
"graphFillAlpha": 0,
"graphLineAlpha": 0.5,
"selectedGraphFillAlpha": 0,
"selectedGraphLineAlpha": 1,
"autoGridCount": true,
"color": "#AAAAAA"},
"chartCursor": {
"categoryBalloonDateFormat": "JJ:NN, DD MMMM",
"cursorPosition": "mouse"},
"categoryField": "date",
"categoryAxis": {
"minPeriod": "mm",
"parseDates": true
},
"export": {
"enabled": true,
"dateFormat": "YYYY-MM-DD HH:NN:SS"}
});

chart1.addListener("dataUpdated", zoomChart);
chart1.zoomToIndexes(chartData.length - 1000, chartData.length);

}
// when we apply theme, the dataUpdated event is fired even before we add listener, so
// we need to call zoomChart here
zoomChart();
// this method is called when chart is first inited as we listen for "dataUpdated" event
function zoomChart() {
// different zoom methods can be used - zoomToIndexes, zoomToDates, zoomToCategoryValues
}
}

Что я могу сделать, чтобы эффективно рисовать диаграммы с таким количеством постоянно растущих данных? Могут быть детали, которые я пропустил, чтобы объяснить. Пожалуйста, спросите меня, и я могу объяснить дальше.

Благодарю.

0

Решение

Для больших наборов данных на основе дат лучше использовать биржевую диаграмму от AmCharts. Он лучше разработан для этого случая использования с функциональностью группировки данных, которая повышает производительность за счет минимизации количества отображаемых точек. Серийные диаграммы в обычной библиотеке JavaScript AmCharts могут обрабатывать только очень небольшую часть этого количества точек данных.

Что касается одновременной загрузки нескольких диаграмм, мы обычно рекомендуем несколько методов: ленивая загрузка каждой диаграммы, когда они прокручиваются в поле зрения, или последовательная инициализация / обновление. Более полное объяснение этих методов можно найти в этом ответе.

1

Другие решения

Я не могу дать вам прямой ответ за это. Но хотя я не могу добавить комментарий из-за моей текущей репутации, я дам здесь 2 предложения:

  1. Если вам не нужно получать все эти 20 000 записей одновременно, вы можете упростить свою диаграмму или создать еще одну упрощенную диаграмму amChart, которая показывает только, например, 1000 записей.

  2. Если вы ДОЛЖНЫ получить все эти записи в свой amChart, я могу порекомендовать вам использовать «асинхронную» выборку записей. Таким образом, вы можете разделить ваш «один» запрос к базе данных на различные асинхронные запросы.

И, как упоминал @xorspark, вы можете разделить свой график на несколько графиков и использовать ленивую загрузку.

Но я рекомендую вам объединить несколько графиков с отложенной загрузкой и асинхронной выборкой записей.

1

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector