根据视图,分辨率和文件大小计算视频的传出流量

时间:2014-12-22 18:26:19

标签: php mysql performance

我有一个视频门户网站,我想根据管理员中的视图,分辨率和文件大小计算视频传递的传出流量。我的问题是如果我的视频视图表中有很多行,php正在退出,内存限制致命错误(512M内存限制)。现在我需要最佳实践或最佳解决方案来计算/处理它。

这就是我的视频观看表:

|----vwid----|--vid--|--------resolution--------|
|Videoview ID|VideoID|Resolution of viewed video|

(有3个与此问题列无关)

每个视频和分辨率的文件大小都不同,需要从每个stat()的文件系统中获取,这就是为什么我不能只拿一个并将它与视图的数量相乘。

在获取每一行并添加单个文件的文件大小之后,我的下一次尝试只获取每个连续的固定行数(如10'000)。但这并没有采取与第一次尝试不同的方式。

之后我尝试计算每个视频的每个分辨率的视图,但这导致了一个非常慢的查询(35s),我放弃了这个想法。

目前我的视图表中有300万行用于测试目的(它们每天都会随机添加),所以我需要一个很好的解决方案来处理大量的行。

现在问题是你们有没有想过要做得更好?如果您需要更多信息,请不要犹豫。

解释查询:

 SELECT vid, resolution, COUNT(*) FROM videoviews GROUP BY vid, resolution

输出:

 id     select_type     table   type    possible_keys   key     key_len     ref     rows    Extra   
 1  SIMPLE  anitube_videoviews  ALL     NULL    NULL    NULL    NULL    3126686     Using temporary; Using filesort

3 个答案:

答案 0 :(得分:4)

首先,你应该考虑一种不同的方法。您的解决方案将根据潜在视图计算结果 - 所以如果我刷新页面5次(对于1GB视频文件),我会在一瞬间产生5 GB的流量 - 我没有。

按照彼得的建议存储视图持续时间更好 - 但仍然不准确。该文件可能已经完全下载,但用户只查看了3分钟。所以你会追踪更少的流量。

因此,您应该跟踪转移到客户端计算机的实际字节的字节,而不是依赖于分辨率和viewcount或view-duration,以获得准确的结果。


实现此目的的一种可能方法是避免文件上的直接链接,但使用php脚本文件传递它们,该文件可以跟踪传输的卷。

以下代码段将提供此类功能。请注意,对于拥有大量用户的服务器,您不应该使用如此大的块大小 - 或者您将很快耗尽内存: - )

以下文件可以存储为getFile.php,并通过传递文件ID来调用,如getFile.php?id=25565。 (拥有这样的getFile.php还具有以下优势:您无需将文件显示为到您的网络 - 在getFile.php中,您可以再次检查登录信息,以及限制未经授权的访问。)

$file = resolveIdToActualFilePath($_GET["id"]);

set_time_limit(0);

//Important: we catch that manually to determine transfered bytes.
ignore_user_abort(true); 

ini_set('output_buffering', 0);
ini_set('zlib.output_compression', 0);

header('Content-Description: File Transfer');
header('Content-Type: video/mp4'); //set depending on format.
header('Content-Disposition: attachment; filename="' . basename($file) . '"'); 
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($file));

// Repeat reading until EOF
$chunk = 1024 * 1024; // bytes per chunk (1 MB)
$fh = fopen($file, "rb");
while (!feof($fh)) { 
    echo fread($fh, $chunk);
    flush();
    ob_flush();

    //catch user abort manually.
    if (connection_status() != 0){
        //abort or timeout. Store already transfered amount to database.
        //here an error of one time chunk size might appear, cause it has been read, but not delivered.
        file_put_contents("test.txt", "Aborted after: ".(ftell($fh)+1)." Bytes.");
        fclose($fh);
        exit; 
    }
}

//pointer pos + 1 = actual bytes transfered - write to database.
$bytesTransfered = ftell($fh) +1; 
file_put_contents("test.txt", "Download complete after ".$bytesTransfered." Bytes");
fclose($fh);
exit; 

根据您存储转移字节的方式,查询变得非常简单 - 而且速度很快。

我建议您使用这样的表格来维护评估所需的所有信息:

id | fileId | userId | bytes | dateTimeStart       | dateTimeEnd         | status   
1    2256     158      15454   2014-12-27 18:45:20   2014-12-27 18:52:17   COMPLETE
2    1123     122      185     2014-12-27 19:00:00   2014-12-27 19:00:02   ABORT
3    12355    112      13365   2014-12-27 20:45:20   2014-12-27 20:45:36   COMPLETE

这样可以确定哪些文件的中止频率,平均下载量是多少 用户的速度(假设您的服务器不是瓶颈),在给定时间内您的峰值上传率是多少,平均负载是多少等。

将表格编入索引,这样的查询应该立即运行:

SELECT SUM(bytes) WHERE fileId = 1123; --traffic per file
SELECT SUM(bytes) WHERE userId = 189; -- traffic per user
SELECT SUM(bytes) WHERE DATE(dateTimeStart) = CURDATE(); -- traffic today
SELECT SUM(bytes) WHERE fileId = 1123 AND DATE(dateTimeStart) = CURDATE(); -- traffic today for file 1123.
SELECT SUM(bytes) WHERE dateTimeStart >= DATE_SUB(NOW(), INTERVAL 7 DAY): -- traffic within last 7 days.

在bytes列上使用(无符号)Bigint可以将流量加总到9223372036854775807个字节,即8 ExaBytes。 (8192 PetaByte)(目前全球互联网流量为每月27,48 PetaByte - 所以除非你主持整个互联网,否则你应该会好一段时间:))

答案 1 :(得分:0)

这个问题可能有几种方法:

视频表中的商店规模

我看到VideoID列,这意味着你有视频表,你可以添加列size default 0并创建简单的php脚本来填充它,如下所示:

set_time_limit(0);
do {
    data = 'select * from `videos` where `size` = 0 limit 1000'; // select any reasonable limit here
    foreach row in data {
        file_size = fstat(filename)
        update `videos` set `size` = ??? where `videoid` = ???
    }
} while (sizeof(rows) > 0);

检查您是否有索引:

  1. 视频:videoid,尺寸
  2. viedeoviews:videoid
  3. 然后您将能够运行如下查询:

    select sum(a.`size`) as totalsize
    from `videos` as a inner join `videoviews` as b on (a.`videoid` = b.`videoid`)
    

    当然,在上传新视频或更改旧视频时 - 您需要将size更新为实际视频

    保存实际汇总统计信息

    您可以创建单独的表,存储实际流量,如下所示:

    create table `stats` (
        `resolution` varchar(...) primary key,
        `total_size` bigint
    );
    

    在每个视图中 - 像

    一样更新此表
    update `stats` set `total_size` = `total_size` + ??? where `resolution` = ???;
    

    当然您会错过旧数据,但您可以使用与第一个解决方案类似的方法填充它(只需在开始统计数据之前选择所有视图)

    PS。在任何情况下,我建议你填充视频表大小,以避免文件系统查询

答案 2 :(得分:0)

我认为您应该在该表中添加一个view_duration字段,并运行如下查询:

SELECT resolution, SUM(view_duration) FROM videoviews WHERE vwid>0 GROUP BY resolution

以上内容应该为您提供每个分辨率的所有观看次数的总时间。对于每种分辨率和视频格式,您的传出流量将是bytes_per_second * sum_of_duration_of_view(以秒为单位)。 https://documentation.apple.com/en/motion/usermanual/index.html#chapter=B%26section=2%26tasks=true