He3DB 是由移动云数据库团队研发的一款计算/存储分离的云原生数据库,He3DB通过计算/存储分离、数据冷热分层和压缩、智能中间件等技术,来保证高性能和低成本完美兼得,在获得高性能的同时,最大化的帮助客户节省数据库使用成本。
本次主要介绍下He3DB与PostgreSQL的性能对比,以及对测试结果进行分析。
测试环境
He3DB采用的一主一推进的部署架构,由于He3DB改造了WAL日志模块,因此该部署模式与PostgreSQL的一主一半同步的效果类似,但是在测试写相关的操作时,会对PostgreSQL进行同步流复制和异步流复制两种同步方式的测试。
接下来,会分几篇文章对He3DB和PostgreSQL在不同的资源情况下进行测试,并对其进行分析。
资源不受限
He3DB和PostgreSQL以容器的方式运行,并且CPU、内存的资源不受限,计算层的databuffer可以覆盖测试数据。
数据准备:使用sysbench导入8张表,每张表500万条记录。
插入场景
使用以下命令对insert场景进行测试:
sysbench --db-driver=pgsql --pgsql-user=postgres --pgsql-password='xxx' --pgsql-db=postgres --events=0 --report-interval=5 --pgsql-host=xxx --pgsql-port=xxx --table-size=5000000 --tables=8 oltp_insert.lua run --time=120 --threads=xxx --rand-type='uniform'
insert场景下的TPS值:
| 并发数 | He3DB | PostgreSQL(主+半同步备) | PostgreSQL(主+异步备) |
|---|---|---|---|
| 16 | 2659.35 | 44875.07 | 74547.90 |
| 32 | 10352.36 | 75518.04 | 111616.26 |
| 64 | 26007.47 | 90484.12 | 154076.68 |
| 128 | 50388.38 | 102514.74 | 141294.04 |
| 256 | 81121.17 | 87586.67 | 125809.43 |
| 512 | 109125.34 | 71878.98 | 112207.38 |
从上面的测试结果可以看出:
1、随着并发数的逐渐增加,He3DB的TPS值持续增加,且增幅很大,而PostgreSQL增长幅度相对不大,并且在一定并发下TPS值达到峰值,并且TPS值随着并发的继续增加而逐渐降低。
2、在并发数小的情况下,He3DB的TPS值比PostgreSQL相差较大,而随着并发的增加,He3DB与PostgreSQL(主+半同步备)之间的TPS逐渐缩小并反超。
3、在不同并发下,He3DB的TPS都要比PostgreSQL(主+异步备)低,随着并发的增加,两者之间的差距逐渐缩小。
造成以上现象的原因,主要是在并发数小的情况下,wal写入的并发数比较小,而单次写入到TiKV的时延比写入到高性能磁盘的时延高很多,从而导致在小并发的情况下,He3DB与PostgreSQL的性能差距很大。随着并发数的增加,wal写入的并发数也在增加,单位时间内写入的wal也变多了,并且由于He3DB优化了wal写入的一些锁机制,以及去掉了FPW等操作,因此在大并发的情况,He3DB的性能会比PostgreSQL高很多。
测试分析
以下是在不同的并发下,写入wal到kv层的各耗时段的次数:
| 持久化wal耗时 | 16并发 | 32并发 | 64并发 | 128并发 | 256并发 | 512并发 |
|---|---|---|---|---|---|---|
| <1ms | 307642 | 3960 | 14491 | 18626 | 14819 | 7979 |
| 1~2ms | 6724 | 59947 | 140804 | 267818 | 380801 | 489253 |
| 2~3ms | 360 | 996 | 5393 | 22907 | 83829 | 120678 |
| 3~4ms | 122 | 151 | 1087 | 2173 | 11662 | 20981 |
| 4~5ms | 20 | 116 | 577 | 1555 | 7198 | 16198 |
| 5~6ms | 17 | 15 | 62 | 187 | 1109 | 4398 |
| 6~7ms | 3 | 3 | 9 | 36 | 188 | 831 |
| 7~8ms | 4 | 2 | 14 | 35 | 83 | 230 |
| 8~9ms | 2 | 4 | 28 | 52 | 120 | 216 |
| >9ms | 27 | 42 | 76 | 151 | 396 | 781 |
| 总计 | 314921 | 65236 | 162541 | 313540 | 500205 | 661545 |



从上面的表格和饼图可以看出:
1、在不同的并发数情况下,wal写入耗时绝大数处于1~2ms或者更低的范围内。
2、随着压测程序并发的增加(除了16并发),写入wal的次数也增加了,同时较高耗时的写入比例也有所增加。
3、写入wal的耗时最长时间超过了9ms,而且随着压测程序并发数的增加,超过9ms的写入次数基本呈倍数增加。
下面表格是关于insert场景下TPS与WAL写入的一些统计信息:
| 并发数 | TPS | 写入WAL次数 | 平均每秒写入的WAL次数 | 写入WAL总耗时(us) | 平均每次写入WAL的耗时(us) | WAL写入的平均并发数 | 写入WAL字节数 | 平均每次写入WAL的字节数 |
|---|---|---|---|---|---|---|---|---|
| 16 | 2659.35 | 314921 | 2624.34 | 162508831 | 516.03 | 1.35 | 141939632 | 450 |
| 32 | 10352.36 | 65236 | 543.63 | 87077324 | 1334.80 | 0.73 | 552506905 | 8469 |
| 64 | 26007.47 | 162541 | 1354.51 | 218940009 | 1346.98 | 1.82 | 1388077140 | 8539 |
| 128 | 50388.38 | 313540 | 2612.83 | 460179003 | 1467.69 | 3.83 | 2689764415 | 8579 |
| 256 | 81121.17 | 500205 | 4168.38 | 859125626 | 1717.55 | 7.16 | 4355198296 | 8706 |
| 512 | 109125.34 | 661545 | 5512.88 | 1214071531 | 1835.21 | 10.12 | 5890229089 | 8903 |
从上面表格上面,可以看出:
1、随着并发数的增加,写入WAL的次数、TPS都是处于增加的状态(除了16并发外)。
2、在不同并发的情况下,平均每次写入WAL的字节数相差不大(除了16并发外)。
3、在16并发下,虽然写入WAL次数很多,但是由于平均每次写入WAL的字节数比较少,所以写入WAL的总字节数并不高。
4、随着并发数的增加,虽然平均每次写入WAL的时延逐渐增加,但是由于WAL写入的平均并发数也逐渐增加,因此单位时间内写入的wal总量也是逐渐增加的。

上图是TPS值与WAL写入的相关性,从上图可以看出,TPS值与写入WAL总字节数的相关性更高。
纯读场景
使用以下命令对point_select场景进行测试:
sysbench --db-driver=pgsql --pgsql-user=postgres --pgsql-password='xxx' --pgsql-db=postgres --events=0 --report-interval=5 --pgsql-host=xxx --pgsql-port=15432 --table-size=5000000 --tables=8 oltp_point_select.lua run --time=120 --threads=xxx --rand-type='uniform'
point_select场景下的TPS值:
| 并发数 | He3DB | PostgreSQL |
|---|---|---|
| 16 | 193685.63 | 187923.32 |
| 32 | 341050.60 | 337567.90 |
| 64 | 529478.74 | 526082.66 |
| 128 | 693607.85 | 690418.83 |
| 256 | 776906.62 | 749479.64 |
| 512 | 892231.08 | 800822.33 |
从上面的测试结果可以看出,针对point_select场景,He3DB与PostgreSQL在不同的并发数下的TPS值相差不大,主要原因是CPU、内存不受限,data buffer大小超过了数据量,基本上查询page都是内存操作。
纯写场景
使用以下命令对update_non_index场景进行测试:
sysbench --db-driver=pgsql --pgsql-user=postgres --pgsql-password='xxx' --pgsql-db=postgres --events=0 --report-interval=5 --pgsql-host=xxx --pgsql-port=xxx --table-size=5000000 --tables=8 oltp_update_non_index.lua run --time=120 --threads=xxx --rand-type='uniform'
update_non_index场景下的TPS值:
| 并发数 | He3DB | PostgreSQL(主+半同步备) | PostgreSQL(主+异步备) |
|---|---|---|---|
| 16 | 4718.45 | 20443.57 | 44442.60 |
| 32 | 10320.54 | 30895.09 | 53302.41 |
| 64 | 24365.61 | 34724.31 | 56809.72 |
| 128 | 50425.34 | 34912.40 | 58862.67 |
| 256 | 88765.61 | 41754.36 | 56488.34 |
| 512 | 120231.16 | 49355.61 | 55156.06 |
从上面的测试结果可以看出:
1、随着并发数的逐渐增加,He3DB的TPS值持续增加,而PostgreSQL增长幅度相对不大。
2、在并发数小的情况下,He3DB的TPS值比PostgreSQL相差较大,而随着并发的增加,两者之间的TPS逐渐缩小并反超,在512并发时He3DB的TPS是PostgreSQL的两倍还多。
造成以上现象的原因,主要是在并发数小的情况下,wal写入的并发数比较小,而单次写入到TiKV的时延比写入到高性能磁盘的时延高很多,从而导致在小并发的情况下,He3DB与PostgreSQL的性能差距很大。随着并发数的增加,wal写入的并发数也在增加,单位时间内写入的wal也变多了,并且由于He3DB优化了wal写入的一些锁机制,以及去掉了FPW等操作,因此在大并发的情况,He3DB的性能会比PostgreSQL高很多。
测试分析
以下是在不同的并发下,写入wal到kv层的各耗时段的次数:
| 持久化wal耗时 | 16并发 | 32并发 | 64并发 | 128并发 | 256并发 | 512并发 |
|---|---|---|---|---|---|---|
| <1ms | 82745 | 3719 | 7856 | 14131 | 13403 | 3417 |
| 1~2ms | 42397 | 57829 | 110846 | 228098 | 344195 | 369514 |
| 2~3ms | 1580 | 1725 | 2466 | 14300 | 58376 | 118518 |
| 3~4ms | 226 | 360 | 349 | 2163 | 7460 | 16927 |
| 4~5ms | 65 | 183 | 427 | 1792 | 5257 | 14303 |
| 5~6ms | 29 | 19 | 41 | 185 | 719 | 4147 |
| 6~7ms | 12 | 15 | 6 | 101 | 150 | 681 |
| 7~8ms | 11 | 7 | 14 | 84 | 56 | 207 |
| 8~9ms | 8 | 13 | 19 | 56 | 125 | 263 |
| >9ms | 47 | 97 | 108 | 251 | 369 | 676 |
| 总计 | 127120 | 63967 | 122132 | 261161 | 430110 | 528653 |



从上面的表格和饼图可以看出:
1、在不同的并发数情况下,wal写入耗时绝大数低于2ms。
2、随着并发数的增加(除了16并发),写入wal的次数也增加了,同时较高耗时的写入比例也有所增加。
3、写入wal的耗时最长时间超过了9ms,而且随着压测程序并发数的增加,超过9ms的写入次数也增加了很多。
下面表格是关于update_non_index场景下TPS与WAL写入的一些统计信息:
| 并发数 | TPS | 写入WAL次数 | 平均每秒写入的WAL次数 | 写入WAL总耗时(us) | 平均每次写入WAL的耗时(us) | WAL写入的平均并发数 | 写入WAL字节数 | 平均每次写入WAL的字节数 |
|---|---|---|---|---|---|---|---|---|
| 16 | 4718.15 | 127120 | 1059.33 | 113543190 | 893.20 | 0.95 | 316833821 | 2492 |
| 32 | 10320.54 | 63967 | 533.06 | 88778857 | 1387.89 | 0.74 | 537890664 | 8408 |
| 64 | 24365.61 | 122132 | 1017.77 | 164191775 | 1344.38 | 1.37 | 1024879887 | 8391 |
| 128 | 50425.34 | 261161 | 2176.34 | 381753565 | 1461.76 | 3.18 | 2209227624 | 8459 |
| 256 | 88765.61 | 430110 | 3584.25 | 711759600 | 1654.83 | 5.93 | 3665152619 | 8521 |
| 512 | 120231.16 | 528653 | 4405.44 | 1016297881 | 1922.43 | 8.47 | 4739454186 | 8965 |
从上面表格上面,可以看出:
1、随着并发数的增加,写入WAL的次数、TPS都是处于增加的状态(除了16并发)。
2、在不同并发的情况下,平均每次写入WAL的字节数相差不大(除了16并发)。
3、在16并发下,虽然写入WAL次数很多,但是由于平均每次写入WAL的字节数比较少,所以写入WAL的总字节数并不高。
4、随着并发数的增加,虽然平均每次写入WAL的时延逐渐增加,但是由于WAL写入的平均并发数也逐渐增加,因此单位时间内写入的wal总量也是逐渐增加的。

上图是TPS值与WAL写入的相关性,从上图可以看出,TPS值与写入WAL总字节数的相关性更高。
读写场景
使用以下命令对read_write场景进行测试:
sysbench --db-driver=pgsql --pgsql-user=postgres --pgsql-password='xxx' --pgsql-db=postgres --events=0 --report-interval=5 --pgsql-host=xxx --pgsql-port=xxx --table-size=5000000 --tables=8 oltp_read_write.lua run --time=120 --threads=xxx --rand-type='uniform'
read_write场景下的TPS值:
| 并发数 | He3DB | PostgreSQL(主+半同步备) | PostgreSQL(主+异步备) |
|---|---|---|---|
| 16 | 2565.81 | 3298.55 | 5308.61 |
| 32 | 6308.74 | 6219.23 | 8083.01 |
| 64 | 12045.87 | 7785.13 | 9963.64 |
| 128 | 18765.09 | 8903.62 | 11648.99 |
| 256 | 23387.68 | 11227.80 | 11529.68 |
| 512 | 24526.76 | 9823.68 | 10703.55 |
从上面的测试结果可以看出:
1、随着并发数的增加,He3DB的TPS值也逐渐增加;PostgreSQL的TPS值也逐渐增多,在一定并发时达到峰值,并发数再增加,反而TPS值在逐渐下降。
2、相较于PostgreSQL(异步备),随着并发数的变化,He3DB的TPS值变化幅度很大。He3DB的最小TPS值明显小于PostgreSQL,最大TPS也明显高于PostgreSQL。
测试分析
以下是在不同的并发下,写入wal到kv层的各耗时段的次数:
| 持久化wal耗时 | 16并发 | 32并发 | 64并发 | 128并发 | 256并发 | 512并发 |
|---|---|---|---|---|---|---|
| <1ms | 372 | 1950 | 3427 | 2004 | 724 | 942 |
| 1~2ms | 35642 | 88491 | 170049 | 210404 | 239674 | 220341 |
| 2~3ms | 2014 | 5327 | 18090 | 50561 | 62765 | 81922 |
| 3~4ms | 151 | 642 | 1726 | 5648 | 6453 | 9299 |
| 4~5ms | 47 | 333 | 1261 | 3113 | 6082 | 8515 |
| 5~6ms | 7 | 61 | 263 | 845 | 2204 | 6052 |
| 6~7ms | 1 | 16 | 34 | 171 | 340 | 977 |
| 7~8ms | 3 | 9 | 14 | 46 | 196 | 261 |
| 8~9ms | 5 | 21 | 31 | 57 | 113 | 118 |
| >9ms | 41 | 78 | 201 | 366 | 652 | 1012 |
| 总计 | 38283 | 96928 | 195096 | 273215 | 319203 | 329439 |



从上面的表格和饼图可以看出,针对read_write场景,He3DB的WAL写入的情况与update_non_index场景相似:
1、在不同的并发数情况下,wal写入耗时绝大数处于1~2ms的范围内。
2、随着压测程序并发的增加,写入wal的次数也增加了,同时较高耗时的写入比例也有所增加。
3、写入wal的耗时最长时间超过了9ms,而且随着压测程序并发数的增加,超过9ms的写入次数也增加了很多。
下面表格是关于read_write场景下TPS与WAL写入的一些统计信息:
| 并发数 | TPS | 写入WAL次数 | 平均每秒写入的WAL次数 | 写入WAL总耗时(us) | 平均每次写入WAL的耗时(us) | WAL写入的平均并发数 | 写入WAL字节数 | 平均每次写入WAL的字节数 |
|---|---|---|---|---|---|---|---|---|
| 16 | 2565.81 | 38283 | 319.03 | 60088940 | 1569.60 | 0.50 | 362552697 | 9470 |
| 32 | 6308.74 | 96928 | 807.73 | 146691141 | 1513.40 | 1.22 | 910365074 | 9392 |
| 64 | 12045.87 | 195096 | 1625.80 | 312332682 | 1600.92 | 2.60 | 1906322047 | 9771 |
| 128 | 18765.09 | 273215 | 2276.79 | 493186188 | 1805.12 | 4.11 | 2716263797 | 9941 |
| 256 | 23387.68 | 319203 | 2660.03 | 611763961 | 1916.54 | 5.10 | 3025767456 | 9479 |
| 512 | 24526.76 | 329439 | 2745.33 | 689790798 | 2093.83 | 5.75 | 3093217769 | 9389 |
从上面表格上面,可以看出:
1、随着并发数的增加,写入WAL的次数、TPS都是处于增加的状态。
2、在不同并发的情况下,平均每次写入WAL的字节数相差不大。
3、随着并发数的增加,虽然平均每次写入WAL的时延逐渐增加,但是由于WAL写入的平均并发数也逐渐增加,因此单位时间内写入的wal总量也是逐渐增加的。

上图是TPS值与WAL写入的相关性,从上图可以看出,TPS值与写入WAL总字节数的相关性更高。
小结
在资源不受限的情况下,通过以上几个场景对He3DB和PostgreSQL的性能进行对比,可以发现:
- 随着并发的逐渐增加,所有测试场景下He3DB的TPS值都是逐渐增加的。
- 在16并发时,由于并发比较小,所以平均每次写入WAL的字节数比较少。
- 在小并发的写场景下,He3DB的性能会明显落后于PostgreSQL,但是随着并发数的增加,He3DB的性能提升比较明显,并且最终会明显超过PostgreSQL。
- 对于存在写的场景来说,TPS值与写入WAL总字节数的相关性比较高。
- 对于读场景,He3DB在不同并发下的TPS值和PostgreSQL接近。
本篇主要介绍了在不限资源的情况下,He3DB与PostgreSQL在不同场景下的性能对比,后续会对限资源的情况下(CPU、内存)的性能对比进行介绍。




