MySQL的插入操作,使用PHP比C++更快,这是否正常?

5

最近我被分配了一些速度检测任务,以便我可以判断在将某个数量的行插入数据库时是更快使用php/php-cli还是c++。

在我们开始之前,让我告诉你一些细节,以便一切清楚明白:

  • PHP 部分通过 Apache 直接在浏览器中请求运行。
  • 进行硬盘驱动器测试的是 SSD 硬盘。我猜在普通硬盘上会更慢。这台机器本身没什么特别之处,大约六年左右。
  • 所有插入操作都通过 prepared statements 完成。我们在 PHP 上使用 mysqli,而在 C++ 中使用 mysqlcppconn(由 Oracle 提供的 MySQL C++ 连接器)。
  • 所有插入操作都逐个进行。我知道我们可以将它们堆叠起来,但是,我们正在进行测试。
  • 时间是通过 PHP 中的 microtime 和 C++ 中的 header 显示的。
  • 代码本身当然是不相等的。稍后会详细说明。
  • 所有文本都采用 UTF-8 编码。里面有俄语、中文、阿拉伯语、西班牙语、英语和各种疯狂的东西。MySQL 表使用 utf8_4mb。
  • C++ 代码中的数字是使用 std::vector 和 -O2 级别编译 g++ 的结果(向量优于映射、无序映射和 std::array)。

所以,这就是流程:

  • 连接到数据库。
  • 打开一个有N行的文本文件。
  • 读取文件中的一行。
  • 在分隔符上拆分该行。
  • 使用拆分行的某些部分获取插入值(例如第0、1和3个索引)。
  • 将这些部分发送到准备好的语句以进行插入。
  • 重复此过程,直到完全读取文件。

两种代码都按预期工作。以下是结果数字:

php:

  • 5000条目:1.42 - 1.27秒。
  • 20000条目:5.53 - 6.18秒。
  • 50000条目:14.43 - 15.69秒。

c++:

  • 5000条目:1.78 - 1.81秒。
  • 20000条目:7.19 - 7.22秒。
  • 50000条目:18.52 - 18.84秒。

随着文件中的行数增加,PHP比C++表现更出色...起初,我怀疑是行分割函数的问题:在PHP中,使用"explode"进行分割。该算法对于C++来说非常简单...容器通过引用传递,并且其内容在运行时发生改变。容器只遍历一次。我确保容器“保留()”了所有必要的空间(记住,我最终选择了向量),这是固定的。容器在主函数中创建,然后通过引用传递到代码中。它从未被清空或调整大小:只有其内容发生改变。

template<typename container> void explode(const std::string& p_string, const char p_delimiter, container& p_result)
{
    auto it=p_result.begin();
    std::string::const_iterator beg=p_string.begin(), end=p_string.end();
    std::string temp;

    while(beg < end)
    {
        if( (*beg)==p_delimiter)
        {
            *(it)=temp;
            ++it;
            temp="";
        }
        else
        {
            temp+=*beg;
        }

        ++beg;
    }

    *(it)=temp;
}

如前所述,执行的任务是相同的,但生成它的代码不同。C++代码具有通常用于控制mysql交互的try-catch块。至于其余部分,主循环运行直到达到EOF,并且每次迭代都会检查插入是否失败(在c++和php中都是如此)。
我曾看到c++在处理文件及其内容方面表现得比php更出色,因此我希望在这里也能适用。不知何故,我怀疑分割算法,但也许只是数据库连接器较慢(尽管当我禁用数据库交互时,php仍然处理得更快),或者我的代码不够好...
就性能分析而言,gprof对c++代码产生了以下输出:
Each sample counts as 0.01 seconds.
  %   cumulative   self              self     total           
 time   seconds   seconds    calls  ns/call  ns/call  name    
 60.00      0.03     0.03    50000   600.00   600.00  void anc_str::explotar_cadena<std::vector<std::string, std::allocator<std::string> > >(std::string const&, char, std::vector<std::string, std::allocator<std::string> >&)
 40.00      0.05     0.02                             insertar(sql::PreparedStatement*, std::string const&, std::vector<std::string, std::allocator<std::string> >&)
  0.00      0.05     0.00        1     0.00     0.00  _GLOBAL__sub_I__ZN7anc_str21obtener_linea_archivoERSt14basic_ifstreamIcSt11char_traitsIcEE

在这里,“explotar_cadena”是“explode”,“insertar”是“拆分此行并设置预处理语句”。正如您所看到的,有60%的时间花在那里(不足为奇......它运行了50000次并进行了这种疯狂的拆分操作)。"obtener_linea_archivo"只是“请将下一行转储到字符串中”。
没有mysql交互(只需加载文件,读取行并拆分它们),我得到了以下测量结果: php - 5000个条目:0.019-0.036秒。 - 20000个条目:0.09-0.10秒。 - 50000个条目:0.14-0.17秒。
c ++
- 5000个条目:0.07-0.10秒。 - 20000个条目:0.25-0.26秒。 - 50000个条目:0.49-0.55秒。
好的,两种方法的时间都很好,对于实际生活来说几乎不可感知,但我很惊讶...因此问题在于:我应该期望这种情况吗?任何有先前经验的人愿意帮忙吗?

希望您能提前感谢。

编辑:这里有一个简化版本的快速链接,包含输入文件、C++代码和php代码 [ http://www.datafilehost.com/d/d31034d6 ]。请注意,此处没有SQL交互:只有文件打开、字符串分割和时间测量。请原谅代码和注释中出现的语法错误和一半的西班牙语变量名,因为这是匆忙完成的。此外,请注意上面的gprof结果:我不是专家,但我认为我们正在尝试找到更好的字符串分割方法。


请使用Very Sleepy测试您的C++程序,并在此处添加结果。 - light_keeper
不好意思,我不熟悉Windows... 我试过了gprof。会编辑这篇文章以反映出来。 - The Marlboro Man
1
从代码中删除计时器,使用控制台上的系统时间命令进行测量。您不应包括PHP的启动和关闭时间,以避免结果偏差。 - Joe Watkins
Joe,我非常非常愿意这样做...你能指点一下如何实际测量php时间的方向吗?(我不希望它变得更快,因为我对c++有偏爱)。 - The Marlboro Man
1
微秒是足够好的,然而在第一次调用微秒时,mysql客户端库会被初始化,以及PHP的其余部分,我敢打赌C++代码仅仅包含了比PHP更多的指令。世界需要变得有意义,PHP不比C++更快,如果真是这样,那么你的C++代码可能存在问题。当然,如果你发布一个到gist/git/bitbucket/pastebin/洞穴壁上的代码方向的链接,我们就能做得更好了......我现在只是盲目地猜测...... - Joe Watkins
显示剩余5条评论
1个回答

1

其中一部分可能与每种语言中使用的驱动程序/接口有关。例如,在PHP / MySQL中,您可能会发现mysqli比mysql快,而mysql比PDO快。这是因为库逐渐更抽象(或者不再维护)。您可以尝试在数据库服务器上对查询本身进行分析,以查看执行时间是否有差异。但另一方面,正如其他评论者所指出的那样,可能还有其他情况。


非常感谢您的回答。我认为我可以假设实现本身有更多层,因此速度较慢。尽管没有任何数据库交互,但我使用php获得了更好的数字(如您在编辑后的问题中所看到的)。正如之前提到的那样,与简单的php代码相比,c++代码似乎生成了更多的指令。 - The Marlboro Man

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接