MySQL 性能压测工具-sysbench,从入门到自定义测试项

sysbench是一个开源的、基于LuaJIT(LuaJIT 是 Lua 的即时编译器,可将代码直接翻译成机器码,性能比原生 lua 要高) 的、可自定义脚本的多线程基准测试工具,也是目前用得最多的 MySQL 性能压测工具。

基于 sysbench,我们可以对比 MySQL 在不同版本、不同硬件配置、不同参数(操作系统和数据库)下的性能差异。

下面会从 sysbench 的基本用法出发,逐渐延伸到 sysbench 的一些高级玩法,譬如如何阅读自带的测试脚本、如何自定义测试项等。除此之外,使用 sysbench 对 CPU 进行测试,网上很多资料都语焉不详,甚至是错误的,所以这次也会从源码的角度分析 CPU 测试的实现逻辑及 –cpu-max-prime 选项的具体含义。

本文主要包括以下几部分:

安装 sysbench

下面是 sysbench 源码包的安装步骤。

#&#xA0;yum&#xA0;-y&#xA0;install&#xA0;make&#xA0;automake&#xA0;libtool&#xA0;pkgconfig&#xA0;libaio-devel&#xA0;openssl-devel&#xA0;mysql-devel<br>#&#xA0;cd&#xA0;/usr/src/<br>#&#xA0;wget&#xA0;https://github.com/akopytov/sysbench/archive/refs/tags/1.0.20.tar.gz<br>#&#xA0;tar&#xA0;xvf&#xA0;1.0.20.tar.gz<br>#&#xA0;cd&#xA0;sysbench-1.0.20/<br>#&#xA0;./autogen.sh<br>#&#xA0;./configure<br>#&#xA0;make&#xA0;-j<br>#&#xA0;make&#xA0;install

安装完成后,压测脚本默认会安装在 /usr/local/share/sysbench 目录下。

我们看看该目录的内容。

#&#xA0;ls&#xA0;/usr/local/share/sysbench/<br>bulk_insert.lua&#xA0;&#xA0;oltp_insert.lua&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;oltp_read_write.lua&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;oltp_write_only.lua&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;tests<br>oltp_common.lua&#xA0;&#xA0;oltp_point_select.lua&#xA0;&#xA0;oltp_update_index.lua&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;select_random_points.lua<br>oltp_delete.lua&#xA0;&#xA0;oltp_read_only.lua&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;oltp_update_non_index.lua&#xA0;&#xA0;select_random_ranges.lua

除了 oltp_common.lua是个公共模块,其它每个 lua 脚本都对应一个测试场景。

sysbench 用法讲解

sysbench 命令语法如下:

sysbench&#xA0;[options]...&#xA0;[testname]&#xA0;[command]

命令中的 testname是测试项名称。sysbench 支持的测试项包括:

  • *.lua:数据库性能基准测试。
  • fileio:磁盘 IO 基准测试。
  • cpu:CPU 性能基准测试。
  • memory:内存访问基准测试。
  • threads:基于线程的调度程序基准测试。
  • mutex:POSIX 互斥量基准测试。

command是 sysbench 要执行的命令,支持的选项有: prepareprewarmruncleanuphelp。注意,不是所有的测试项都支持这些选项。

options是配置项。sysbench 中的配置项主要包括以下两部分:

  1. 通用配置项。这部分配置项可通过 sysbench --help 查看。例如,
#&#xA0;sysbench&#xA0;--help<br>...<br>General&#xA0;options:<br>&#xA0;&#xA0;--threads=N&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;number&#xA0;of&#xA0;threads&#xA0;to&#xA0;use&#xA0;[1]<br>&#xA0;&#xA0;--events=N&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;limit&#xA0;for&#xA0;total&#xA0;number&#xA0;of&#xA0;events&#xA0;[0]<br>&#xA0;&#xA0;--time=N&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;limit&#xA0;for&#xA0;total&#xA0;execution&#xA0;time&#xA0;in&#xA0;seconds&#xA0;[10]<br>&#xA0;...

  1. 测试项相关的配置项。各个测试项支持的配置项可通过 sysbench testname help 查看。例如,
#&#xA0;sysbench&#xA0;memory&#xA0;help<br>sysbench&#xA0;1.0.20&#xA0;(using&#xA0;bundled&#xA0;LuaJIT&#xA0;2.1.0-beta2)<br><br>memory&#xA0;options:<br>&#xA0;&#xA0;--memory-block-size=SIZE&#xA0;&#xA0;&#xA0;&#xA0;size&#xA0;of&#xA0;memory&#xA0;block&#xA0;for&#xA0;test&#xA0;[1K]<br>&#xA0;&#xA0;--memory-total-size=SIZE&#xA0;&#xA0;&#xA0;&#xA0;total&#xA0;size&#xA0;of&#xA0;data&#xA0;to&#xA0;transfer&#xA0;[100G]<br>&#xA0;&#xA0;--memory-scope=STRING&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;memory&#xA0;access&#xA0;scope&#xA0;{global,local}&#xA0;[global]<br>&#xA0;&#xA0;--memory-hugetlb[=on|off]&#xA0;&#xA0;&#xA0;allocate&#xA0;memory&#xA0;from&#xA0;HugeTLB&#xA0;pool&#xA0;[off]<br>&#xA0;&#xA0;--memory-oper=STRING&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;type&#xA0;of&#xA0;memory&#xA0;operations&#xA0;{read,&#xA0;write,&#xA0;none}&#xA0;[write]<br>&#xA0;&#xA0;--memory-access-mode=STRING&#xA0;memory&#xA0;access&#xA0;mode&#xA0;{seq,rnd}&#xA0;[seq]

对 MySQL 进行基准测试的基本步骤

下面以 oltp_read_write为例,看看使用 sysbench 对 MySQL 进行基准测试的四个标准步骤:

#&#xA0;sysbench&#xA0;oltp_read_write&#xA0;--mysql-host=10.0.0.64&#xA0;--mysql-port=3306&#xA0;--mysql-user=admin&#xA0;--mysql-password=Py@123456&#xA0;--mysql-db=sbtest&#xA0;--tables=30&#xA0;--table-size=1000000&#xA0;--threads=30&#xA0;prepare

命令中各个选项的具体含义如下:

  • oltp_read_write:测试项,对应的是 /usr/local/share/sysbench/oltp_read_write.lua。这里也可指定脚本的绝对路径名。
  • –mysql-host、–mysql-port、–mysql-user、–mysql-password:分别代表 MySQL 实例的主机名、端口、用户名和密码。
  • –mysql-db:库名。不指定则默认为 sbtest
  • –tables :表的数量,默认为 1。
  • –table-size :单表的大小,默认为 10000。
  • –threads :并发线程数,默认为 1。注意,导入时,单表只能使用一个线程。
  • prepare:执行准备工作。

oltp_read_write 用来压测 OLTP 场景。在 sysbench 1.0 之前, 该场景是通过 oltp.lua 这个脚本来测试的。不过该脚本在 sysbench 1.0 之后就被废弃了,但为了跟之前的版本兼容,该脚本放到了 /usr/local/share/sysbench/tests/include/oltp_legacy/ 目录下。

鉴于 oltp_read_write.lua 和 oltp.lua 两者的压测内容完全一致。从 sysbench 1.0 开始,压测 OLTP 场景建议直接使用 oltp_read_write。

预热。主要是将磁盘中的数据加载到内存中。

#&#xA0;sysbench&#xA0;oltp_read_write&#xA0;--mysql-host=10.0.0.64&#xA0;--mysql-port=3306&#xA0;--mysql-user=admin&#xA0;--mysql-password=Py@123456&#xA0;--mysql-db=sbtest&#xA0;--tables=30&#xA0;--table-size=1000000&#xA0;--threads=30&#xA0;prewarm

除了需要将命令设置为 prewarm,其它配置与 prepare 中一样。

压测。

#&#xA0;sysbench&#xA0;oltp_read_write&#xA0;--mysql-host=10.0.0.64&#xA0;--mysql-port=3306&#xA0;--mysql-user=admin&#xA0;--mysql-password=Py@123456&#xA0;--mysql-db=sbtest&#xA0;--tables=30&#xA0;--table-size=1000000&#xA0;--threads=64&#xA0;--time=60&#xA0;--report-interval=10&#xA0;run

其中,

  • –time :压测时间。不指定则默认为 10 秒。除了 –time,也可通过 –events 限制需要执行的 event 的数量。
  • –report-interval=10 :每 10 秒输出一次测试结果,默认为 0,不输出。

清理数据。

#&#xA0;sysbench&#xA0;oltp_read_write&#xA0;--mysql-host=10.0.0.64&#xA0;--mysql-port=3306&#xA0;--mysql-user=admin&#xA0;--mysql-password=Py@123456&#xA0;--mysql-db=sbtest&#xA0;--tables=30&#xA0;cleanup

这里只需指定 –tables ,sysbench 会串行执行 DROP TABLE IF EXISTS sbtest 操作。

如何分析 MySQL 基准测试结果

下面我们分析下 oltp_read_write 场景下的压测结果。注:右滑可以看到每个指标的具体含义。

Threads&#xA0;started!<br><br>[&#xA0;10s&#xA0;]&#xA0;thds:&#xA0;64&#xA0;tps:&#xA0;5028.08&#xA0;qps:&#xA0;100641.26&#xA0;(r/w/o:&#xA0;70457.59/20121.51/10062.16)&#xA0;lat&#xA0;(ms,95%):&#xA0;17.32&#xA0;err/s:&#xA0;0.00&#xA0;reconn/s:&#xA0;0.00<br># thds &#x662F;&#x5E76;&#x53D1;&#x7EBF;&#x7A0B;&#x6570;&#x3002;tps &#x662F;&#x6BCF;&#x79D2;&#x4E8B;&#x52A1;&#x6570;&#x3002;qps &#x662F;&#x6BCF;&#x79D2;&#x64CD;&#x4F5C;&#x6570;&#xFF0C;&#x7B49;&#x4E8E; r&#xFF08;&#x8BFB;&#x64CD;&#x4F5C;&#xFF09;&#x52A0;&#x4E0A; w&#xFF08;&#x5199;&#x64CD;&#x4F5C;&#xFF09;&#x52A0;&#x4E0A; o&#xFF08;&#x5176;&#x4ED6;&#x64CD;&#x4F5C;&#xFF0C;&#x4E3B;&#x8981;&#x5305;&#x62EC; BEGIN &#x548C; COMMIT&#xFF09;&#x3002;lat &#x662F;&#x5EF6;&#x8FDF;&#xFF0C;(ms,95%)&#xA0;&#x662F; 95%&#xA0;&#x7684;&#x67E5;&#x8BE2;&#x65F6;&#x95F4;&#x5C0F;&#x4E8E;&#x6216;&#x7B49;&#x4E8E;&#x8BE5;&#x503C;&#xFF0C;&#x5355;&#x4F4D;&#x6BEB;&#x79D2;&#x3002;err/s &#x662F;&#x6BCF;&#x79D2;&#x9519;&#x8BEF;&#x6570;&#x3002;reconn/s &#x662F;&#x6BCF;&#x79D2;&#x91CD;&#x8BD5;&#x7684;&#x6B21;&#x6570;&#x3002;<br>[&#xA0;20s&#xA0;]&#xA0;thds:&#xA0;64&#xA0;tps:&#xA0;5108.93&#xA0;qps:&#xA0;102192.09&#xA0;(r/w/o:&#xA0;71533.28/20440.64/10218.17)&#xA0;lat&#xA0;(ms,95%):&#xA0;17.32&#xA0;err/s:&#xA0;0.00&#xA0;reconn/s:&#xA0;0.00<br>[&#xA0;30s&#xA0;]&#xA0;thds:&#xA0;64&#xA0;tps:&#xA0;5126.50&#xA0;qps:&#xA0;102505.50&#xA0;(r/w/o:&#xA0;71756.30/20496.60/10252.60)&#xA0;lat&#xA0;(ms,95%):&#xA0;17.32&#xA0;err/s:&#xA0;0.00&#xA0;reconn/s:&#xA0;0.00<br>[&#xA0;40s&#xA0;]&#xA0;thds:&#xA0;64&#xA0;tps:&#xA0;5144.50&#xA0;qps:&#xA0;102907.20&#xA0;(r/w/o:&#xA0;72034.07/20583.72/10289.41)&#xA0;lat&#xA0;(ms,95%):&#xA0;17.01&#xA0;err/s:&#xA0;0.00&#xA0;reconn/s:&#xA0;0.00<br>[&#xA0;50s&#xA0;]&#xA0;thds:&#xA0;64&#xA0;tps:&#xA0;5137.29&#xA0;qps:&#xA0;102739.80&#xA0;(r/w/o:&#xA0;71916.99/20548.64/10274.17)&#xA0;lat&#xA0;(ms,95%):&#xA0;17.01&#xA0;err/s:&#xA0;0.00&#xA0;reconn/s:&#xA0;0.00<br>[&#xA0;60s&#xA0;]&#xA0;thds:&#xA0;64&#xA0;tps:&#xA0;4995.38&#xA0;qps:&#xA0;99896.35&#xA0;(r/w/o:&#xA0;69925.98/19979.61/9990.75)&#xA0;lat&#xA0;(ms,95%):&#xA0;17.95&#xA0;err/s:&#xA0;0.00&#xA0;reconn/s:&#xA0;0.00<br>SQL&#xA0;statistics:<br>&#xA0;&#xA0;&#xA0;&#xA0;queries&#xA0;performed:<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;read:&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;4276622&#xA0;#&#xA0;&#x8BFB;&#x64CD;&#x4F5C;&#x7684;&#x6570;&#x91CF;<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;write:&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;1221892&#xA0;#&#xA0;&#x5199;&#x64CD;&#x4F5C;&#x7684;&#x6570;&#x91CF;<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;other:&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;610946&#xA0;&#xA0;#&#xA0;&#x5176;&#x5B83;&#x64CD;&#x4F5C;&#x7684;&#x6570;&#x91CF;<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;total:&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;6109460&#xA0;#&#xA0;&#x603B;&#x7684;&#x64CD;&#x4F5C;&#x6570;&#x91CF;&#xFF0C;total&#xA0;=&#xA0;read&#xA0;+&#xA0;write&#xA0;+&#xA0;other<br>&#xA0;&#xA0;&#xA0;&#xA0;transactions:&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;305473&#xA0;(5088.63&#xA0;per&#xA0;sec.)&#xA0;&#xA0;&#xA0;&#xA0;#&#xA0;&#x603B;&#x7684;&#x4E8B;&#x52A1;&#x6570;&#xFF08;&#x6BCF;&#x79D2;&#x4E8B;&#x52A1;&#x6570;&#xFF09;<br>&#xA0;&#xA0;&#xA0;&#xA0;queries:&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;6109460&#xA0;(101772.64&#xA0;per&#xA0;sec.)&#xA0;#&#xA0;&#x603B;&#x7684;&#x64CD;&#x4F5C;&#x6570;&#xFF08;&#x6BCF;&#x79D2;&#x64CD;&#x4F5C;&#x6570;&#xFF09;<br>&#xA0;&#xA0;&#xA0;&#xA0;ignored&#xA0;errors:&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;0&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;(0.00&#xA0;per&#xA0;sec.)&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;#&#xA0;&#x5FFD;&#x7565;&#x7684;&#x9519;&#x8BEF;&#x6570;&#xFF08;&#x6BCF;&#x79D2;&#x5FFD;&#x7565;&#x7684;&#x9519;&#x8BEF;&#x6570;&#xFF09;<br>&#xA0;&#xA0;&#xA0;&#xA0;reconnects:&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;0&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;(0.00&#xA0;per&#xA0;sec.)&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;#&#xA0;&#x91CD;&#x8BD5;&#x6B21;&#x6570;&#xFF08;&#x6BCF;&#x79D2;&#x91CD;&#x8BD5;&#x7684;&#x6B21;&#x6570;&#xFF09;<br><br>General&#xA0;statistics:<br>&#xA0;&#xA0;&#xA0;&#xA0;total&#xA0;time:&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;60.0301s&#xA0;&#xA0;#&#xA0;&#x603B;&#x7684;&#x6267;&#x884C;&#x65F6;&#x95F4;<br>&#xA0;&#xA0;&#xA0;&#xA0;total&#xA0;number&#xA0;of&#xA0;events:&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;305473&#xA0;&#xA0;&#xA0;&#xA0;#&#xA0;&#x6267;&#x884C;&#x7684;&#xA0;event&#xA0;&#x7684;&#x6570;&#x91CF;<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;#&#xA0;&#x5728;&#xA0;oltp_read_write&#xA0;&#x4E2D;&#xFF0C;&#x9ED8;&#x8BA4;&#x53C2;&#x6570;&#x4E0B;&#xFF0C;&#x4E00;&#x4E2A;&#xA0;event&#xA0;&#x5176;&#x5B9E;&#x5C31;&#x662F;&#x4E00;&#x4E2A;&#x4E8B;&#x52A1;<br><br>Latency&#xA0;(ms):<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;min:&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;5.81&#xA0;#&#xA0;&#x6700;&#x5C0F;&#x8017;&#x65F6;<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;avg:&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;12.57&#xA0;#&#xA0;&#x5E73;&#x5747;&#x8017;&#x65F6;<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;max:&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;228.87&#xA0;#&#xA0;&#x6700;&#x5927;&#x8017;&#x65F6;<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;95th&#xA0;percentile:&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;17.32&#xA0;#&#xA0;95%&#xA0;event&#xA0;&#x7684;&#x6267;&#x884C;&#x8017;&#x65F6;<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;sum:&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;3840044.28&#xA0;#&#xA0;&#x603B;&#x8017;&#x65F6;<br><br>Threads&#xA0;fairness:<br>&#xA0;&#xA0;&#xA0;&#xA0;events&#xA0;(avg/stddev):&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;4773.0156/30.77&#xA0;&#xA0;#&#xA0;&#x5E73;&#x5747;&#x6BCF;&#x4E2A;&#x7EBF;&#x7A0B;&#x6267;&#x884C;&#xA0;event&#xA0;&#x7684;&#x6570;&#x91CF;<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;# stddev &#x662F;&#x6807;&#x51C6;&#x5DEE;&#xFF0C;&#x503C;&#x8D8A;&#x5C0F;&#xFF0C;&#x4EE3;&#x8868;&#x7ED3;&#x679C;&#x8D8A;&#x7A33;&#x5B9A;&#x3002;<br>&#xA0;&#xA0;&#xA0;&#xA0;execution&#xA0;time&#xA0;(avg/stddev):&#xA0;&#xA0;&#xA0;60.0007/0.01&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;#&#xA0;&#x5E73;&#x5747;&#x6BCF;&#x4E2A;&#x7EBF;&#x7A0B;&#x7684;&#x6267;&#x884C;&#x65F6;&#x95F4;

输出中,重点关注三个指标:

TPS 和 QPS 反映了系统的吞吐量,越大越好。执行耗时代表了事务的执行时长,越小越好。在一定范围内,并发线程数指定得越大,TPS 和 QPS 也会越高。

使用 sysbench 对服务器进行测试

除了数据库基准测试,sysbench 还能对服务器的性能进行测试。服务器资源一般包括四大类:CPU、内存、IO和网络。sysbench 可对CPU、内存和磁盘IO进行测试。下面我们具体来看看。

CPU 性能测试。支持的选项只有一个,即 --cpu-max-prime

CPU 测试的命令如下:

#&#xA0;sysbench&#xA0;cpu&#xA0;--cpu-max-prime=20000&#xA0;--threads=32&#xA0;run

输出中,重点关注 events per second。值越大,代表 CPU 的计算性能越强。

CPU&#xA0;speed:<br>&#xA0;&#xA0;&#xA0;&#xA0;events&#xA0;per&#xA0;second:&#xA0;25058.08

下面是 CPU 测试相关的代码,可以看到,sysbench 是通过计算 --cpu-max-prime范围内的质数来衡量 CPU 的计算能力的。

质数(prime number)又称素数,指的是大于 1,且只能被 1 和自身整除的自然数。在代码实现时,对于自然数 n,一般会用 2 到根号 n 之间的整数去除,如果都无法整除,则意味着 n 是个质数。

int&#xA0;cpu_execute_event(sb_event_t&#xA0;*r,&#xA0;int&#xA0;thread_id)<br>{<br>&#xA0;&#xA0;unsigned&#xA0;long&#xA0;long&#xA0;c;<br>&#xA0;&#xA0;unsigned&#xA0;long&#xA0;long&#xA0;l;<br>&#xA0;&#xA0;double&#xA0;t;<br>&#xA0;&#xA0;unsigned&#xA0;long&#xA0;long&#xA0;n=0;<br><br>&#xA0;&#xA0;(void)thread_id;&#xA0;/*&#xA0;unused&#xA0;*/<br>&#xA0;&#xA0;(void)r;&#xA0;/*&#xA0;unused&#xA0;*/<br><br>&#xA0;&#xA0;//&#xA0;max_prime&#xA0;&#x5373;&#x547D;&#x4EE4;&#x884C;&#x4E2D;&#x6307;&#x5B9A;&#x7684;&#xA0;--cpu-max-prime<br>&#xA0;&#xA0;for(c=3;&#xA0;c&#xA0;< max_prime; c++)<br>&#xA0;&#xA0;{<br>&#xA0;&#xA0;&#xA0;&#xA0;t&#xA0;=&#xA0;sqrt((double)c);<br>&#xA0;&#xA0;&#xA0;&#xA0;for(l&#xA0;=&#xA0;2;&#xA0;l&#xA0;<= t; l++)<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;if&#xA0;(c&#xA0;%&#xA0;l&#xA0;==&#xA0;0)<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;break;<br>&#xA0;&#xA0;&#xA0;&#xA0;if&#xA0;(l&#xA0;>&#xA0;t&#xA0;)<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;n++;<br>&#xA0;&#xA0;}<br><br>&#xA0;&#xA0;return&#xA0;0;<br>}<br></= t; l++)<br></ max_prime; c++)

内存测试,支持的选项有:

  • –memory-block-size:内存块的大小,默认为 1KB。测试时建议设置为 1MB。
  • –memory-total-size:要传输的数据的总大小。默认为 100GB。
  • –memory-scope:内存访问范围,可指定 global、local,默认为 global。
  • –memory-hugetlb:是否从 HugeTLB 池中分配内存,默认为 off。
  • –memory-oper:内存操作类型,可指定 read、write、none,默认为 write。
  • –memory-access-mode:内存访问模式,可指定 seq(顺序访问)、rnd(随机访问),默认为 seq。

内存测试的命令如下:

#&#xA0;sysbench&#xA0;--test=memory&#xA0;--memory-block-size=1M&#xA0;--memory-total-size=100G&#xA0;--num-threads=1&#xA0;run

输出中,重点关注以下部分:

102400.00&#xA0;MiB&#xA0;transferred&#xA0;(23335.96&#xA0;MiB/sec)

23335.96 MiB/sec 即数据在内存中的顺序写入速率。

磁盘 IO 测试。支持的选项有:

  • –file-num:需要创建的文件数,默认为128。
  • –file-block-size:数据块的大小,默认为16384,即16KB。
  • –file-total-size:需要创建的文件总大小,默认为2GB。
  • –file-test-mode:测试模式,可指定 seqwr(顺序写)、seqrewr(顺序重写)、seqrd(顺序读)、rndrd(随机读)、rndwr(随机写)、rndrw(随机读写)。
  • –file-io-mode:文件的操作模式,可指定 sync(同步 IO)、async(异步 IO)、mmap,默认为 sync。
  • –file-async-backlog:每个线程异步 IO 队列的长度,默认为 128。
  • –file-extra-flags:打开文件时指定的标志,可指定 sync、dsync、direct,默认为空,没指定。
  • –file-fsync-freq:指定持久化操作的频率,默认为 100,即每执行 100 个 IO 请求,则会进行一次持久化操作。
  • –file-fsync-all:在每次写入操作后执行持久化操作,默认为 off。
  • –file-fsync-end:在测试结束时执行持久化操作,默认为 on。
  • –file-fsync-mode:持久化操作的模式,可指定 fsync、fdatasync,默认为 fsync。fdatasync 和 fsync类似,只不过 fdatasync 只会更新数据,而 fsync 还会同步更新文件的属性。
  • –file-merged-requests:允许合并的最多 IO 请求数,默认为0,不合并。
  • –file-rw-ratio:混合测试中的读写比例,默认为1.5。

磁盘 IO 测试主要分为以下三步:

#&#xA0;&#x51C6;&#x5907;&#x6D4B;&#x8BD5;&#x6587;&#x4EF6;<br>#&#xA0;sysbench&#xA0;fileio&#xA0;--file-num=1&#xA0;--file-total-size=10G&#xA0;--file-test-mode=rndrw&#xA0;prepare<br><br>#&#xA0;&#x6D4B;&#x8BD5;<br>#&#xA0;sysbench&#xA0;fileio&#xA0;--file-num=1&#xA0;--file-total-size=10G&#xA0;--file-test-mode=rndrw&#xA0;run<br><br>#&#xA0;&#x5220;&#x9664;&#x6D4B;&#x8BD5;&#x6587;&#x4EF6;<br>#&#xA0;sysbench&#xA0;fileio&#xA0;--file-num=1&#xA0;--file-total-size=10G&#xA0;--file-test-mode=rndrw&#xA0;cleanup

输出中,重点关注以下两部分:

File&#xA0;operations:<br>&#xA0;&#xA0;&#xA0;&#xA0;reads/s:&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;4978.26<br>&#xA0;&#xA0;&#xA0;&#xA0;writes/s:&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;3318.84<br>&#xA0;&#xA0;&#xA0;&#xA0;fsyncs/s:&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;83.07<br><br>Throughput:<br>&#xA0;&#xA0;&#xA0;&#xA0;read,&#xA0;MiB/s:&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;77.79<br>&#xA0;&#xA0;&#xA0;&#xA0;written,&#xA0;MiB/s:&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;51.86

其中,reads/s 加上 writes/s 即我们常说的 IOPS。read, MiB/s 加上 written, MiB/s 即我们常说的吞吐量。

MySQL 常见测试场景及对应的 SQL 语句

接下来会列举 MySQL 常见的测试场景及各个场景对应的 SQL 语句。

为了让大家清晰的知道 SQL 语句的含义,首先我们看看测试表的表结构。

除了 bulk_insert 会创建单独的测试表,其它场景都会使用下面的表结构。

mysql>&#xA0;show&#xA0;create&#xA0;table&#xA0;sbtest.sbtest1\G<br>***************************&#xA0;1.&#xA0;row&#xA0;***************************<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;Table:&#xA0;sbtest1<br>Create&#xA0;Table:&#xA0;CREATE&#xA0;TABLE&#xA0;sbtest1&#xA0;(<br>&#xA0;&#xA0;id&#xA0;int&#xA0;NOT&#xA0;NULL&#xA0;AUTO_INCREMENT,<br>&#xA0;&#xA0;k&#xA0;int&#xA0;NOT&#xA0;NULL&#xA0;DEFAULT&#xA0;'0',<br>&#xA0;&#xA0;c&#xA0;char(120)&#xA0;NOT&#xA0;NULL&#xA0;DEFAULT&#xA0;'',<br>&#xA0;&#xA0;pad&#xA0;char(60)&#xA0;NOT&#xA0;NULL&#xA0;DEFAULT&#xA0;'',<br>&#xA0;&#xA0;PRIMARY&#xA0;KEY&#xA0;(id),<br>&#xA0;&#xA0;KEY&#xA0;k_1&#xA0;(k)<br>)&#xA0;ENGINE=InnoDB&#xA0;AUTO_INCREMENT=1000001&#xA0;DEFAULT&#xA0;CHARSET=utf8mb4&#xA0;COLLATE=utf8mb4_0900_ai_ci<br>1&#xA0;row&#xA0;in&#xA0;set&#xA0;(0.00&#xA0;sec)

批量插入测试。

#&#xA0;sysbench&#xA0;bulk_insert&#xA0;--mysql-host=10.0.0.64&#xA0;--mysql-port=3306&#xA0;--mysql-user=admin&#xA0;--mysql-password=Py@123456&#xA0;--mysql-db=sbtest&#xA0;--tables=30&#xA0;--table-size=1000000&#xA0;--threads=64&#xA0;--time=60&#xA0;--report-interval=10&#xA0;run

下面是 bulk_insert 场景下创建的测试表。

mysql>&#xA0;show&#xA0;create&#xA0;table&#xA0;sbtest.sbtest1\G<br>***************************&#xA0;1.&#xA0;row&#xA0;***************************<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;Table:&#xA0;sbtest1<br>Create&#xA0;Table:&#xA0;CREATE&#xA0;TABLE&#xA0;sbtest1&#xA0;(<br>&#xA0;&#xA0;id&#xA0;int&#xA0;NOT&#xA0;NULL,<br>&#xA0;&#xA0;k&#xA0;int&#xA0;NOT&#xA0;NULL&#xA0;DEFAULT&#xA0;'0',<br>&#xA0;&#xA0;PRIMARY&#xA0;KEY&#xA0;(id)<br>)&#xA0;ENGINE=InnoDB&#xA0;DEFAULT&#xA0;CHARSET=utf8mb4&#xA0;COLLATE=utf8mb4_0900_ai_ci<br>1&#xA0;row&#xA0;in&#xA0;set&#xA0;(0.01&#xA0;sec)

测试对应的 SQL 语句如下:

INSERT&#xA0;INTO&#xA0;sbtest1&#xA0;VALUES(?,&#xA0;?),(?,&#xA0;?),(?,&#xA0;?),(?,&#xA0;?)...

删除测试。

#&#xA0;sysbench&#xA0;oltp_delete&#xA0;--mysql-host=10.0.0.64&#xA0;--mysql-port=3306&#xA0;--mysql-user=admin&#xA0;--mysql-password=Py@123456&#xA0;--mysql-db=sbtest&#xA0;--tables=30&#xA0;--table-size=1000000&#xA0;--threads=64&#xA0;--time=60&#xA0;--report-interval=10&#xA0;run

基于主键进行删除。测试对应的 SQL 语句如下:

DELETE&#xA0;FROM&#xA0;sbtest1&#xA0;WHERE&#xA0;id=?

插入测试。

#&#xA0;sysbench&#xA0;oltp_insert&#xA0;--mysql-host=10.0.0.64&#xA0;--mysql-port=3306&#xA0;--mysql-user=admin&#xA0;--mysql-password=Py@123456&#xA0;--mysql-db=sbtest&#xA0;--tables=30&#xA0;--table-size=1000000&#xA0;--threads=64&#xA0;--time=60&#xA0;--report-interval=10&#xA0;run

测试对应的 SQL 语句如下:

INSERT&#xA0;INTO&#xA0;sbtest1&#xA0;(id,&#xA0;k,&#xA0;c,&#xA0;pad)&#xA0;VALUES&#xA0;(?,&#xA0;?,&#xA0;?,&#xA0;?)

基于主键进行查询。

#&#xA0;sysbench&#xA0;oltp_point_select&#xA0;--mysql-host=10.0.0.64&#xA0;--mysql-port=3306&#xA0;--mysql-user=admin&#xA0;--mysql-password=Py@123456&#xA0;--mysql-db=sbtest&#xA0;--tables=30&#xA0;--table-size=1000000&#xA0;--threads=64&#xA0;--time=60&#xA0;--report-interval=10&#xA0;run

测试对应的 SQL 语句如下:

SELECT&#xA0;c&#xA0;FROM&#xA0;sbtest1&#xA0;WHERE&#xA0;id=?

只读测试。

#&#xA0;sysbench&#xA0;oltp_read_only&#xA0;--mysql-host=10.0.0.64&#xA0;--mysql-port=3306&#xA0;--mysql-user=admin&#xA0;--mysql-password=Py@123456&#xA0;--mysql-db=sbtest&#xA0;--tables=30&#xA0;--table-size=1000000&#xA0;--threads=64&#xA0;--time=60&#xA0;--report-interval=10&#xA0;run

测试对应的 SQL 语句如下:

SELECT&#xA0;c&#xA0;FROM&#xA0;sbtest1&#xA0;WHERE&#xA0;id=?&#xA0;#&#xA0;&#x9ED8;&#x8BA4;&#x4F1A;&#x6267;&#x884C; 10&#xA0;&#x6B21;&#xFF0C;&#x7531;&#xA0;--point_selects &#x9009;&#x9879;&#x63A7;&#x5236;&#x3002;<br>SELECT&#xA0;c&#xA0;FROM&#xA0;sbtest1&#xA0;WHERE&#xA0;id&#xA0;BETWEEN&#xA0;?&#xA0;AND&#xA0;?<br>SELECT&#xA0;SUM(k)&#xA0;FROM&#xA0;sbtest1&#xA0;WHERE&#xA0;id&#xA0;BETWEEN&#xA0;?&#xA0;AND&#xA0;?<br>SELECT&#xA0;c&#xA0;FROM&#xA0;sbtest1&#xA0;WHERE&#xA0;id&#xA0;BETWEEN&#xA0;?&#xA0;AND&#xA0;?&#xA0;ORDER&#xA0;BY&#xA0;c<br>SELECT&#xA0;DISTINCT&#xA0;c&#xA0;FROM&#xA0;sbtest1&#xA0;WHERE&#xA0;id&#xA0;BETWEEN&#xA0;?&#xA0;AND&#xA0;?&#xA0;ORDER&#xA0;BY&#xA0;c

读写测试。

测试对应的 SQL 语句如下:

SELECT&#xA0;c&#xA0;FROM&#xA0;sbtest1&#xA0;WHERE&#xA0;id=?&#xA0;#&#xA0;&#x9ED8;&#x8BA4;&#x4F1A;&#x6267;&#x884C; 10&#xA0;&#x6B21;&#xFF0C;&#x7531;&#xA0;--point_selects &#x9009;&#x9879;&#x63A7;&#x5236;&#x3002;<br>SELECT&#xA0;c&#xA0;FROM&#xA0;sbtest1&#xA0;WHERE&#xA0;id&#xA0;BETWEEN&#xA0;?&#xA0;AND&#xA0;?<br>SELECT&#xA0;SUM(k)&#xA0;FROM&#xA0;sbtest1&#xA0;WHERE&#xA0;id&#xA0;BETWEEN&#xA0;?&#xA0;AND&#xA0;?<br>SELECT&#xA0;c&#xA0;FROM&#xA0;sbtest1&#xA0;WHERE&#xA0;id&#xA0;BETWEEN&#xA0;?&#xA0;AND&#xA0;?&#xA0;ORDER&#xA0;BY&#xA0;c<br>SELECT&#xA0;DISTINCT&#xA0;c&#xA0;FROM&#xA0;sbtest1&#xA0;WHERE&#xA0;id&#xA0;BETWEEN&#xA0;?&#xA0;AND&#xA0;?&#xA0;ORDER&#xA0;BY&#xA0;c<br>UPDATE&#xA0;sbtest1&#xA0;SET&#xA0;k=k+1&#xA0;WHERE&#xA0;id=?<br>UPDATE&#xA0;sbtest1&#xA0;SET&#xA0;c=?&#xA0;WHERE&#xA0;id=?<br>DELETE&#xA0;FROM&#xA0;sbtest1&#xA0;WHERE&#xA0;id=?<br>INSERT&#xA0;INTO&#xA0;sbtest1&#xA0;(id,&#xA0;k,&#xA0;c,&#xA0;pad)&#xA0;VALUES&#xA0;(?,&#xA0;?,&#xA0;?,&#xA0;?)

基于主键进行更新,更新的是索引字段。

#&#xA0;sysbench&#xA0;oltp_update_index&#xA0;--mysql-host=10.0.0.64&#xA0;--mysql-port=3306&#xA0;--mysql-user=admin&#xA0;--mysql-password=Py@123456&#xA0;--mysql-db=sbtest&#xA0;--tables=30&#xA0;--table-size=1000000&#xA0;--threads=64&#xA0;--time=60&#xA0;--report-interval=10&#xA0;run

测试对应的 SQL 语句如下:

UPDATE&#xA0;sbtest1&#xA0;SET&#xA0;k=k+1&#xA0;WHERE&#xA0;id=?

基于主键进行更新,更新的是非索引字段。

#&#xA0;sysbench&#xA0;oltp_update_non_index&#xA0;--mysql-host=10.0.0.64&#xA0;--mysql-port=3306&#xA0;--mysql-user=admin&#xA0;--mysql-password=Py@123456&#xA0;--mysql-db=sbtest&#xA0;--tables=30&#xA0;--table-size=1000000&#xA0;--threads=64&#xA0;--time=60&#xA0;--report-interval=10&#xA0;run

测试对应的 SQL 语句如下:

UPDATE&#xA0;sbtest1&#xA0;SET&#xA0;c=?&#xA0;WHERE&#xA0;id=?

只写测试。

#&#xA0;sysbench&#xA0;oltp_write_only&#xA0;--mysql-host=10.0.0.64&#xA0;--mysql-port=3306&#xA0;--mysql-user=admin&#xA0;--mysql-password=Py@123456&#xA0;--mysql-db=sbtest&#xA0;--tables=30&#xA0;--table-size=1000000&#xA0;--threads=64&#xA0;--time=60&#xA0;--report-interval=10&#xA0;run

测试对应的 SQL 语句如下:

UPDATE&#xA0;sbtest1&#xA0;SET&#xA0;k=k+1&#xA0;WHERE&#xA0;id=?<br>UPDATE&#xA0;sbtest1&#xA0;SET&#xA0;c=?&#xA0;WHERE&#xA0;id=?<br>DELETE&#xA0;FROM&#xA0;sbtest1&#xA0;WHERE&#xA0;id=?<br>INSERT&#xA0;INTO&#xA0;sbtest1&#xA0;(id,&#xA0;k,&#xA0;c,&#xA0;pad)&#xA0;VALUES&#xA0;(?,&#xA0;?,&#xA0;?,&#xA0;?)

基于索引进行随机查询。

#&#xA0;sysbench&#xA0;select_random_points&#xA0;--mysql-host=10.0.0.64&#xA0;--mysql-port=3306&#xA0;--mysql-user=admin&#xA0;--mysql-password=Py@123456&#xA0;--mysql-db=sbtest&#xA0;--tables=30&#xA0;--table-size=1000000&#xA0;--threads=64&#xA0;--time=60&#xA0;--report-interval=10&#xA0;run

测试对应的 SQL 语句如下:

SELECT&#xA0;id,&#xA0;k,&#xA0;c,&#xA0;pad<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;FROM&#xA0;sbtest1<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;WHERE&#xA0;k&#xA0;IN&#xA0;(?,&#xA0;?,&#xA0;?,&#xA0;?,&#xA0;?,&#xA0;?,&#xA0;?,&#xA0;?,&#xA0;?,&#xA0;?)

基于索引进行随机范围查询。

#&#xA0;sysbench&#xA0;select_random_ranges&#xA0;--mysql-host=10.0.0.64&#xA0;--mysql-port=3306&#xA0;--mysql-user=admin&#xA0;--mysql-password=Py@123456&#xA0;--mysql-db=sbtest&#xA0;--tables=30&#xA0;--table-size=1000000&#xA0;--threads=64&#xA0;--time=60&#xA0;--report-interval=10&#xA0;run

测试对应的 SQL 语句如下:

SELECT&#xA0;count(k)<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;FROM&#xA0;sbtest1<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;WHERE&#xA0;k&#xA0;BETWEEN&#xA0;?&#xA0;AND&#xA0;?&#xA0;OR&#xA0;k&#xA0;BETWEEN&#xA0;?&#xA0;AND&#xA0;?&#xA0;OR&#xA0;k&#xA0;BETWEEN&#xA0;?&#xA0;AND&#xA0;?&#xA0;OR&#xA0;k&#xA0;BETWEEN&#xA0;?&#xA0;AND&#xA0;?&#xA0;OR&#xA0;k&#xA0;BETWEEN&#xA0;?&#xA0;AND&#xA0;?&#xA0;OR&#xA0;k&#xA0;BETWEEN&#xA0;?&#xA0;AND&#xA0;?&#xA0;OR&#xA0;k&#xA0;BETWEEN&#xA0;?&#xA0;AND&#xA0;?&#xA0;OR&#xA0;k&#xA0;BETWEEN&#xA0;?&#xA0;AND&#xA0;?&#xA0;OR&#xA0;k&#xA0;BETWEEN&#xA0;?&#xA0;AND&#xA0;?&#xA0;OR&#xA0;k&#xA0;BETWEEN&#xA0;?&#xA0;AND&#xA0;?

如何自定义 sysbench 测试脚本

下面通过 bulk_insert.lua 和 oltp_point_select.lua 这两个脚本分析下 sysbench 测试脚本的实现逻辑。

首先看看 bulk_insert.lua。

#&#xA0;cat&#xA0;bulk_insert.lua<br>#!/usr/bin/env&#xA0;sysbench<br><br>cursize=0<br>function&#xA0;thread_init()<br>&#xA0;&#xA0;&#xA0;drv&#xA0;=&#xA0;sysbench.sql.driver()<br>&#xA0;&#xA0;&#xA0;con&#xA0;=&#xA0;drv:connect()<br>end<br><br>function&#xA0;prepare()<br>&#xA0;&#xA0;&#xA0;local&#xA0;i<br><br>&#xA0;&#xA0;&#xA0;local&#xA0;drv&#xA0;=&#xA0;sysbench.sql.driver()<br>&#xA0;&#xA0;&#xA0;local&#xA0;con&#xA0;=&#xA0;drv:connect()<br><br>&#xA0;&#xA0;&#xA0;for&#xA0;i&#xA0;=&#xA0;1,&#xA0;sysbench.opt.threads&#xA0;do<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;print("Creating&#xA0;table&#xA0;'sbtest"&#xA0;..&#xA0;i&#xA0;..&#xA0;"'...")<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;con:query(string.format([[<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;CREATE&#xA0;TABLE&#xA0;IF&#xA0;NOT&#xA0;EXISTS&#xA0;sbtest%d&#xA0;(<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;id&#xA0;INTEGER&#xA0;NOT&#xA0;NULL,<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;k&#xA0;INTEGER&#xA0;DEFAULT&#xA0;'0'&#xA0;NOT&#xA0;NULL,<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;PRIMARY&#xA0;KEY&#xA0;(id))]],&#xA0;i))<br>&#xA0;&#xA0;&#xA0;end<br>end<br><br>function&#xA0;event()<br>&#xA0;&#xA0;&#xA0;if&#xA0;(cursize&#xA0;==&#xA0;0)&#xA0;then<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;con:bulk_insert_init("INSERT&#xA0;INTO&#xA0;sbtest"&#xA0;..&#xA0;thread_id+1&#xA0;..&#xA0;"&#xA0;VALUES")<br>&#xA0;&#xA0;&#xA0;end<br><br>&#xA0;&#xA0;&#xA0;cursize&#xA0;=&#xA0;cursize&#xA0;+&#xA0;1<br><br>&#xA0;&#xA0;&#xA0;con:bulk_insert_next("("&#xA0;..&#xA0;cursize&#xA0;..&#xA0;","&#xA0;..&#xA0;cursize&#xA0;..&#xA0;")")<br>end<br><br>function&#xA0;thread_done(thread_9d)<br>&#xA0;&#xA0;&#xA0;con:bulk_insert_done()<br>&#xA0;&#xA0;&#xA0;con:disconnect()<br>end<br><br>function&#xA0;cleanup()<br>&#xA0;&#xA0;&#xA0;local&#xA0;i<br><br>&#xA0;&#xA0;&#xA0;local&#xA0;drv&#xA0;=&#xA0;sysbench.sql.driver()<br>&#xA0;&#xA0;&#xA0;local&#xA0;con&#xA0;=&#xA0;drv:connect()<br><br>&#xA0;&#xA0;&#xA0;for&#xA0;i&#xA0;=&#xA0;1,&#xA0;sysbench.opt.threads&#xA0;do<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;print("Dropping&#xA0;table&#xA0;'sbtest"&#xA0;..&#xA0;i&#xA0;..&#xA0;"'...")<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;con:query("DROP&#xA0;TABLE&#xA0;IF&#xA0;EXISTS&#xA0;sbtest"&#xA0;..&#xA0;i&#xA0;)<br>&#xA0;&#xA0;&#xA0;end<br>end

下面,我们看看这几个函数的具体作用:

  • thread_init():线程初始化时调用。这个函数常用来创建数据库连接。
  • prepare():指定 prepare 时调用。这个函数常用来创建测试表,生成测试数据。
  • event():指定 run 时调用。这个函数会定义需要测试的 SQL 语句。
  • thread_done():线程退出时调用。这个函数常用来关闭 Prepared Statements 和数据库连接。
  • cleanup():指定 cleanup 时调用。这个函数常用来删除测试表。

如果我们要自定义测试脚本,只需实现这几个函数即可。

如果我们要基于 sbtest 表自定义测试项,就要分析 oltp*.lua 脚本的实现逻辑。

下面,以 oltp_point_select.lua 脚本为例。

#!/usr/bin/env&#xA0;sysbench<br>...<br>require("oltp_common")<br><br>function&#xA0;prepare_statements()<br>&#xA0;&#xA0;&#xA0;-- point_selects &#x662F; oltp_point_select &#x4E2D;&#x652F;&#x6301;&#x7684;&#x9009;&#x9879;&#xFF0C;&#x9ED8;&#x8BA4;&#x4E3A; 10&#xFF0C;&#x8FD9;&#x91CC;&#x8C03;&#x6574;&#x4E3A;&#x4E86; 1&#x3002;<br>&#xA0;&#xA0;&#xA0;sysbench.opt.point_selects=1<br><br>&#xA0;&#xA0;&#xA0;prepare_point_selects()<br>end<br><br>function&#xA0;event()<br>&#xA0;&#xA0;&#xA0;execute_point_selects()<br>end

与 bulk_insert.lua 不一样的是,oltp_point_select.lua 只简单的定义了两个函数: prepare_statements()event()。实际上,不仅仅是 oltp_point_select.lua,其它 oltp*.lua 脚本也只定义了这两个函数。

虽然只定义了这两个函数,但脚本导入了 oltp_common 模块,所以实际上,脚本中的 prepare_point_selects(),execute_point_selects() 以及 bulk_insert.lua 中的 thread_init(),prepare(),thread_done(),cleanup() 都是在 oltp_common.lua这个公共模块中定义的。

接下来,我们看看 prepare_point_selects() 和 execute_point_selects() 这两个函数的实现逻辑。

首先看看 prepare_point_selects()

它调用的是 prepare_for_each_table()。prepare_for_each_table()是一个基础函数。所有prepare 相关的函数都会调用prepare_for_each_table(), 只不过不同的 prepare 函数会传入不同的参数名。

prepare_for_each_table()会填充两张表(Lua 中的表既可用来表示数组,也可用来表示集合):stmt 和 param。其中,stmt 用来存储 Prepared Statements 语句,param 用来存储 Prepared Statements 语句相关的参数类型。

填充完毕后,最后再通过 bind_param 函数将两者绑定在一起。

可以看到,无论是 Prepared Statements 语句还是相关的参数类型,都是在 stmt_defs 定义的。

function&#xA0;prepare_point_selects()<br>&#xA0;&#xA0;&#xA0;prepare_for_each_table("point_selects")<br>end<br><br>function&#xA0;prepare_for_each_table(key)<br>&#xA0;&#xA0;&#xA0;for&#xA0;t&#xA0;=&#xA0;1,&#xA0;sysbench.opt.tables&#xA0;do<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;--&#xA0;t&#xA0;&#x662F;&#x8868;&#x7684;&#x5E8F;&#x53F7;&#xFF0C;key&#xA0;&#x662F;&#x6D4B;&#x8BD5;&#x9879;&#x7684;&#x540D;&#x5B57;<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;stmt[t][key]&#xA0;=&#xA0;con:prepare(string.format(stmt_defs[key][1],&#xA0;t))<br>&#xA0;&#xA0;&#xA0;&#xA0;<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;local&#xA0;nparam&#xA0;=&#xA0;#stmt_defs[key]&#xA0;-&#xA0;1<br><br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;if&#xA0;nparam&#xA0;>&#xA0;0&#xA0;then<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;param[t][key]&#xA0;=&#xA0;{}<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;end<br><br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;for&#xA0;p&#xA0;=&#xA0;1,&#xA0;nparam&#xA0;do<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;local&#xA0;btype&#xA0;=&#xA0;stmt_defs[key][p+1]<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;local&#xA0;len<br><br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;if&#xA0;type(btype)&#xA0;==&#xA0;"table"&#xA0;then<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;len&#xA0;=&#xA0;btype[2]<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;btype&#xA0;=&#xA0;btype[1]<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;end<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;if&#xA0;btype&#xA0;==&#xA0;sysbench.sql.type.VARCHAR&#xA0;or<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;btype&#xA0;==&#xA0;sysbench.sql.type.CHAR&#xA0;then<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;param[t][key][p]&#xA0;=&#xA0;stmt[t][key]:bind_create(btype,&#xA0;len)<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;else<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;param[t][key][p]&#xA0;=&#xA0;stmt[t][key]:bind_create(btype)<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;end<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;end<br><br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;if&#xA0;nparam&#xA0;>&#xA0;0&#xA0;then<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;stmt[t][key]:bind_param(unpack(param[t][key]))<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;end<br>&#xA0;&#xA0;&#xA0;end<br>end

接下来,我们看看 stmt_defs 的内容。

local&#xA0;stmt_defs&#xA0;=&#xA0;{<br>&#xA0;&#xA0;&#xA0;point_selects&#xA0;=&#xA0;{<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;"SELECT&#xA0;c&#xA0;FROM&#xA0;sbtest%u&#xA0;WHERE&#xA0;id=?",<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;t.INT},<br>&#xA0;&#xA0;&#xA0;simple_ranges&#xA0;=&#xA0;{<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;"SELECT&#xA0;c&#xA0;FROM&#xA0;sbtest%u&#xA0;WHERE&#xA0;id&#xA0;BETWEEN&#xA0;?&#xA0;AND&#xA0;?",<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;t.INT,&#xA0;t.INT},<br>&#xA0;&#xA0;&#xA0;sum_ranges&#xA0;=&#xA0;{<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;"SELECT&#xA0;SUM(k)&#xA0;FROM&#xA0;sbtest%u&#xA0;WHERE&#xA0;id&#xA0;BETWEEN&#xA0;?&#xA0;AND&#xA0;?",<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;t.INT,&#xA0;t.INT},<br>&#xA0;&#xA0;&#xA0;order_ranges&#xA0;=&#xA0;{<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;"SELECT&#xA0;c&#xA0;FROM&#xA0;sbtest%u&#xA0;WHERE&#xA0;id&#xA0;BETWEEN&#xA0;?&#xA0;AND&#xA0;?&#xA0;ORDER&#xA0;BY&#xA0;c",<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;t.INT,&#xA0;t.INT},<br>&#xA0;&#xA0;&#xA0;distinct_ranges&#xA0;=&#xA0;{<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;"SELECT&#xA0;DISTINCT&#xA0;c&#xA0;FROM&#xA0;sbtest%u&#xA0;WHERE&#xA0;id&#xA0;BETWEEN&#xA0;?&#xA0;AND&#xA0;?&#xA0;ORDER&#xA0;BY&#xA0;c",<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;t.INT,&#xA0;t.INT},<br>&#xA0;&#xA0;&#xA0;index_updates&#xA0;=&#xA0;{<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;"UPDATE&#xA0;sbtest%u&#xA0;SET&#xA0;k=k+1&#xA0;WHERE&#xA0;id=?",<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;t.INT},<br>&#xA0;&#xA0;&#xA0;non_index_updates&#xA0;=&#xA0;{<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;"UPDATE&#xA0;sbtest%u&#xA0;SET&#xA0;c=?&#xA0;WHERE&#xA0;id=?",<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;{t.CHAR,&#xA0;120},&#xA0;t.INT},<br>&#xA0;&#xA0;&#xA0;deletes&#xA0;=&#xA0;{<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;"DELETE&#xA0;FROM&#xA0;sbtest%u&#xA0;WHERE&#xA0;id=?",<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;t.INT},<br>&#xA0;&#xA0;&#xA0;inserts&#xA0;=&#xA0;{<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;"INSERT&#xA0;INTO&#xA0;sbtest%u&#xA0;(id,&#xA0;k,&#xA0;c,&#xA0;pad)&#xA0;VALUES&#xA0;(?,&#xA0;?,&#xA0;?,&#xA0;?)",<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;t.INT,&#xA0;t.INT,&#xA0;{t.CHAR,&#xA0;120},&#xA0;{t.CHAR,&#xA0;60}},<br>}

可以看到,stmt_defs 是一张表,里面定义了不同测试项对应的 Prepared Statements 语句和参数类型。

具体到 point_selects 这个测试项,它对应的 Prepared Statements 语句是 SELECT c FROM sbtest%u WHERE id=?,对应的参数类型是 t.INT

梳理完 prepare_point_selects() 函数的实现逻辑。最后我们看看 execute_point_selects()函数的实现逻辑。

function&#xA0;execute_point_selects()<br>&#xA0;&#xA0;&#xA0;local&#xA0;tnum&#xA0;=&#xA0;get_table_num()<br>&#xA0;&#xA0;&#xA0;local&#xA0;i<br>&#xA0;&#xA0;&#xA0;-- point_selects &#x5BF9;&#x5E94;&#x547D;&#x4EE4;&#x884C;&#x4E2D;&#x7684;&#xA0;--point_selects &#x9009;&#x9879;&#xFF0C;&#x9ED8;&#x8BA4;&#x4E3A; 10&#x3002;<br>&#xA0;&#xA0;&#xA0;for&#xA0;i&#xA0;=&#xA0;1,&#xA0;sysbench.opt.point_selects&#xA0;do<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;param[tnum].point_selects[1]:set(get_id())<br><br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;stmt[tnum].point_selects:execute()<br>&#xA0;&#xA0;&#xA0;end<br>end

逻辑也非常简单,先赋值,最后执行。

所以如果我们要基于 sbtest 表自定义测试项,最关键的一步其实就是在 stmt_defs 中定义 Prepared Statements 语句和相关的参数类型。至于 prepare_xxx 和 execute_xxx 函数,实现起来都非常简单。

  1. 基准测试一般会关注三个指标:TPS/QPS、响应耗时和并发量。

  2. 只有进行全链路压测,我们才知道系统的瓶颈在哪里。不能想当然的以为,数据库不容易横向扩展,系统瓶颈就一定会出在数据库层。事实上,很多系统在设计之初就引入了缓存,而缓存会分担很大一部分读流量,这种架构下的数据库压力其实并不大。

  3. 不能简单的将 sysbench 的测试结果(TPS/QPS) 作为业务系统的吞吐量指标,因为两者的业务模型并不一致。

  4. 如果要自定义测试脚本,实现的方式有两种:

  5. 自己实现测试相关的所有函数,具体实现细节可参考 bulk_insert.lua。

  6. 基于 sbtest 表自定义测试项。实现过程中最关键的一步是在 stmt_defs 中定义 Prepared Statements 语句和相关的参数类型。

Original: https://www.cnblogs.com/ivictor/p/16955580.html
Author: iVictor
Title: MySQL 性能压测工具-sysbench,从入门到自定义测试项

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/797117/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • admin后台管理数据库里的表

    admin后台管理 django给您提供了一个可视化图形界面,来方便您来对数据库里的表进行增删改查的管理 但是!使用admin后台管理你自己注册的模型表时,需要自行进行先注册该表!…

    Python 2023年10月31日
    062
  • Alexnet论文介绍(超详细)——ImageNet Classification with Deep Convolutional Neural Networks

    近期开始阅读cv领域的一些经典论文,本文整理计算机视觉的奠基之作—— Alexnet 论文原文:ImageNet Classification with Deep Convolut…

    Python 2023年10月27日
    038
  • 【Flask】 路由入门

    为了给 URL 增加变量的部分,你需要把一些特定的字段标记成 <variable_name></variable_name> 。 这些特定的字段将作为参数传…

    Python 2023年8月11日
    055
  • 关于中蓄投资

    精神 传承水库精神,奋勇向前,福润万方。 使命 用投资让世界更美好! 愿景 用理想之光照亮奋斗之路,用信仰之力开创美好未来。中蓄投资立志成为全球价值投资的引领者。 理念 支持对生态…

    Python 2023年11月6日
    035
  • torch.nn.Parameter()函数的讲解和使用

    0. 引言 在学习SSD网络的时候发现源码里使用 nn.Parameter()这个函数,故对其进行了解。 1. 官方文档 torch.nn.parameter.Parameter(…

    Python 2023年10月11日
    039
  • Django Oracle后端 — cx_Oracle字符集

    使用Django3.2.5作为后端开发框架,数据库为Oracle11.2。据Django官方文档说明,要求Oracle Database的最低版本为12c。阅读过Django中OR…

    Python 2023年8月5日
    068
  • 深度学习基础之BatchNorm和LayerNorm

    文章目录 BatchNorm LayerNorm 总结 参考 BatchNorm Batch Normalization(下文简称 Batch Norm)是 2015 年提出的方法…

    Python 2023年9月16日
    052
  • matplotlib之坐标轴对象简单设置(Axes)

    本篇文章为笔者的学习笔记和心得,错漏之处请多多包含。 matplotlib是Python最著名的绘图库,它提供了一整套和MATLAB类似的绘图函数集,十分适合编写短小的脚本程序进行…

    Python 2023年8月31日
    086
  • Docker容器获取宿主机信息

    思路:在docker容器内安装ssh,sshpass服务,通过ssh连接到宿主机执行命令,获 取宿主机信息(必须知道宿主机Ip和密码) 步骤: 安装服务 yum -y instal…

    Python 2023年10月20日
    046
  • pytest框架的测试方法运行的优先级的设置

    pytest框架,运行顺序的方法:下面介绍一下通过mark标识来,来实现对方法的执行 一、跳过不执行的测试类:1、@pytest.mark.skip()标签,用他们装饰测试类imp…

    Python 2023年9月10日
    045
  • win10 软件资源包管理——scoop

    包管理器: 通过一条简洁的命令管理安装卸载软件的管理包,linux系统的标配。 scoop 是为win10平台的开发的包管理器,Scoop 由澳洲程序员Luke Sampson于2…

    Python 2023年6月10日
    078
  • 前端面试八股文(详细版)—上

    本文将详细讲解 HTML 、CSS 、JavaScript 、计算机网络知识等方面的内容,Vue 、React 、git 、项目开发实例等内容放在下篇,预祝各位成功上岸! 第一板块…

    Python 2023年9月25日
    050
  • web开发模式

    前后端不分离 返回的是html的内容,需要在服务端拿到数据库的数据,再渲染给模板层,最后将渲染好的模板返回给浏览器! 前后端分离 前后端分离:只需要在浏览器上运行JS代码,使用aj…

    Python 2023年6月9日
    080
  • React学习8(DOM的diff算法)

    1.虚拟DOM中Key的作用 Key是虚拟DOM对象中的标识,当状态中的数据发生变化时,react会根据新数据生成新的虚拟 DOM,随后,react进行新虚拟Dom与旧虚拟DOM的…

    Python 2023年8月22日
    054
  • flask—-蓝图

    蓝图:类似于主系统中的子系统。 蓝图注册3步骤: 1.定义蓝图:main = Blueprint(‘main’,name,url_prefix=&#8217…

    Python 2023年8月11日
    051
  • 1维线性回归

    w= 1.0595238095237538 b= -117.79761904760 undefined Original: https://www.cnblogs.com/canx…

    Python 2023年10月26日
    045
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球