PostgreSQL 是成千上万应用程序的基础。该系统长期以来一直证明了其价值,并且运行可靠。然而,人们经常问的一个问题是:调试数据库应用程序的最佳方法是什么?
多年来,我一直使用一种简单的方法,可以大大加快典型应用程序的调试过程。
使用继承存储数据
PostgreSQL 的一个核心特性,已经存在了几十年,就是“继承”。那么,这到底是什么意思呢?简单来说,表可以相互继承列。子表将拥有父表的所有列,以及它自己的额外列。
但在现实生活中,这到底意味着什么,它与调试有什么关系呢?让我们仔细看看:
CREATE TABLE t_global
(
id serial,
tstamp timestamptz DEFAULT now()
);
CREATE TABLE t_product
(
name text,
price numeric
) INHERITS (t_global);
INSERT INTO t_product (name, price)
VALUES ('Shoes', 113.98), ('Sausage', 4.58);
t_global 表包含一个序列和一个时间戳。这两个列被传递到 t_product。这意味着序列现在也适用于子表。
再看看第二个表:
CREATE TABLE t_country
(
country_name text
) INHERITS (t_global);
INSERT INTO t_country (country_name)
VALUES ('Austria'), ('Germany'), ('Japan');
我们正在使用相同的父表来创建国家表。这有什么意义呢?我们刚刚产生了两件事:首先,有一个全局序列适用于所有表,这意味着整个系统中的所有 ID 都是唯一的(稍后我们会用到这个)。其次:所有表都有一个时间戳,其默认值是相同的。
使用父表调试应用程序
现在,假设我们想调试一个应用程序,并且想知道两件事:
我们正在寻找的 ID 位于哪个表中?
在同一事务中发生了哪些操作?
我们刚刚创建的结构使得回答这两个问题变得非常容易:
test=# SELECT tableoid::regclass, * FROM t_global;
tableoid | id | tstamp
----------+----+-------------------------------
t_product | 1 | 2025-01-24 12:02:07.295524+01
t_product | 2 | 2025-01-24 12:02:07.295524+01
t_country | 3 | 2025-01-24 12:02:07.295524+01
t_country | 4 | 2025-01-24 12:02:07.295524+01
t_country | 5 | 2025-01-24 12:02:07.295524+01
(5 行)
记住,只有一个序列来填充所有 ID 列,因此我们可以依赖于整个数据库中的 ID 是唯一的。但还有更多:在 PostgreSQL 中,每个表都支持一个虚拟列,名为“tableoid”。它显示表的对象 ID。诀窍如下:如果我们把这个对象 ID 转换为一个特殊的数据类型(= regclass),它将给我们一个表名的字符串表示。换句话说,我们可以运行一个 SQL 语句,它将给我们整个系统中的所有 ID,包括包含该 ID 的表名。由于整个应用程序中的所有内容都是唯一的,我们可以轻松地找出我们可能正在寻找的任何东西的位置。
然而,我们可能还想仔细看看第二列:时间戳。注意所有时间戳都是相同的。这是因为所有数据都是由同一个事务写入的。一眼就能看出数据插入的顺序,同时我们也能看出这是否是在同一个事务中发生的。与此同时,我们仍然可以清楚地看到所有表中数据变化的顺序。
这篇文章中概述的方法多年来一直为我服务得很好,使我对数据库方面的调试工作变得更加轻松。因此,我希望这也能帮助到其他人。
原文地址:https://www.cybertec-postgresql.com/en/debugging-postgresql-more-easily/
原文作者:Hans-Jürgen Schönig