用太多数据绘制amCharts图形的有效方法

时间:2017-10-30 06:16:57

标签: javascript php ajax amcharts

我创建了一个如下所述的系统:

有些设备在运行Linux。这些设备连续(每30秒)向数据库发送一些信息,如负载平均数量,可用内存量,序列号,正常运行时间。然后我获取这些数据,在网页上显示并创建图表。这是一个简单的系统。我使用php用于网页,PDO用于数据库操作,amCharts用于图表。我绘制了可用内存和加载平均数据的图表。但是性能存在问题。我将用一个例子来解释这个问题。我们来看一下可用内存数据:

每30秒设备将数据发送到数据库。假设数据连续发送7天。这意味着必须从数据库中提取(60 / 30) * 60 * 24 * 7 ~ 20000行,以便在7天后绘制单个设备的可用内存图表。在每次重新加载时,我必须一起获取所有20000行。因为我有超过10个设备,页面重新加载需要花费太多时间,而且性能非常低。

这是20000行的图表: chart with 20000 rows

我在做的是:

在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
    }
}

如果能够通过这么多不断增加的数据有效地绘制图表,我该怎么做?可能有部分我错过了解释。请问我,我可以进一步解释。

感谢。

4 个答案:

答案 0 :(得分:1)

对于大型基于日期的数据集,您最好使用AmCharts的Stock Chart。它具有更好的数据分组功能,可以通过最大限度地减少显示的点数来提高性能。常规AmCharts JavaScript库中的序列图只能处理该数量的数据点中的一小部分。

对于一次加载多个图表,我们通常建议的一些技术是在每个图表滚动到视图或菊花链初始化/更新时延迟加载每个图表。可以找到对这些技术的更完整的解释in this answer

答案 1 :(得分:1)

我不能直接给你答案。但由于我目前的声誉,我无法添加评论,我将在此提出2条建议:

  1. 如果您不必同时获取所有这些20.000条记录,则可以简化图表或创建一个仅显示1000条记录的简化amChart。

  2. 如果您必须将所有这些记录发送到您的amChart,我建议您使用“异步”记录提取。这样,您可以将“一个”数据库查询拆分为各种异步查询。

  3. 正如@xorspark所提到的,您可以将图表划分为多个图表,并可以使用延迟加载。

    但我建议你将多个图表与延迟加载和异步记录提取合并。

答案 2 :(得分:0)

在仍然使用当前结构的情况下,可以对渲染进行一些基本的优化:

  1. 预格式化日期(即,从date time属性转换为您希望使用的格式,因为图表会自动对所有数据执行此操作
  2. 组轴数据,因为这将减少渲染的负担。该代码对所有数据进行分组,每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);
        })
 }
});