设为首页 收藏本站
查看: 1390|回复: 0

[经验分享] Java、PHP、Python与MySQL交互的性能测试

[复制链接]

尚未签到

发表于 2018-8-16 10:30:57 | 显示全部楼层 |阅读模式
  这几天看源码弄清了一件事:WEB服务器接收浏览器请求、将请求传给PHP/Python进程(FCGI等)、与数据库进行交互都是用socket(套接字)。
  也就是说,这些行为都是进程间通信。一台WEB服务器在硬件、操作系统不变的情况下,它的性能主要取决于socket通信的速度。如果所有进程都在一台服务器上的话,这个速度就取决于通信的效率了。
  例如与MySQL数据库交互时,程序会调用驱动程序来访问数据库,这个驱动程序主要做这几件事:
  1.创建socket,连接到MySQL。
  2.将程序调用的API翻译成SQL语句,通过socket发送给MySQL;MySQL执行后,将结果发送回来;驱动程序将结果(字符串)传给程序,如有需要,还可以自动翻译成程序能识别的变量类型(如整型)。
  3.断开连接。
  可见连接的速度、翻译的速度和接受响应的及时性是最主要的3个方面。
  弄明白这点后就不难发现,与数据库的执行时间相比,这些只是很少的一部分;而且PHP和Python使用的都是C实现,而JDBC是用Java实现,所以根本不必担心PHP和Python的性能。
  不过在翻译方面还存在算法和实现上的差异,客户端还可以缓存一些语句,所以仍然会出现一些性能上的差异。
  为证明我的想法,我特意去测试了一番。
  首先列出测试平台:
  引用
  CPU:Intel Core2 Duo T9400 @ 2.53GHz
  内存:3GB
  操作系统:Windows XP Pro SP2
  MySQL:5.1.36
  Java:1.6.0_17-b04
  JDBC:MySQL Connector/J 5.1.10
  PHP:5.2.11 (cli)
  MySQLi:5.2.11.11
  Python:2.6.4
  MySQL-Python:1.2.3c1
  所用的库都是最新版的,也都采用了最为推荐的库。
  但数据库并没有使用最新的稳定版,因为我懒得重下了。5.4.3-beta测试版也试过,在连续插入时,性能比5.1快1~2个数量级,估计是服务器端缓存和设置的原因。
  测试项目:
  1.创建100万个随机数,并生成插入这些随机数的SQL语句。
  2.连接本地数据库,如不成功,尝试创建数据库。
  3.删除并创建数据库表,引擎类型为InnoDB,主键为自动递增的整数,此外有个浮点型的字段(无索引)。
  4.分成100组,每次插入1万个随机数。(因为每组的执行量都很大,因此启用自动提交事务。)
  5.用SELECT COUNT(*)统计小于0.1的随机数个数。(约10万个)
  6.用SELECT *取出再统计大于0.9的随机数个数。(约10万个)
  7.将所有0.4~0.5之间的随机数加1。(约10万个)
  8.将所有0.5~0.6之间的行删除。(约20万个)
  9.断开数据库连接。
  10.再次连接数据库。
  测试代码:
  Java:
  Java代码  

  •   import java.sql.Connection;
  •   import java.sql.DriverManager;
  •   import java.sql.SQLException;
  •   import java.sql.Statement;
  •   import java.sql.ResultSet;
  •   import java.util.Random;


  •   public final class Test {

  •   public static void main(String[] args) {
  •   final int SIZE1 = 10000;
  •   final int SIZE2 = 100;
  •   final String DB_ENGINE = "InnoDB"; // InnoDB Memory MyISAM
  •   final double NANO_TIME_PER_SEC = 1000000000.0;
  •   System.out.printf("测试数据量:%d\n", SIZE1 * SIZE2);
  •   System.out.printf("测试引擎:%s\n", DB_ENGINE);

  •   long t1 = System.nanoTime(), t2, t3 = 0, t4, t5, t6, t7, t8, t9, t10, t11;
  •   Connection conn = null;
  •   Statement stmt = null;
  •   ResultSet rs = null;

  •   Random r = new Random();
  •   String[] sqls = new String[SIZE2];
  •   for (int i = 0; i < SIZE2; ++i){
  •   StringBuilder buffer = new StringBuilder("INSERT INTO test (value) VALUES (");
  •   for (int j = 0; j < SIZE1; ++j){
  •   buffer.append(r.nextDouble()).append("),(");
  •   }
  •   sqls = buffer.substring(0, buffer.length() -2);
  •   }
  •   t2 = System.nanoTime();

  •   try {
  •   conn = DriverManager.getConnection("jdbc:mysql://localhost/testdb?user=root&password=123456");
  •   t3 = System.nanoTime();
  •   stmt = conn.createStatement();
  •   } catch (SQLException e) {
  •   try {
  •   conn = DriverManager.getConnection("jdbc:mysql://localhost/?user=root&password=123456");
  •   t3 = System.nanoTime();
  •   stmt = conn.createStatement();
  •   stmt.execute("CREATE DATABASE testdb");
  •   } catch (SQLException ex) {
  •   System.out.println("SQLException: " + ex.getMessage());
  •   System.out.println("SQLState: " + ex.getSQLState());
  •   System.out.println("VendorError: " + ex.getErrorCode());
  •   }
  •   }

  •   try {
  •   stmt.execute("DROP TABLE test");
  •   } catch (SQLException e) {
  •   }
  •   try {
  •   stmt.execute("CREATE TABLE test (`id` INT AUTO_INCREMENT PRIMARY KEY, `value` REAL) ENGINE = " + DB_ENGINE);
  •   } catch (SQLException e) {
  •   }
  •   t4 = System.nanoTime();

  •   try {
  •   for (String sql: sqls){
  •   stmt.execute(sql);
  •   }
  •   t5 = System.nanoTime();

  •   rs = stmt.executeQuery("SELECT COUNT(*) FROM test WHERE value < 0.1");
  •   if (rs.next())
  •   System.out.printf("共有%d个小于0.1的随机数\n", rs.getInt(1));
  •   t6 = System.nanoTime();

  •   rs = stmt.executeQuery("SELECT * FROM test WHERE value > 0.9");
  •   if (rs.last())
  •   System.out.printf("共有%d个大于0.9的随机数\n", rs.getRow());
  •   t7 = System.nanoTime();

  •   stmt.executeUpdate("UPDATE test SET value = value + 0.1 WHERE value > 0.4 AND value < 0.5");
  •   t8 = System.nanoTime();

  •   stmt.execute("DELETE FROM test WHERE value > 0.5 AND value < 0.6");
  •   t9 = System.nanoTime();

  •   stmt.close();
  •   conn.close();
  •   t10 = System.nanoTime();

  •   conn = DriverManager.getConnection("jdbc:mysql://localhost/?user=root&password=123456");
  •   t11 = System.nanoTime();
  •   conn.close();

  •   System.out.printf("创建随机数:%f\n", (t2 - t1) / NANO_TIME_PER_SEC);
  •   System.out.printf("初次连接数据库:%f\n", (t3 - t2) / NANO_TIME_PER_SEC);
  •   System.out.printf("再次连接数据库:%f\n", (t11 - t10) / NANO_TIME_PER_SEC);
  •   System.out.printf("初始化数据库和表:%f\n", (t4 - t3) / NANO_TIME_PER_SEC);
  •   System.out.printf("插入:%f\n", (t5 - t4) / NANO_TIME_PER_SEC);
  •   System.out.printf("选择(COUNT):%f\n", (t6 - t5) / NANO_TIME_PER_SEC);
  •   System.out.printf("选择:%f\n", (t7 - t6) / NANO_TIME_PER_SEC);
  •   System.out.printf("更新:%f\n", (t8 - t7) / NANO_TIME_PER_SEC);
  •   System.out.printf("删除:%f\n", (t9 - t8) / NANO_TIME_PER_SEC);
  •   System.out.printf("关闭连接:%f\n", (t10 - t9) / NANO_TIME_PER_SEC);
  •   System.out.printf("总时间:%f\n", (t10 - t1) / NANO_TIME_PER_SEC);

  •   } catch (SQLException ex) {
  •   System.out.println("SQLException: " + ex.getMessage());
  •   System.out.println("SQLState: " + ex.getSQLState());
  •   System.out.println("VendorError: " + ex.getErrorCode());
  •   }

  •   }

  •   }
  PHP:
  Php代码  

  •   
  Python:
  Python代码  

  •   # -*- coding: gbk -*-

  •   import MySQLdb
  •   from random import random
  •   from time import clock

  •   SIZE1 = 10000
  •   SIZE2 = 100
  •   DB_ENGINE = 'InnoDB' # InnoDB Memory MyISAM
  •   print '测试数据量:', SIZE1 * SIZE2
  •   print '测试引擎:', DB_ENGINE

  •   t1 = clock()

  •   sqls = ['INSERT INTO test (value) VALUES (%s)' % '),('.join([`random()` for i in xrange(SIZE1)]) for j in xrange(SIZE2)]
  •   t2 = clock()

  •   try:
  •   con = MySQLdb.connect(user='root', passwd='123456', db='testdb')
  •   t3 = clock()
  •   cu = con.cursor()
  •   except:
  •   con = MySQLdb.connect(user='root', passwd='123456')
  •   t3 = clock()
  •   cu = con.cursor()
  •   cu.execute('CREATE DATABASE testdb')
  •   con.select_db('testdb')

  •   con.autocommit(True)

  •   try:
  •   cu.execute('DROP TABLE test')
  •   except:
  •   pass
  •   cu.execute('''''CREATE TABLE test (
  •   `id` INT AUTO_INCREMENT PRIMARY KEY,
  •   `value` REAL)
  •   ENGINE = %s''' % DB_ENGINE)
  •   t4 = clock()

  •   for sql in sqls:
  •   cu.execute(sql)
  •   t5 = clock()

  •   cu.execute('SELECT COUNT(*) FROM test WHERE value < 0.1')
  •   print '共有%d个小于0.1的随机数' % cu.fetchone()[0]
  •   t6 = clock()

  •   cu.execute('SELECT * FROM test WHERE value > 0.9')
  •   print '共有%d个大于0.9的随机数' % len(cu.fetchall())
  •   t7 = clock()

  •   cu.execute('UPDATE test SET value = value + 0.1 WHERE value > 0.4 AND value < 0.5')
  •   t8 = clock()

  •   cu.execute('DELETE FROM test WHERE value > 0.5 AND value < 0.6')
  •   t9 = clock()

  •   cu.close()
  •   con.close()
  •   t10 = clock()

  •   con = MySQLdb.connect(user='root', passwd='123456', db='testdb')
  •   t11 = clock()
  •   con.close()

  •   print '创建随机数:', t2 - t1
  •   print '初次连接数据库:', t3 - t2
  •   print '再次连接数据库:', t11 - t10
  •   print '初始化数据库:', t4 - t3
  •   print '插入:', t5 - t4
  •   print '选择(COUNT)', t6 - t5
  •   print '选择:', t7 - t6
  •   print '更新:', t8 - t7
  •   print '删除:', t9 - t8
  •   print '关闭连接:', t10 - t9
  •   print '总时间:', t10 - t1
  MySQL-Python还有个底层的模块,一并测试下:
  Python代码  

  •   # -*- coding: gbk -*-

  •   import _mysql
  •   from MySQLdb.converters import conversions
  •   from random import random
  •   from time import clock

  •   SIZE1 = 10000
  •   SIZE2 = 100
  •   DB_ENGINE = 'InnoDB' # InnoDB Memory MyISAM
  •   print '测试数据量:', SIZE1 * SIZE2
  •   print '测试引擎:', DB_ENGINE

  •   t1 = clock()

  •   sqls = ['INSERT INTO test (value) VALUES (%s)' % '),('.join([`random()` for i in xrange(SIZE1)]) for j in xrange(SIZE2)]
  •   t2 = clock()

  •   try:
  •   con = _mysql.connect(user='root', passwd='123456', db='testdb', conv=conversions)
  •   t3 = clock()
  •   except:
  •   con = _mysql.connect(user='root', passwd='123456', conv=conversions)
  •   t3 = clock()
  •   con.query('CREATE DATABASE testdb')
  •   con.select_db('testdb')

  •   con.autocommit(True)

  •   try:
  •   con.query('DROP TABLE test')
  •   except:
  •   pass
  •   con.query('''''CREATE TABLE test (
  •   `id` INT AUTO_INCREMENT PRIMARY KEY,
  •   `value` REAL)
  •   ENGINE = %s''' % DB_ENGINE)
  •   t4 = clock()

  •   for sql in sqls:
  •   con.query(sql)
  •   t5 = clock()

  •   con.query('SELECT COUNT(*) FROM test WHERE value < 0.1')
  •   print '共有%d个小于0.1的随机数' % con.store_result().fetch_row()[0]
  •   t6 = clock()

  •   con.query('SELECT * FROM test WHERE value > 0.9')
  •   print '共有%d个大于0.9的随机数' % con.store_result().num_rows()
  •   t7 = clock()

  •   con.query('UPDATE test SET value = value + 0.1 WHERE value > 0.4 AND value < 0.5')
  •   t8 = clock()

  •   con.query('DELETE FROM test WHERE value > 0.5 AND value < 0.6')
  •   t9 = clock()

  •   con.close()
  •   t10 = clock()

  •   con = _mysql.connect(user='root', passwd='123456', db='testdb', conv=conversions)
  •   t11 = clock()
  •   con.close()

  •   print '创建随机数:', t2 - t1
  •   print '初次连接数据库:', t3 - t2
  •   print '再次连接数据库:', t11 - t10
  •   print '初始化数据库:', t4 - t3
  •   print '插入:', t5 - t4
  •   print '选择(COUNT)', t6 - t5
  •   print '选择:', t7 - t6
  •   print '更新:', t8 - t7
  •   print '删除:', t9 - t8
  •   print '关闭连接:', t10 - t9
  •   print '总时间:', t10 - t1
  每种测试3次(等硬盘灯无闪烁后才进行下一次测试),取最好的一次作为测试结果:
  Java:
  引用
  测试数据量:1000000
  测试引擎:InnoDB
  共有99465个小于0.1的随机数
  共有99859个大于0.9的随机数
  创建随机数:2.367840
  初次连接数据库:0.220420
  再次连接数据库:0.013174
  初始化数据库和表:0.075140
  插入:12.139346
  选择(COUNT):1.130345
  选择:1.017769
  更新:6.173245
  删除:9.380070
  关闭连接:0.002131
  总时间:32.506307
  PHP:
  引用
  测试数据量:1000000
  测试引擎:InnoDB
  共有99898个小于0.1的随机数
  共有100152个大于0.9的随机数
  创建随机数:1.506294
  初次连接数据库:0.003146
  再次连接数据库:0.001808
  初始化数据库和表:0.131754
  插入:12.046944
  选择(COUNT):1.236742
  选择:1.238153
  更新:6.115232
  删除:8.145547
  关闭连接:0.000125
  总时间:30.423937
  Python(MySQLdb):
  引用
  测试数据量: 1000000
  测试引擎: InnoDB
  共有100040个小于0.1的随机数
  共有100351个大于0.9的随机数
  创建随机数: 1.6822107279
  初次连接数据库: 0.0332120423126
  再次连接数据库: 0.00221704155137
  初始化数据库: 0.131054924578
  插入: 11.7999030603
  选择(COUNT) 1.27067266929
  选择: 1.16714526567
  更新: 6.29200638629
  删除: 8.13660563005
  关闭连接: 0.000131022238861
  总时间: 30.5129417286
  Python(_mysql):
  引用
  测试数据量: 1000000
  测试引擎: InnoDB
  共有99745个小于0.1的随机数
  共有99869个大于0.9的随机数
  创建随机数: 1.68099074044
  初次连接数据库: 0.0112056141213
  再次连接数据库: 0.00159293988482
  初始化数据库: 0.130169616529
  插入: 12.1364623157
  选择(COUNT) 1.125517908
  选择: 0.968366649951
  更新: 6.8042843434
  删除: 8.9760508668
  关闭连接: 9.61015995031e-05
  总时间: 31.8331441566
  可以看到,在大批量数据测试中,Java是最慢的,而PHP是最快的。
  不考虑IO性能的波动的话,Java主要慢在连接和关闭数据库。JDBC4.0在第一次连接数据库时会动态加载驱动,非常耗时,因此使用Java要记住使用数据库连接池,避免连接浪费大量时间。当然,这也造成了数据库的负担,势必影响内存占用。而创建随机数的算法实现各不相同,所以不具备可比性;令我惊讶的是SELECT的翻译速度,将字符串转换成浮点数居然慢于Python,要知道后者的浮点数可是对象。
  PHP连接数据库非常快,所以完全无需使用连接池,因为维护连接池会增加复杂性。
  Python的表现和PHP差不多,但是第一次连接数据库比较慢(仍比Java快1个数量级)。如果不用连接池,则使用FCGI等方式来运行比较合适。_mysql模块的通信很快,但更新和删除操作却不太理想,也许是IO性能波动的原因。此外,我在连接数据库时使用了转换参数,实际上我用的语句都不需要翻译,不使用的话会更快一点。
  接着试试小数据,改成最常用的MyISAM引擎,插入100条(1组)。一般的应用不可能一次插入那么多,所以足够满足平时的应用了;而且由于数据量很小,也基本不受IO影响。
  测试结果:
  Java:
  引用
  测试数据量:100
  测试引擎:MyISAM
  共有9个小于0.1的随机数
  共有10个大于0.9的随机数
  创建随机数:0.001596
  初次连接数据库:0.224135
  再次连接数据库:0.018656
  初始化数据库和表:0.055601
  插入:0.001476
  选择(COUNT):0.000529
  选择:0.000433
  更新:0.000304
  删除:0.000313
  关闭连接:0.000927
  总时间:0.285314
  PHP:
  引用
  测试数据量:测试数据量:100
  测试引擎:MyISAM
  共有12个小于0.1的随机数
  共有9个大于0.9的随机数
  创建随机数:0.000649
  初次连接数据库:0.008077
  再次连接数据库:0.001609
  初始化数据库和表:0.060421
  插入:0.001860
  选择(COUNT):0.000580
  选择:0.000465
  更新:0.000326
  删除:0.000373
  关闭连接:0.000127
  总时间:0.072878
  Python(MySQLdb):
  引用
  测试数据量: 100
  测试引擎: MyISAM
  共有14个小于0.1的随机数
  共有9个大于0.9的随机数
  创建随机数: 0.000198907961766
  初次连接数据库: 0.0334640296462
  再次连接数据库: 0.00150577796899
  初始化数据库: 0.0123194428342
  插入: 0.00125211444471
  选择(COUNT) 0.000581079438867
  选择: 0.000484139744018
  更新: 0.000250311142897
  删除: 0.000262323842835
  关闭连接: 7.98984228442e-05
  总时间: 0.0488922474784
  Python(_mysql):
  引用
  测试数据量: 100
  测试引擎: MyISAM
  共有12个小于0.1的随机数
  共有10个大于0.9的随机数
  创建随机数: 0.000214273043082
  初次连接数据库: 0.0118774872225
  再次连接数据库: 0.00123702872851
  初始化数据库: 0.0315031659052
  插入: 0.00120322554962
  选择(COUNT) 0.000596165155069
  选择: 0.000507327048549
  更新: 0.0002447238406
  删除: 0.00026148574749
  关闭连接: 5.78285787719e-05
  总时间: 0.0464656820909
  从结果可以看出,虽然差距都很小,但Python仍然稍微占优。不过Java的SELECT操作稍微胜出,而这也是实际应用中最常使用的操作。
  再从语言方面来看,Python无疑是写得最欢畅的,生成随机数只用了1行代码;PHP的变量要写个$让我老是出错,不过数据库操作不需要处理异常,这点节省了很多代码;Java的代码量很大,而且不得不使用很多try...catch,我甚至懒得以安全的方式将close()放在finally块里面,因为它也会抛出我根本懒得去管的异常,且会提示我计时变量可能没有初始化。
  总体上来看,Google放弃Python,只采用C++和Java是有点不明智。因为页面响应时间主要在于数据库通信和磁盘文件IO上,语言的影响基本忽略不计。


运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-552564-1-1.html 上篇帖子: python实现对nginx的access日志的统计 下篇帖子: 【Python进阶】03、json-xiexiaojun
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表