PostgreSQL pgbench tpcb 数据生成与SQL部分源码解读

本文涉及的产品
云原生数据库 PolarDB MySQL 版,Serverless 5000PCU 100GB
云原生数据库 PolarDB 分布式版,标准版 2核8GB
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
简介: 标签PostgreSQL , pgbench , tpcb背景pgbench是PG的一款测试工具,内置的测试CASE为tpcb测试。同时支持用户自己写测试CASE。大量自定义CASE参考https://github.com/digoal/blog/blob/master/201711/readme.md本文为pgbench 内置tpcb的解读。

标签

PostgreSQL , pgbench , tpcb


背景

pgbench是PG的一款测试工具,内置的测试CASE为tpcb测试。同时支持用户自己写测试CASE。

大量自定义CASE参考

https://github.com/digoal/blog/blob/master/201711/readme.md

本文为pgbench 内置tpcb的解读。

源码

src/bin/pgbench/pgbench.c

表结构

/*  
 * Create pgbench's standard tables  
 */  
static void  
initCreateTables(PGconn *con)  
{  
        /*  
         * The scale factor at/beyond which 32-bit integers are insufficient for  
         * storing TPC-B account IDs.  
         *  
         * Although the actual threshold is 21474, we use 20000 because it is  
         * easier to document and remember, and isn't that far away from the real  
         * threshold.  
         */  
#define SCALE_32BIT_THRESHOLD 20000  
  
        /*  
         * Note: TPC-B requires at least 100 bytes per row, and the "filler"  
         * fields in these table declarations were intended to comply with that.  
         * The pgbench_accounts table complies with that because the "filler"  
         * column is set to blank-padded empty string. But for all other tables  
         * the columns default to NULL and so don't actually take any space.  We  
         * could fix that by giving them non-null default values.  However, that  
         * would completely break comparability of pgbench results with prior  
         * versions. Since pgbench has never pretended to be fully TPC-B compliant  
         * anyway, we stick with the historical behavior.  
         */  
        struct ddlinfo  
        {  
                const char *table;              /* table name */  
                const char *smcols;             /* column decls if accountIDs are 32 bits */  
                const char *bigcols;    /* column decls if accountIDs are 64 bits */  
                int                     declare_fillfactor;  
        };  
        static const struct ddlinfo DDLs[] = {  
                {  
                        "pgbench_history",  
                        "tid int,bid int,aid    int,delta int,mtime timestamp,filler char(22)",  
                        "tid int,bid int,aid bigint,delta int,mtime timestamp,filler char(22)",  
                        0  
                },  
                {  
                        "pgbench_tellers",  
                        "tid int not null,bid int,tbalance int,filler char(84)",  
                        "tid int not null,bid int,tbalance int,filler char(84)",  
                        1  
                },  
                {  
                        "pgbench_accounts",  
                        "aid    int not null,bid int,abalance int,filler char(84)",  
                        "aid bigint not null,bid int,abalance int,filler char(84)",  
                        1  
                },  
                {  
                        "pgbench_branches",  
                        "bid int not null,bbalance int,filler char(88)",  
                        "bid int not null,bbalance int,filler char(88)",  
                        1  
                }  
        };  
        int                     i;  
  
        fprintf(stderr, "creating tables...\n");  
  
        for (i = 0; i < lengthof(DDLs); i++)  
        {  
                char            opts[256];  
                char            buffer[256];  
                const struct ddlinfo *ddl = &DDLs[i];  
                const char *cols;  
  
                /* Construct new create table statement. */  
                opts[0] = '\0';  
                if (ddl->declare_fillfactor)  
                        snprintf(opts + strlen(opts), sizeof(opts) - strlen(opts),  
                                         " with (fillfactor=%d)", fillfactor);  
                if (tablespace != NULL)  
                {  
                        char       *escape_tablespace;  
  
                        escape_tablespace = PQescapeIdentifier(con, tablespace,  
                                                                                                   strlen(tablespace));  
                        snprintf(opts + strlen(opts), sizeof(opts) - strlen(opts),  
                                         " tablespace %s", escape_tablespace);  
                        PQfreemem(escape_tablespace);  
                }  
  
                cols = (scale >= SCALE_32BIT_THRESHOLD) ? ddl->bigcols : ddl->smcols;  
  
                snprintf(buffer, sizeof(buffer), "create%s table %s(%s)%s",  
                                 unlogged_tables ? " unlogged" : "",  
                                 ddl->table, cols, opts);  
  
                executeStatement(con, buffer);  
        }  
}  

tpcb 记录数算法

1、系数:

#define nbranches       1                       /* Makes little sense to change this.  Change  
                                                                 * -s instead */  
#define ntellers        10  
#define naccounts       100000  

2、记录数算法:

nbranches * scale  
  
ntellers * scale  
  
naccounts * scale  

3、如果要写入1万亿数据,那么设置 scale = 10000000

此时:

tellers = 1亿条  
  
branches = 1000万条  
  
accounts = 1万亿条  

初始化数据

只有pgbench_accounts用了copy协议,另外两个表数据相对较少,用的是INSERT

	/*  
         * fill branches, tellers, accounts in that order in case foreign keys  
         * already exist  
         */  
        for (i = 0; i < nbranches * scale; i++)  
        {  
                /* "filler" column defaults to NULL */  
                snprintf(sql, sizeof(sql),  
                                 "insert into pgbench_branches(bid,bbalance) values(%d,0)",  
                                 i + 1);  
                executeStatement(con, sql);  
        }  
  
        for (i = 0; i < ntellers * scale; i++)  
        {  
                /* "filler" column defaults to NULL */  
                snprintf(sql, sizeof(sql),  
                                 "insert into pgbench_tellers(tid,bid,tbalance) values (%d,%d,0)",  
                                 i + 1, i / ntellers + 1);  
                executeStatement(con, sql);  
        }  
.....  
        /*  
         * accounts is big enough to be worth using COPY and tracking runtime  
         */  
        res = PQexec(con, "copy pgbench_accounts from stdin");  
        if (PQresultStatus(res) != PGRES_COPY_IN)  
        {  
                fprintf(stderr, "%s", PQerrorMessage(con));  
                exit(1);  
        }  
        PQclear(res);  
  
        INSTR_TIME_SET_CURRENT(start);  
  
        for (k = 0; k < (int64) naccounts * scale; k++)  
        {  
                int64           j = k + 1;  
  
                /* "filler" column defaults to blank padded empty string */  
                snprintf(sql, sizeof(sql),  
                                 INT64_FORMAT "\t" INT64_FORMAT "\t%d\t\n",  
                                 j, k / naccounts + 1, 0);  
                if (PQputline(con, sql))  
                {  
                        fprintf(stderr, "PQputline failed\n");  
                        exit(1);  
                }  
  
                /*  
                 * If we want to stick with the original logging, print a message each  
                 * 100k inserted rows.  
                 */  
                if ((!use_quiet) && (j % 100000 == 0))  
                {  
                        INSTR_TIME_SET_CURRENT(diff);  
                        INSTR_TIME_SUBTRACT(diff, start);  
  
                        elapsed_sec = INSTR_TIME_GET_DOUBLE(diff);  
                        remaining_sec = ((double) scale * naccounts - j) * elapsed_sec / j;  
  
                        fprintf(stderr, INT64_FORMAT " of " INT64_FORMAT " tuples (%d%%) done (elapsed %.2f s, remaining %.2f s)\n",  
                                        j, (int64) naccounts * scale,  
                                        (int) (((int64) j * 100) / (naccounts * (int64) scale)),  
                                        elapsed_sec, remaining_sec);  
                }  
                /* let's not call the timing for each row, but only each 100 rows */  
                else if (use_quiet && (j % 100 == 0))  
                {  
                        INSTR_TIME_SET_CURRENT(diff);  
                        INSTR_TIME_SUBTRACT(diff, start);  
  
                        elapsed_sec = INSTR_TIME_GET_DOUBLE(diff);  
                        remaining_sec = ((double) scale * naccounts - j) * elapsed_sec / j;  
  
                        /* have we reached the next interval (or end)? */  
                        if ((j == scale * naccounts) || (elapsed_sec >= log_interval * LOG_STEP_SECONDS))  
                        {  
                                fprintf(stderr, INT64_FORMAT " of " INT64_FORMAT " tuples (%d%%) done (elapsed %.2f s, remaining %.2f s)\n",  
                                                j, (int64) naccounts * scale,  
                                                (int) (((int64) j * 100) / (naccounts * (int64) scale)), elapsed_sec, remaining_sec);  
  
                                /* skip to the next interval */  
                                log_interval = (int) ceil(elapsed_sec / LOG_STEP_SECONDS);  
                        }  
                }  
  
        }  
        if (PQputline(con, "\\.\n"))  
        {  
                fprintf(stderr, "very last PQputline failed\n");  
                exit(1);  
        }  
        if (PQendcopy(con))  
        {  
                fprintf(stderr, "PQendcopy failed\n");  
                exit(1);  
        }  
  
        executeStatement(con, "commit");  
}  

如果你要测试tpcb, 并且生成的数据量特别庞大(比如我最近在生成1万亿的CASE,实际上tellers, branches两张表也分别有1万千和1亿。),可以修改一下pgbench的源码,全部改成COPY协议。

COPY写入时,可以达到120万行/s左右的写入速度。

tpcb 读写测试

\set aid random(1, 100000 * :scale)  
\set bid random(1, 1 * :scale)  
\set tid random(1, 10 * :scale)  
\set delta random(-5000, 5000)  
BEGIN;  
UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;  
SELECT abalance FROM pgbench_accounts WHERE aid = :aid;  
UPDATE pgbench_tellers SET tbalance = tbalance + :delta WHERE tid = :tid;  
UPDATE pgbench_branches SET bbalance = bbalance + :delta WHERE bid = :bid;  
INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);  
END;  

tpcb 只读测试

\set aid random(1, 100000 * :scale)  
SELECT abalance FROM pgbench_accounts WHERE aid = :aid;  
相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
相关文章
|
16天前
|
SQL 存储 关系型数据库
一文搞懂SQL优化——如何高效添加数据
**SQL优化关键点:** 1. **批量插入**提高效率,一次性建议不超过500条。 2. **手动事务**减少开销,多条插入语句用一个事务。 3. **主键顺序插入**避免页分裂,提升性能。 4. **使用`LOAD DATA INFILE`**大批量导入快速。 5. **避免主键乱序**,减少不必要的磁盘操作。 6. **选择合适主键类型**,避免UUID或长主键导致的性能问题。 7. **避免主键修改**,保持索引稳定。 这些技巧能优化数据库操作,提升系统性能。
213 4
一文搞懂SQL优化——如何高效添加数据
|
25天前
|
存储 关系型数据库 分布式数据库
PolarDB常见问题之PolarDB冷存数据到OSS之后恢复失败如何解决
PolarDB是阿里云推出的下一代关系型数据库,具有高性能、高可用性和弹性伸缩能力,适用于大规模数据处理场景。本汇总囊括了PolarDB使用中用户可能遭遇的一系列常见问题及解答,旨在为数据库管理员和开发者提供全面的问题指导,确保数据库平稳运行和优化使用体验。
|
1月前
|
SQL 关系型数据库 分布式数据库
在PolarDB中,行数评估是通过对表的统计数据、基数估计以及算子代价模型来进行估算的。
【2月更文挑战第14天】在PolarDB中,行数评估是通过对表的统计数据、基数估计以及算子代价模型来进行估算的。
82 1
|
2月前
|
SQL 关系型数据库 MySQL
【MySQL进阶之路丨第十四篇】一文带你精通MySQL重复数据及SQL注入
【MySQL进阶之路丨第十四篇】一文带你精通MySQL重复数据及SQL注入
46 0
|
1月前
|
SQL 数据可视化 数据处理
使用SQL和Python处理Excel文件数据
使用SQL和Python处理Excel文件数据
51 0
|
6天前
|
人工智能 Cloud Native 算法
数据之势丨AI时代,云原生数据库的最新发展趋势与进展
AI与云数据库的深度结合是数据库发展的必然趋势,基于AI能力的加持,云数据库未来可以实现更快速的查询和决策,帮助企业更好地利用海量数据进行业务创新和决策优化。
数据之势丨AI时代,云原生数据库的最新发展趋势与进展
|
22天前
|
关系型数据库 MySQL OLAP
PolarDB +AnalyticDB Zero-ETL :免费同步数据到ADB,享受数据流通新体验
Zero-ETL是阿里云瑶池数据库提供的服务,旨在简化传统ETL流程的复杂性和成本,提高数据实时性。降低数据同步成本,允许用户快速在AnalyticDB中对PolarDB数据进行分析,降低了30%的数据接入成本,提升了60%的建仓效率。 Zero-ETL特性包括免费的PolarDB MySQL联邦分析和PolarDB-X元数据自动同步,提供一体化的事务处理和数据分析,并能整合多个数据源。用户只需简单配置即可实现数据同步和实时分析。
|
26天前
|
SQL 安全 数据库
第三章用sql语句操作数据
第三章用sql语句操作数据
9 0
|
1月前
|
SQL 关系型数据库 分布式数据库
在PolarDB中,如果慢SQL导致了CPU升高,进而又产生了更多的慢SQL
【2月更文挑战第22天】在PolarDB中,如果慢SQL导致了CPU升高,进而又产生了更多的慢SQL
12 1
|
1月前
|
SQL 数据库 数据库管理
SQL中如何添加数据:基础指南
SQL中如何添加数据:基础指南
24 2

相关产品

  • 云原生数据库 PolarDB