我创建了一个如下所述的系统:
有些设备在运行Linux。这些设备连续(每30秒)向数据库发送一些信息,如负载平均数量,可用内存量,序列号,正常运行时间。然后我获取这些数据,在网页上显示并创建图表。这是一个简单的系统。我使用php用于网页,PDO用于数据库操作,amCharts用于图表。我绘制了可用内存和加载平均数据的图表。但是性能存在问题。我将用一个例子来解释这个问题。我们来看一下可用内存数据:
每30秒设备将数据发送到数据库。假设数据连续发送7天。这意味着必须从数据库中提取(60 / 30) * 60 * 24 * 7 ~ 20000
行,以便在7天后绘制单个设备的可用内存图表。在每次重新加载时,我必须一起获取所有20000行。因为我有超过10个设备,页面重新加载需要花费太多时间,而且性能非常低。
我在做的是:
在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 :(得分:1)
对于大型基于日期的数据集,您最好使用AmCharts的Stock Chart。它具有更好的数据分组功能,可以通过最大限度地减少显示的点数来提高性能。常规AmCharts JavaScript库中的序列图只能处理该数量的数据点中的一小部分。
对于一次加载多个图表,我们通常建议的一些技术是在每个图表滚动到视图或菊花链初始化/更新时延迟加载每个图表。可以找到对这些技术的更完整的解释in this answer。
答案 1 :(得分:1)
我不能直接给你答案。但由于我目前的声誉,我无法添加评论,我将在此提出2条建议:
如果您不必同时获取所有这些20.000条记录,则可以简化图表或创建一个仅显示1000条记录的简化amChart。
如果您必须将所有这些记录发送到您的amChart,我建议您使用“异步”记录提取。这样,您可以将“一个”数据库查询拆分为各种异步查询。
正如@xorspark所提到的,您可以将图表划分为多个图表,并可以使用延迟加载。
但我建议你将多个图表与延迟加载和异步记录提取合并。
答案 2 :(得分:0)
在仍然使用当前结构的情况下,可以对渲染进行一些基本的优化:
组轴数据,因为这将减少渲染的负担。该代码对所有数据进行分组,每24个条目仅绘制一次(并在放大时绘制更精细的细节):
var dateAxis = chart.xAxes.push(new am4charts.DateAxis());
dateAxis.baseInterval = {
"timeUnit": "hour",
"count": 1
};
dateAxis.groupData = true;
dateAxis.groupCount = 24;
答案 3 :(得分:0)
当我不得不将3000多个记录加载到条形图中时,我遇到了同样的问题。自然,初始加载时间太多了,并影响了用户体验。为了解决这个问题,我添加了滚动条并根据滚动位置实现了延迟加载。
// init chart and add scrollbar
chart.scrollbarX.events.on('rangechanged', () => {
if (e.target.end === 1 && !isLast) {
fetchData({ dataId, nextPageToken })
.then((data) => {
chartRef.current.activityChart.bindData(trends);
})
}
});