Matomo数据模型系列文章之三:归档过程

日志数据(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添加进去。

发表评论