序言
这篇文章不关于如何使用PHP和MySQL,或者如何编写将某些信息记录到数据库的脚本。这个问题旨在找出最佳解决方案,以便使用PHP脚本将最强最快方式的信息记录到MySQL数据库中!所以它真正关于微观改进。谢谢!
情况:
我在服务器上运行了一个PHP脚本,可以非常快速地向客户提供内容。 MySQL服务器也可以在该机器上使用,因此这是首选的武器。现在我想跟踪一些有关请求的信息。因此,我需要以某种方式记录信息,我认为这里最好的解决方案是可以存储信息的平面数据库表。
但我需要尽可能保持日志的时间,以便至少对响应时间没有影响,即使是在许多同时发出的请求中也是如此。系统每天有100K到1M的请求。其中大多数是在工作时间之间( 8 - 18 o' clock )。实际响应时间约为3-5毫秒,因此即使1毫秒也意味着增加20%。
我创建的数据库表非常扁平且没有 extras 。该表上只有索引位于id
列,这是一个PRIMARY
字段AUTO_INCREMENT
,因为我希望为其他作业提供唯一标识符(稍后会详细介绍此)。对于这篇文章和进一步的例子,我们假设一个像这样的表结构:
| id | d1 | d2 | d3 |
|----|-----|-----|-----|
| 1 | foo | bar | baz |
| 2 | foo | bar | baz |
| 3 | foo | bar | baz |
| ...
记录数据的处理将在稍后由另一个脚本完成。因此,不需要对数据进行进一步的操作,而是关于存储本身。但是这张桌子可以长到大约3M排。
数据库思路:
首先,我问自己正确的数据库引擎。我的第一个想法是,Memory
将是最快的,但是每当服务器停机时我们都会丢失所有条目(我有一个每周维护窗口来安装更新并重新启动系统 )。这不应该发生。所以我回到MyISAM
和InnoDB
。但是要采取哪一个?
所以我做了一个简单的基准测试,看看这两个引擎之间是否存在很大差异。我已经在我的开发机器上创建了三个表,每个表都有另一个引擎,并创建了一个简单的脚本,计算了一些时间。
CREATE TABLE `log_myisam` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`d1` varchar(3) NOT NULL,
`d2` varchar(3) NOT NULL,
`d3` varchar(3) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM;
CREATE TABLE `log_innodb` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`d1` varchar(3) NOT NULL,
`d2` varchar(3) NOT NULL,
`d3` varchar(3) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
CREATE TABLE `log_memory` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`d1` varchar(3) NOT NULL,
`d2` varchar(3) NOT NULL,
`d3` varchar(3) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=Memory;
我的测试脚本现在只需在表格中输入1,000,000
个扁平线。然后计算平均消耗时间。这是基准脚本:
foreach( array("myisam", "innodb", "memory") as $table ) {
$query = "INSERT INTO log_{$table} SET d1='foo',d2='bar',d3='baz'";
$start = microtime(true);
for( $i = 0; $i < 1000000; $i++ ) {
$sql->query($query);
}
$end = microtime(true);
$results[$table] = $end - $start;
}
正如预期的那样,Memory
表是迄今为止最快的表。但即使MyISAM
每次都比InnoDB
快。这对我来说很有意义,因为MyISAM
泄漏了对foreign keys
和transactions
等内容的支持,因此此引擎中的功能开销较少。
我真正感到惊讶的是,Memory
表的大小几乎是其他表的两倍。在这一点上,我不确定为什么。结果:
| InnoDB | MyISAM | Memory |
|-----------------|-----------|-----------|-----------|
| time for insert | 133.92 s | 101.00 s | 79.351 s |
| avg. per entry | 0.1392 ms | 0.1010 ms | 0.0794 ms |
| time saved in % | 0.0000 % | 24.585 % | 21.436 % |
| table size | 35.6 mb | 29.9 mb | 55.9 mb |
但据我所知,MyISAM
在执行INSERT
时会锁定表格。对于许多同时发出的请求,这可能会有问题。但我不知道如何对此进行基准测试。
另一个问题是,id
列的索引将如何影响运行时间。它有用还是会减慢时间。因此,在我从PRIMARY
列中删除AUTO_INCREMENT
索引和id
选项后,我再次运行基准脚本。
| InnoDB | MyISAM | Memory |
|-----------------|-----------|-----------|-----------|
| time with id | 133.92 s | 101.00 s | 79.351 s |
| avg. with id | 0.1392 ms | 0.1010 ms | 0.0794 ms |
|-----------------|-----------|-----------|-----------|
| time without id | 131.88 s | 91.868 s | 73.014 s |
| avg. without id | 0.1319 ms | 0.0919 ms | 0.0701 ms |
MyISAM
似乎最有利于删除索引。但是这两个结果的范围并没有像预期的那样扩散。
查询提示:
查询本身一直很简单。我不知道如何进一步改进这一点。
INSERT INTO log_myisam
SET
d1 = 'foo',
d2 = 'bar',
d3 = 'baz'
PHP脚本思想:
需要花费时间的一件事就是连接本身。因此,我会使用持久连接。我当然使用mysqli
。但procedural
或oop
用法之间是否存在差异?我再次做了一个简单的基准测试。
$startProcedual = microtime(true);
for( $i = 0; $i < 1000; $i++ ) {
$sql = mysqli_connect('localhost', 'root', '', 'benchmark');
mysqli_close($sql);
unset($sql);
}
$endProcedual = microtime(true);
$startOop = microtime(true);
for( $i = 0; $i < 1000; $i++ ) {
$sql = new mysqli('localhost', 'root', '', 'benchmark');
$sql->close();
unset($sql);
}
$endOop = microtime(true);
没有持久连接,差异非常明显! oop
样式更快一点,这只有1.000个连接。
procedural: 0.326150 s
oop: 0.256580 s
启用持久连接后,两个版本几乎相同。但整个连接时间下降了正常时间的三分之一。因此,最好的方法是在这里使用持久连接。
procedural: 0.093201 s
oop: 0.092088 s
我的临时结论:
实际上,我的记录时间为0.204毫秒(为100,000个插入)。
至于现在我会说以下内容:
MyISAM
,因为它是速度,安全性和尺寸的最佳组合id
列,因为索引更快oop
style 但对我来说有一些悬而未决的问题。我做出了正确的决定吗? MyISAM
阻止执行吗?有没有办法使用Memory
?持久连接是否会产生任何副作用,例如通过更高的内存使用速度减慢? ...
我非常感谢您提供更快速记录的想法或提示。也许我在某些方面完全错了。如果您对此类事情有任何经验,请告诉我。
提前致谢!
答案 0 :(得分:2)
无论引擎如何,我很高兴不接触那个烫手山芋,到目前为止最快的方法是使用您创建的CSV文件中的LOAD DATA INFILE
。因此,当流量进入时,CSV会以附加模式创建。有一种机制可以关闭一个版本,获得一个新的增量,并重新开始。也许每小时。完成后,您的文件可能如下所示
/tmp/csvfiles/traffic_20160725_00.csv
...
/tmp/csvfiles/traffic_20160725_23.csv
这让你获得了24小时的流量。如上所述上传,无论何时你想要,请放心两件事:
INSERT
电话(会受到影响您心情的因素)。额外的好处是你的csv,只是称它们为文本文件,当你决定它可能属于哪个时,它已经准备好摇滚到无sql解决方案。
注意:我是活动的忠实粉丝。我的个人资料页面中有3个链接作为示例。但是,禁止使用事件和存储过程LOAD DATA INFILE
。所以我有单独的代理人这样做。那些代理很好,因为随着时间的推移我自然会为它们添加不同的功能使用事件和代理的组合,从不需要使用cron或其他o / s调度程序。
The Takeway:永远不要使用INSERT
。