日志数据(Log Data)不会被直接用于终端用户的报告。因为一个报告所需要的数据很多,直接使用会直接影响报告的呈现效率。
为了解决这个问题,归档过程就应运而成,它会将日志数据归档为归档数据。然后报表就能使用这些归档数据呈现报表了。
欢迎加入Matomo中文网。请加入官方QQ群255820112(点击滚动至本页末可扫描二维码),随时提问,有问必答。
举例
让我们来举个例子,有个网站每天会有1000个页面访问。它的日志数据就会是下面的样子(省略号表示其他属性) 。
URL Time …
/homepage 17:00:19 …
/about 17:01:10 …
/homepage 17:05:30 …
/categories 17:06:14 …
/homepage 17:10:03 ……
归档过程聚合原始数据成归档数据。如果你想构建每个页面访问次数的报表(找到最热门的网页),归档会列举所有的网页并计算每个网页的访问次数。
URL Page views
/homepage 205
/categories 67
/about 5
…
这个数据就是归档数据。 对1000个PV进行预计算看起来似乎是多余的,不过当处理大量数据的时候就尤显必要了。
何时归档
默认情况下,归档数据是按需计算和缓存的。当某个报表被请求的时候,Piwik会检查所需的归档数据是否存在,如果不存在它会启动归档。
提前归档
当追踪的网站流量很大时,按需归档会花费很多时间。在这种情况下,按需归档必须被禁用,取而代之的是将归档变成后台定时任务。
提前归档可以针对每个站点和日期区间(除了自定义日期范围),命令如下:
$ ./console core:archive
通常设置的方法是使用cron(Linux定时任务)固定间隔时间执行。
这个命令会记住最后运行的位置,只有在有新记录的时候才会继续执行。
如何执行
日志数据会按照站点、周期(日、周、月、年或自定义时间段)、分段条件(Segment)进行归档。
归档的逻辑是由插件来定义的。插件定义的所有报表会被一起归档,而不是单个单个归档。
如果分段条件(Segment)没有被指定,并且数据没找到,每个插件的每个报表会一次性被生成和缓存。如果分段条件被指定,所请求数据对应插件的所有报表会被生成和缓存。
周期聚合
归档数据的运算方式依赖于周期的类型:
- 按天数据是直接归档原始日志数据
- 按周、按月、按年或自定时间段是使用按天数据进行计算的。
比如说,按周的归档数据是将期间的每天的归档数据进行二次归档的。这样处理效率会比较高。
插件归档器
插件如果想归档报告和数值指标的话,可以自定义一个归档器,它只需要继承 Piwik\Plugin\Archiver。这个类会被自动检测到并在归档过程中自动被调用(还记得Piwik的扩展点吗?)。
日志数据的聚合由LogAggregator 来处理。归档数据的聚合由ArchiveProcessor::aggregateDataTableRecords() 和ArchiveProcessor::aggregateNumericMetrics()这两个方法 来完成
插件可以通过Piwik\Plugin\Archiver来访问LogAggregator 和ArchiveProcessor实例。详细了解Matomo的MySQL后台是如何实现聚合的,请阅读Piwik数据库结构。
持久化归档数据
归档数据通过ArchiveProcessor进行持久化。
数值指标通过insertNumericRecord插入到数据库。
报表数据首先会使用DataTable::getSerialized()进行序列化,然后通过ArchiveProcessor::insertBlobRecord()插入到数据库中。
// insert a numeric metric
$myFancyMetric = // … calculate the metric value …
$archiveProcessor->insertNumericRecord(‘MyPlugin_myFancyMetric’, $myFancyMetric);
// insert a record (with all of its subtables)
$maxRowsInTable = Config::getInstance()->General[‘datatable_archiving_maximum_rows_standard’];j
$dataTable = // … build by aggregating visits …
$serializedData = $dataTable->getSerialized(
$maxRowsInTable,
$maxRowsInSubtable = $maxRowsInTable,
$columnToSortBy = Metrics::INDEX_NB_VISITS
);
$archiveProcessor->insertBlobRecords(‘MyPlugin_myFancyReport’, $serializedData);
持久化的报告和数值指标是用站点ID,日期,分段条件进行索引的。归档的日期和时间也是与数据关联的。请查看归档表的Piwik数据库表结构会有更直观的认识。
报表是如何以blob的形式被存储在archive_blob_*表中的呢?
当插入一条blob记录的时候,archive_blob_$year_$month 数据表会出现一行新数据。这行新数据用于创建根DataTable。DataTable的子表也会以新行进行存储,不过子表会100个一组分开进行存储。比如说记录 MyPlugin_myFancyReport_chunk_100_199表示包含ID100-199的子表 。在这种情况,archive表的value列就包含了blob数组的序列化数据,形如array([subtableId] => [subtableBlob])。
idarchive | name | value | Description |
1 | Actions_actions_url |
gzcompress( $serializedRootTableBlob ) | 包含根表的blob数据 |
1 | Actions_actions_url_chunk_0_99 |
gzcompress(serialize( array([subtableId]=>[serializedSubtableBlob]) )) | 包含ID为0-99的子表的blob数组(0不能去掉,它表示根表ID) |
1 | Actions_actions_url_chunk_100_199 | … | 包含ID为100-199的子表的blob数组 |
1 | Actions_actions_url_chunk_200_299 | … | 包含ID为200-299的子表的blob数组 |
报表 vs 记录
当报表(report)被归档之后,它就不再叫报表(report),而是成为记录(record )了。之所以把它们区分开来是因为多个报表可以来自于一个记录(record)。
比如说,UerSettings插件使用一条记录存储访客浏览器详情。这条记录可以用来生成 UserSettings.getBrowserVersion 和 UserSettings.getBrowser两个报表。第二张报表只需要将第一报表进行简单处理即可。考虑到新的报表会为站点/周期/分段条件的各种组合条件进行缓存,如果插件将两个报表都归档的话,实属空间浪费。
记录存储指南
当持久化记录的时候,请千万小心,尽量保持存储数据尽量小。在插入记录作为归档数据之前,请确认下是否严格遵守了以下原则:
- 记录存储是的列名请不要使用字符串,而是使用对应列ID(在PHP文件中定义常量)
- Metadata请不要随报告一起存储。取而代之,在API中将记录转化成报告时将metadata添加进去。