PostgreSQL笔记28: PostgreSQL ACID 特性与实现机制深度解析

PostgreSQL 核心原理深度解析:事务与锁机制 PostgreSQL的事务与锁机制在高并发场景下表现优于MySQL,主要体现在三方面:MVCC实现更高效,读写不冲突且读不加锁;锁粒度更精细,支持行锁、页锁等多种锁类型,减少冲突;事务隔离更灵活,默认Read Committed级别且无MySQL的间隙锁问题。PG通过WAL日志保障原子性和持久性,利用MVCC隐藏字段实现快照隔离,配合精细锁机制有效降低死锁概率。不同隔离级别下,PG的表现也优于MySQL,Repeatable Read无需间隙锁,Serializable并发度更高。使用中需避免常见误区,如误 阅读详情

纲要

  • ACID 事务特性概述
    • Atomicity(原子性)
    • Consistency(一致性)
    • Isolation(隔离性)
    • Durability(持久性)
  • 原子性(Atomicity) 与 子事务(Subtransaction)
    • SAVEPOINT 与子事务机制
    • psql 的 ON_ERROR_ROLLBACK 行为
    • 子事务的性能代价与资源消耗
    • JDBC 中的 autosave 参数
  • 一致性(Consistency) 的保障机制
  • 隔离性(Isolation) 与 MVCC
    • 四种标准隔离级别
    • PostgreSQL 的隔离级别实现
    • MVCC(多版本并发控制)机制
    • SSI(可串行化快照隔离)
  • 持久性(Durability) 与 WAL
    • WAL(预写日志)机制
    • fsync 与 checkpoint
    • 流复制场景中的同步复制
  • API 速览
  • Demo 简单示例
  • 官方文档
  • 参考链接

事务的 ACID 特性

ACID 是关系数据库事务的四个核心属性,分别是 Atomicity(原子性)、Consistency(一致性)、Isolation(隔离性)和 Durability(持久性)。这四个属性共同保证了数据库事务的可靠执行。

原子性(Atomicity)

原子性是指一个事务中的所有操作,要么全部成功提交,要么全部失败回滚,不存在中间状态。当事务在执行过程中发生错误时,整个事务会被回滚到开始之前的状态,就好像这个事务从未执行过一样。

在 PostgreSQL 中,原子性通过 事务日志(WAL)和 CLOG(事务提交日志)共同保证。崩溃恢复完成后,未提交的事务会被完整回滚,已提交的事务会被完整重做。

子事务(Subtransaction)

PostgreSQL 支持子事务机制,允许在事务内部通过 SAVEPOINT 创建保存点,从而实现部分回滚。

-- 创建测试表
CREATE TABLE t1 (id INTEGER PRIMARY KEY);

-- 开启事务
BEGIN;

-- 插入第一条数据
INSERT INTO t1 VALUES (99);

-- 创建保存点 sp1
SAVEPOINT sp1;

-- 插入第二条数据(此处模拟错误)
INSERT INTO t1 VALUES (999);  -- 假设此处发生错误

-- 回滚到保存点 sp1
ROLLBACK TO SAVEPOINT sp1;

-- 查询结果:仅能看到 id=99,id=999 已被回滚
SELECT * FROM t1;
-- 输出: 99

-- 提交事务
COMMIT;

子事务的本质是将一个事务拆分为多个更小的事务单元,但它仍然属于同一个顶层事务,不会破坏事务的原子性。

子事务的性能代价

子事务虽然提供了灵活的细粒度错误处理能力,但其使用需要谨慎评估:

  1. 事务 ID 消耗:每个子事务都会分配一个永久事务 ID(XID)。事务 ID 在 PostgreSQL 中是一种有限资源。有实测表明,仅插入 5000 条数据,若每条都使用子事务,可能消耗 64 万个 XID,这会直接导致 autovacuum freeze 提前到来。

  2. 缓存溢出:当一个会话中的子事务超过 64 个时,会触发 overflow 状态,性能会急剧下降。

  3. 查找性能退化:在存在大量活跃子事务的场景中,TransactionIdIsCurrentTransactionId 函数的查找会退化为对链表的线性扫描。PostgreSQL 社区有报告显示,在包含 1000 个保存点的测试中,性能极度缓慢,优化补丁可将性能提升约 10 倍。

  4. 日志膨胀:子事务为记录保存点元数据,产生的日志量可能达到普通操作的数万倍。

  5. 锁竞争:当并发事务较多时,子事务可能引入 SubtransControlLock 等待事件,影响整体性能。

结论:在生产环境中,非必要不应使用子事务。如必须使用,应严格控制每个事务中的子事务数量,建议不超过 64 个。

ON_ERROR_ROLLBACK

psql 命令行工具提供了 ON_ERROR_ROLLBACK 选项,当启用时,如果事务块中的语句发生错误,错误会被忽略,事务继续执行。

-- 启用 ON_ERROR_ROLLBACK
\set ON_ERROR_ROLLBACK on

BEGIN;
INSERT INTO t1 VALUES (100);
INSERT INTO t1 VALUES (200);
-- 假设此处发生错误(例如违反约束)
INSERT INTO t1 VALUES (100);  -- 主键冲突
-- 错误被忽略,事务继续
INSERT INTO t1 VALUES (300);
COMMIT;

ON_ERROR_ROLLBACK 的工作原理是在事务块中每条命令执行前隐式创建一个 SAVEPOINT,出错时回滚到该保存点。

-- 上述行为等价于手动执行
BEGIN;
SAVEPOINT pg_psql_temporary_savepoint;
INSERT INTO t1 VALUES (100);
RELEASE SAVEPOINT pg_psql_temporary_savepoint;
SAVEPOINT pg_psql_temporary_savepoint;
INSERT INTO t1 VALUES (200);
RELEASE SAVEPOINT pg_psql_temporary_savepoint;
-- 以此类推

需要注意的是,ON_ERROR_ROLLBACK 仅在 psql 交互模式下生效。

JDBC 中的 autosave

在 JDBC 驱动中,autosave 参数提供了类似的能力。当启用时,驱动会在每条语句执行前自动创建保存点,出错时回滚到该保存点。这也意味着每条语句都会消耗一个子事务,同样需要注意性能影响。

一致性(Consistency)

一致性是指事务执行前后,数据库的完整性约束没有被破坏。数据必须符合所有预定义的约束规则,包括:

  • 主键约束(PRIMARY KEY)
  • 外键约束(FOREIGN KEY)
  • 唯一约束(UNIQUE)
  • 检查约束(CHECK)
  • 触发器(TRIGGER)
-- 创建带约束的表
CREATE TABLE orders (
    id SERIAL PRIMARY KEY,
    customer_id INTEGER NOT NULL,
    amount DECIMAL(10, 2) CHECK (amount > 0),
    status VARCHAR(20) DEFAULT 'pending'
);

-- 以下插入将因 CHECK 约束失败而回滚
BEGIN;
INSERT INTO orders (customer_id, amount) VALUES (1, -100.00);  -- 违反 CHECK
COMMIT;  -- 事务回滚

在 PostgreSQL 中,一致性主要通过数据类型、约束和触发器来保证。

隔离性(Isolation)

隔离性是指多个事务并发执行时,彼此的操作不会相互干扰。PostgreSQL 通过 MVCC(多版本并发控制)实现了高效的隔离性,避免了读写事务之间的互相阻塞。

事务隔离级别

SQL 标准定义了四种事务隔离级别:

隔离级别脏读(Dirty Read)不可重复读(Nonrepeatable Read)幻读(Phantom Read)序列化异常(Serialization Anomaly)
READ UNCOMMITTED可能(PG 中不允许)可能可能可能
READ COMMITTED不可能可能可能可能
REPEATABLE READ不可能不可能可能(PG 中不允许)可能
SERIALIZABLE不可能不可能不可能不可能

PostgreSQL 在内部仅实现了三种不同的隔离级别:

  1. READ COMMITTED:PostgreSQL 的默认隔离级别。事务中的每条查询只能看到查询开始前已提交的数据。同一事务中的两条 SELECT 语句可能看到不同的数据。
-- 设置隔离级别为 READ COMMITTED
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
  1. REPEATABLE READ:事务中的所有语句只能看到事务中第一个查询或数据修改语句执行前已提交的数据。PostgreSQL 的 REPEATABLE READ 不允许幻读。
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
  1. SERIALIZABLE:最严格的隔离级别。并发事务的执行效果必须与串行执行的效果一致。如果并发读写模式会导致不可串行化的结果,其中一个事务将被回滚并报 serialization_failure 错误。
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

值得注意的是,在 PostgreSQL 中请求 READ UNCOMMITTED 级别时,实际行为等同于 READ COMMITTED。

MVCC(多版本并发控制)

PostgreSQL 的隔离性主要通过 MVCC 实现。其核心原理是:

  1. 数据文件中存放同一逻辑行的多个版本(称为 Tuple)。
  2. 每个行版本的头部记录了创建和删除该版本的事务 ID(分别称为 xmin 和 xmax)。
  3. 每个事务在执行时获取一个数据库快照,根据快照中的事务状态信息决定哪些行版本可见。
  4. 读者不会阻塞写者,写者也不会阻塞读者,从而极大提高了并发性能。

数据行(元组)

版本1: xmin=100, xmax=∞

事务 B (XID=101)

BEGIN (获取快照)

SELECT

快照判断: xmin=100 已提交 → 可见

事务 A (XID=100)

BEGIN

INSERT 创建版本 xmin=100

COMMIT

PostgreSQL 在最严格的 SERIALIZABLE 隔离级别下,使用创新的 可串行化快照隔离(SSI,Serializable Snapshot Isolation) 技术来检测并解决序列化异常。

持久性(Durability)

持久性是指事务提交后,其对数据库的修改是永久的,即使系统宕机也不会丢失。

WAL(预写日志)

PostgreSQL 通过 WAL(Write-Ahead Logging,预写日志)保证持久性。其核心原则是:

对数据文件的修改必须在描述这些修改的日志记录被刷写到持久存储之后才能进行。

崩溃恢复

系统重启

读取 WAL

重做已提交事务

回滚未提交事务

事务提交流程

事务执行

WAL Buffer 写入

WAL 刷盘 fsync

返回 COMMIT 成功

后台异步写入数据文件

事务提交时,PostgreSQL 会执行 fsync 将 WAL 刷写到磁盘,然后才返回提交成功。数据文件的更新是延迟进行的,由后台的 checkpoint 进程负责。

fsync 与 checkpoint
  • fsync:控制是否强制将 WAL 和数据文件同步到磁盘。生产环境绝不能关闭 fsync,否则电源故障可能导致不可恢复的数据丢失。
  • checkpoint:定期将共享缓冲区中的脏数据写入数据文件,并更新 pg_control 文件。相关参数包括 checkpoint_timeout(默认 5 分钟)和 max_wal_size。
流复制场景

在主从流复制场景中,同步复制可以进一步保证持久性——只有当事务的 WAL 已被传输并刷写到至少一个同步备库后,主库才返回提交成功。

API 速览

事务控制命令

BEGIN / START TRANSACTION

开启一个新事务。

语法:

BEGIN [ WORK | TRANSACTION ] [ transaction_mode [, ...] ]
START TRANSACTION [ transaction_mode [, ...] ]

参数:

  • ISOLATION LEVEL { SERIALIZABLE | REPEATABLE READ | READ COMMITTED | READ UNCOMMITTED }
  • READ WRITE | READ ONLY
  • [ NOT ] DEFERRABLE

示例:

BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE READ ONLY;
-- 执行只读查询
COMMIT;
COMMIT / END

提交当前事务。

COMMIT [ WORK | TRANSACTION ];
ROLLBACK

回滚当前事务。

ROLLBACK [ WORK | TRANSACTION ] [ AND [ NO ] CHAIN ];

保存点命令

SAVEPOINT

在当前事务中创建一个新的保存点。

SAVEPOINT savepoint_name;
ROLLBACK TO SAVEPOINT

回滚到指定的保存点。

ROLLBACK [ WORK | TRANSACTION ] TO [ SAVEPOINT ] savepoint_name;
RELEASE SAVEPOINT

销毁一个保存点。

RELEASE [ SAVEPOINT ] savepoint_name;

会话设置

SET TRANSACTION

设置当前事务的特性。

SET TRANSACTION transaction_mode [, ...]
SET SESSION CHARACTERISTICS

设置会话的默认事务特性。

SET SESSION CHARACTERISTICS AS TRANSACTION transaction_mode [, ...]

psql 元命令

\set ON_ERROR_ROLLBACK

启用或禁用错误自动回滚。

\set ON_ERROR_ROLLBACK on
\set ON_ERROR_ROLLBACK off

Demo 简单示例

以下是一个完整的 Node.js 示例,使用 pg 驱动演示 PostgreSQL 事务的 ACID 特性。

运行说明

  1. 确保本地已安装 PostgreSQL 并创建测试数据库
  2. 安装依赖:npm install pg
  3. 运行脚本:node demo.js

代码

const { Pool } = require('pg');

// PostgreSQL 连接配置
const pool = new Pool({
    host: 'localhost',
    port: 5432,
    database: 'testdb',
    user: 'postgres',
    password: 'your_password',
});

async function runDemo() {
    const client = await pool.connect();

    try {
        // ============================================================
        // 1. 创建测试表
        // ============================================================
        await client.query(`
            DROP TABLE IF EXISTS accounts;
            CREATE TABLE accounts (
                id SERIAL PRIMARY KEY,
                name VARCHAR(100) NOT NULL,
                balance DECIMAL(10, 2) CHECK (balance >= 0)
            );
        `);
        console.log('✓ 测试表创建成功');

        // ============================================================
        // 2. 原子性(Atomicity)演示:事务回滚
        // ============================================================
        console.log('\n--- 原子性演示:事务回滚 ---');

        await client.query('BEGIN');

        // 插入第一条数据
        await client.query(
            'INSERT INTO accounts (name, balance) VALUES ($1, $2)',
            ['Alice', 1000.00]
        );

        // 创建保存点
        await client.query('SAVEPOINT sp1');

        try {
            // 插入第二条数据(此处故意违反 CHECK 约束)
            await client.query(
                'INSERT INTO accounts (name, balance) VALUES ($1, $2)',
                ['Bob', -100.00]  // 负数余额,违反 CHECK (balance >= 0)
            );
        } catch (err) {
            console.log(`  捕获错误: ${err.message}`);
            // 回滚到保存点,保留 Alice 的插入
            await client.query('ROLLBACK TO SAVEPOINT sp1');
            console.log('  已回滚到保存点 sp1,Bob 的数据被撤销');
        }

        // 提交事务 —— Alice 的数据被保留
        await client.query('COMMIT');

        const result1 = await client.query('SELECT * FROM accounts');
        console.log('  最终数据:', result1.rows);
        // 输出: [ { id: 1, name: 'Alice', balance: '1000.00' } ]

        // ============================================================
        // 3. 隔离性(Isolation)演示:READ COMMITTED vs REPEATABLE READ
        // ============================================================
        console.log('\n--- 隔离性演示 ---');

        // 清空表
        await client.query('TRUNCATE accounts RESTART IDENTITY');
        await client.query(
            'INSERT INTO accounts (name, balance) VALUES ($1, $2)',
            ['Charlie', 500.00]
        );

        // 3.1 READ COMMITTED(默认)
        console.log('\n  [READ COMMITTED]');

        // 事务1:开启 READ COMMITTED
        await client.query('BEGIN ISOLATION LEVEL READ COMMITTED');
        const snapshot1 = await client.query('SELECT balance FROM accounts WHERE name = $1', ['Charlie']);
        console.log(`    初始余额: ${snapshot1.rows[0].balance}`);

        // 模拟:在另一个连接中修改数据(此处用同一连接模拟)
        await client.query('SAVEPOINT mock_other_tx');
        await client.query('UPDATE accounts SET balance = balance + 100 WHERE name = $1', ['Charlie']);
        await client.query('RELEASE SAVEPOINT mock_other_tx');

        // 再次查询 —— READ COMMITTED 看到最新提交的数据
        const snapshot2 = await client.query('SELECT balance FROM accounts WHERE name = $1', ['Charlie']);
        console.log(`    再次查询: ${snapshot2.rows[0].balance}`);
        // 输出: 600.00(看到了其他事务的提交)

        await client.query('COMMIT');

        // 3.2 REPEATABLE READ
        console.log('\n  [REPEATABLE READ]');

        // 重置数据
        await client.query('TRUNCATE accounts RESTART IDENTITY');
        await client.query(
            'INSERT INTO accounts (name, balance) VALUES ($1, $2)',
            ['Charlie', 500.00]
        );

        // 事务2:开启 REPEATABLE READ
        await client.query('BEGIN ISOLATION LEVEL REPEATABLE READ');
        const snapshot3 = await client.query('SELECT balance FROM accounts WHERE name = $1', ['Charlie']);
        console.log(`    初始余额: ${snapshot3.rows[0].balance}`);

        // 模拟其他事务修改
        await client.query('SAVEPOINT mock_other_tx2');
        await client.query('UPDATE accounts SET balance = balance + 100 WHERE name = $1', ['Charlie']);
        await client.query('RELEASE SAVEPOINT mock_other_tx2');

        // 再次查询 —— REPEATABLE READ 看到的是快照,数据不变
        const snapshot4 = await client.query('SELECT balance FROM accounts WHERE name = $1', ['Charlie']);
        console.log(`    再次查询: ${snapshot4.rows[0].balance}`);
        // 输出: 500.00(仍然看到事务开始时的快照)

        await client.query('COMMIT');

        // ============================================================
        // 4. 持久性(Durability)验证:WAL 机制
        // ============================================================
        console.log('\n--- 持久性说明 ---');
        console.log('  PostgreSQL 通过 WAL(预写日志)保证持久性:');
        console.log('  1. 事务提交时,WAL 记录先 fsync 到磁盘');
        console.log('  2. 然后才返回 COMMIT 成功');
        console.log('  3. 数据文件的更新由 checkpoint 进程异步完成');
        console.log('  ✓ 即使宕机,已提交事务的修改可通过 WAL 恢复');

        // 查看当前 WAL 相关配置
        const walConfig = await client.query(`
            SELECT name, setting, unit
            FROM pg_settings
            WHERE name IN ('fsync', 'synchronous_commit', 'checkpoint_timeout', 'max_wal_size')
            ORDER BY name;
        `);
        console.log('\n  WAL 相关配置:');
        walConfig.rows.forEach(row => {
            console.log(`    ${row.name} = ${row.setting}${row.unit || ''}`);
        });

        console.log('\n✅ Demo 执行完成');

    } catch (err) {
        console.error('❌ 错误:', err.message);
        await client.query('ROLLBACK');
    } finally {
        client.release();
        await pool.end();
    }
}

runDemo();

PostgreSQL 原生指令注释

上述 Node.js 代码对应的 PostgreSQL 原生 SQL 指令:

-- ============================================================
-- 1. 创建测试表
-- ============================================================
DROP TABLE IF EXISTS accounts;
CREATE TABLE accounts (
    id SERIAL PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    balance DECIMAL(10, 2) CHECK (balance >= 0)
);

-- ============================================================
-- 2. 原子性演示:事务与保存点
-- ============================================================
BEGIN;
INSERT INTO accounts (name, balance) VALUES ('Alice', 1000.00);
SAVEPOINT sp1;
INSERT INTO accounts (name, balance) VALUES ('Bob', -100.00);  -- 违反 CHECK
ROLLBACK TO SAVEPOINT sp1;
COMMIT;
SELECT * FROM accounts;

-- ============================================================
-- 3. 隔离性演示
-- ============================================================
TRUNCATE accounts RESTART IDENTITY;
INSERT INTO accounts (name, balance) VALUES ('Charlie', 500.00);

-- READ COMMITTED
BEGIN ISOLATION LEVEL READ COMMITTED;
SELECT balance FROM accounts WHERE name = 'Charlie';
-- 另一个事务修改: UPDATE accounts SET balance = balance + 100 WHERE name = 'Charlie';
SELECT balance FROM accounts WHERE name = 'Charlie';  -- 看到新值 600.00
COMMIT;

-- REPEATABLE READ
TRUNCATE accounts RESTART IDENTITY;
INSERT INTO accounts (name, balance) VALUES ('Charlie', 500.00);
BEGIN ISOLATION LEVEL REPEATABLE READ;
SELECT balance FROM accounts WHERE name = 'Charlie';
-- 另一个事务修改: UPDATE accounts SET balance = balance + 100 WHERE name = 'Charlie';
SELECT balance FROM accounts WHERE name = 'Charlie';  -- 仍看到 500.00
COMMIT;

-- ============================================================
-- 4. 查看 WAL 配置
-- ============================================================
SELECT name, setting, unit
FROM pg_settings
WHERE name IN ('fsync', 'synchronous_commit', 'checkpoint_timeout', 'max_wal_size')
ORDER BY name;

技术点总结

技术点说明
BEGIN / COMMIT / ROLLBACK标准事务控制命令
SAVEPOINT / ROLLBACK TO SAVEPOINT子事务与保存点机制,实现部分回滚
ISOLATION LEVEL事务隔离级别控制
READ COMMITTED默认隔离级别,语句级别快照
REPEATABLE READ事务级别快照,避免不可重复读
MVCC多版本并发控制,读写不互斥
WAL预写日志,保证持久性
fsync强制刷盘,确保持久化
CHECK 约束保证数据一致性

多语言示例

基于上述 Node.js 的 ACID 演示逻辑,本节提供 Go、Python 和 Java 三种语言的完整实现,并附多语言对比表格,便于团队在不同技术栈间迁移与参考。

Go 示例

使用 pgx 驱动(推荐的高性能 PostgreSQL 驱动)。

运行说明
  1. 安装依赖:go get github.com/jackc/pgx/v5
  2. 运行:go run main.go
代码
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/jackc/pgx/v5"
)

func main() {
	ctx := context.Background()

	// 连接配置
	conn, err := pgx.Connect(ctx, "postgres://postgres:your_password@localhost:5432/testdb")
	if err != nil {
		log.Fatal("连接失败:", err)
	}
	defer conn.Close(ctx)

	// 1. 创建测试表
	_, err = conn.Exec(ctx, `
		DROP TABLE IF EXISTS accounts;
		CREATE TABLE accounts (
			id SERIAL PRIMARY KEY,
			name VARCHAR(100) NOT NULL,
			balance DECIMAL(10, 2) CHECK (balance >= 0)
		);
	`)
	if err != nil {
		log.Fatal("建表失败:", err)
	}
	fmt.Println("✓ 测试表创建成功")

	// 2. 原子性演示:事务与保存点
	fmt.Println("\n--- 原子性演示:事务回滚 ---")

	// 开启事务
	tx, err := conn.Begin(ctx)
	if err != nil {
		log.Fatal(err)
	}
	defer tx.Rollback(ctx)

	// 插入 Alice
	_, err = tx.Exec(ctx, "INSERT INTO accounts (name, balance) VALUES ($1, $2)", "Alice", 1000.00)
	if err != nil {
		log.Fatal(err)
	}

	// 创建保存点
	_, err = tx.Exec(ctx, "SAVEPOINT sp1")
	if err != nil {
		log.Fatal(err)
	}

	// 尝试插入 Bob(负数余额,违反 CHECK)
	_, err = tx.Exec(ctx, "INSERT INTO accounts (name, balance) VALUES ($1, $2)", "Bob", -100.00)
	if err != nil {
		fmt.Printf("  捕获错误: %v\n", err)
		// 回滚到保存点
		_, rbErr := tx.Exec(ctx, "ROLLBACK TO SAVEPOINT sp1")
		if rbErr != nil {
			log.Fatal(rbErr)
		}
		fmt.Println("  已回滚到保存点 sp1,Bob 的数据被撤销")
	}

	// 提交事务
	err = tx.Commit(ctx)
	if err != nil {
		log.Fatal(err)
	}

	// 查询结果
	var rows []struct {
		ID      int
		Name    string
		Balance float64
	}
	result, err := conn.Query(ctx, "SELECT * FROM accounts")
	if err != nil {
		log.Fatal(err)
	}
	defer result.Close()
	for result.Next() {
		var r struct {
			ID      int
			Name    string
			Balance float64
		}
		err = result.Scan(&r.ID, &r.Name, &r.Balance)
		if err != nil {
			log.Fatal(err)
		}
		rows = append(rows, r)
	}
	fmt.Println("  最终数据:", rows)
	// 输出: [{1 Alice 1000}]

	// 3. 隔离性演示
	fmt.Println("\n--- 隔离性演示 ---")

	// 清空并准备数据
	_, err = conn.Exec(ctx, "TRUNCATE accounts RESTART IDENTITY")
	if err != nil {
		log.Fatal(err)
	}
	_, err = conn.Exec(ctx, "INSERT INTO accounts (name, balance) VALUES ($1, $2)", "Charlie", 500.00)
	if err != nil {
		log.Fatal(err)
	}

	// 3.1 READ COMMITTED
	fmt.Println("\n  [READ COMMITTED]")
	txRC, err := conn.Begin(ctx)
	if err != nil {
		log.Fatal(err)
	}
	defer txRC.Rollback(ctx)

	// 设置隔离级别
	_, err = txRC.Exec(ctx, "SET TRANSACTION ISOLATION LEVEL READ COMMITTED")
	if err != nil {
		log.Fatal(err)
	}

	var balance float64
	err = txRC.QueryRow(ctx, "SELECT balance FROM accounts WHERE name = $1", "Charlie").Scan(&balance)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("    初始余额: %.2f\n", balance)

	// 模拟另一个事务修改(同一连接内用保存点模拟)
	_, err = txRC.Exec(ctx, "SAVEPOINT mock_other")
	if err != nil {
		log.Fatal(err)
	}
	_, err = txRC.Exec(ctx, "UPDATE accounts SET balance = balance + 100 WHERE name = $1", "Charlie")
	if err != nil {
		log.Fatal(err)
	}
	_, err = txRC.Exec(ctx, "RELEASE SAVEPOINT mock_other")
	if err != nil {
		log.Fatal(err)
	}

	// 再次查询 —— READ COMMITTED 看到新值
	err = txRC.QueryRow(ctx, "SELECT balance FROM accounts WHERE name = $1", "Charlie").Scan(&balance)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("    再次查询: %.2f\n", balance) // 600.00
	txRC.Commit(ctx)

	// 3.2 REPEATABLE READ
	fmt.Println("\n  [REPEATABLE READ]")
	_, err = conn.Exec(ctx, "TRUNCATE accounts RESTART IDENTITY")
	if err != nil {
		log.Fatal(err)
	}
	_, err = conn.Exec(ctx, "INSERT INTO accounts (name, balance) VALUES ($1, $2)", "Charlie", 500.00)
	if err != nil {
		log.Fatal(err)
	}

	txRR, err := conn.Begin(ctx)
	if err != nil {
		log.Fatal(err)
	}
	defer txRR.Rollback(ctx)

	_, err = txRR.Exec(ctx, "SET TRANSACTION ISOLATION LEVEL REPEATABLE READ")
	if err != nil {
		log.Fatal(err)
	}

	err = txRR.QueryRow(ctx, "SELECT balance FROM accounts WHERE name = $1", "Charlie").Scan(&balance)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("    初始余额: %.2f\n", balance)

	// 模拟其他事务修改
	_, err = txRR.Exec(ctx, "SAVEPOINT mock_other2")
	if err != nil {
		log.Fatal(err)
	}
	_, err = txRR.Exec(ctx, "UPDATE accounts SET balance = balance + 100 WHERE name = $1", "Charlie")
	if err != nil {
		log.Fatal(err)
	}
	_, err = txRR.Exec(ctx, "RELEASE SAVEPOINT mock_other2")
	if err != nil {
		log.Fatal(err)
	}

	// 再次查询 —— REPEATABLE READ 仍看到旧值
	err = txRR.QueryRow(ctx, "SELECT balance FROM accounts WHERE name = $1", "Charlie").Scan(&balance)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("    再次查询: %.2f\n", balance) // 500.00
	txRR.Commit(ctx)

	// 4. WAL 配置查询
	fmt.Println("\n--- 持久性说明 ---")
	fmt.Println("  PostgreSQL 通过 WAL(预写日志)保证持久性")
	var walRows []struct {
		Name    string
		Setting string
		Unit    string
	}
	wals, err := conn.Query(ctx, `
		SELECT name, setting, unit
		FROM pg_settings
		WHERE name IN ('fsync', 'synchronous_commit', 'checkpoint_timeout', 'max_wal_size')
		ORDER BY name;
	`)
	if err != nil {
		log.Fatal(err)
	}
	defer wals.Close()
	fmt.Println("  WAL 相关配置:")
	for wals.Next() {
		var name, setting, unit string
		err = wals.Scan(&name, &setting, &unit)
		if err != nil {
			log.Fatal(err)
		}
		fmt.Printf("    %s = %s%s\n", name, setting, unit)
	}
}

Python 示例

使用 psycopg2(最流行的 PostgreSQL 适配器)。

运行说明
  1. 安装依赖:pip install psycopg2-binary
  2. 运行:python demo.py
代码
import psycopg2
from psycopg2 import sql, errors

def main():
    # 连接配置
    conn = psycopg2.connect(
        host="localhost",
        port=5432,
        database="testdb",
        user="postgres",
        password="your_password"
    )
    conn.autocommit = False
    cur = conn.cursor()

    try:
        # 1. 创建测试表
        cur.execute("DROP TABLE IF EXISTS accounts;")
        cur.execute("""
            CREATE TABLE accounts (
                id SERIAL PRIMARY KEY,
                name VARCHAR(100) NOT NULL,
                balance DECIMAL(10, 2) CHECK (balance >= 0)
            );
        """)
        conn.commit()
        print("✓ 测试表创建成功")

        # 2. 原子性演示
        print("\n--- 原子性演示:事务回滚 ---")
        cur.execute("BEGIN")
        cur.execute("INSERT INTO accounts (name, balance) VALUES (%s, %s)", ("Alice", 1000.00))
        cur.execute("SAVEPOINT sp1")
        try:
            cur.execute("INSERT INTO accounts (name, balance) VALUES (%s, %s)", ("Bob", -100.00))
        except errors.CheckViolation as e:
            print(f"  捕获错误: {e}")
            cur.execute("ROLLBACK TO SAVEPOINT sp1")
            print("  已回滚到保存点 sp1,Bob 的数据被撤销")
        cur.execute("COMMIT")
        cur.execute("SELECT * FROM accounts")
        rows = cur.fetchall()
        print("  最终数据:", rows)

        # 3. 隔离性演示
        print("\n--- 隔离性演示 ---")
        cur.execute("TRUNCATE accounts RESTART IDENTITY")
        cur.execute("INSERT INTO accounts (name, balance) VALUES (%s, %s)", ("Charlie", 500.00))
        conn.commit()

        # 3.1 READ COMMITTED
        print("\n  [READ COMMITTED]")
        cur.execute("BEGIN ISOLATION LEVEL READ COMMITTED")
        cur.execute("SELECT balance FROM accounts WHERE name = %s", ("Charlie",))
        balance = cur.fetchone()[0]
        print(f"    初始余额: {balance}")

        # 模拟其他事务
        cur.execute("SAVEPOINT mock_other")
        cur.execute("UPDATE accounts SET balance = balance + 100 WHERE name = %s", ("Charlie",))
        cur.execute("RELEASE SAVEPOINT mock_other")
        cur.execute("SELECT balance FROM accounts WHERE name = %s", ("Charlie",))
        balance = cur.fetchone()[0]
        print(f"    再次查询: {balance}")  # 600.00
        cur.execute("COMMIT")

        # 3.2 REPEATABLE READ
        print("\n  [REPEATABLE READ]")
        cur.execute("TRUNCATE accounts RESTART IDENTITY")
        cur.execute("INSERT INTO accounts (name, balance) VALUES (%s, %s)", ("Charlie", 500.00))
        conn.commit()

        cur.execute("BEGIN ISOLATION LEVEL REPEATABLE READ")
        cur.execute("SELECT balance FROM accounts WHERE name = %s", ("Charlie",))
        balance = cur.fetchone()[0]
        print(f"    初始余额: {balance}")

        cur.execute("SAVEPOINT mock_other2")
        cur.execute("UPDATE accounts SET balance = balance + 100 WHERE name = %s", ("Charlie",))
        cur.execute("RELEASE SAVEPOINT mock_other2")
        cur.execute("SELECT balance FROM accounts WHERE name = %s", ("Charlie",))
        balance = cur.fetchone()[0]
        print(f"    再次查询: {balance}")  # 500.00
        cur.execute("COMMIT")

        # 4. WAL 配置
        print("\n--- 持久性说明 ---")
        print("  PostgreSQL 通过 WAL(预写日志)保证持久性")
        cur.execute("""
            SELECT name, setting, unit
            FROM pg_settings
            WHERE name IN ('fsync', 'synchronous_commit', 'checkpoint_timeout', 'max_wal_size')
            ORDER BY name;
        """)
        print("  WAL 相关配置:")
        for name, setting, unit in cur.fetchall():
            print(f"    {name} = {setting}{unit or ''}")

    except Exception as e:
        print(f"❌ 错误: {e}")
        conn.rollback()
    finally:
        cur.close()
        conn.close()

if __name__ == "__main__":
    main()

Java 示例

使用 HikariCP 连接池和 JDBC 原生 API。

运行说明
  1. 添加依赖(Maven):
    <dependency>
        <groupId>org.postgresql</groupId>
        <artifactId>postgresql</artifactId>
        <version>42.6.0</version>
    </dependency>
    <dependency>
        <groupId>com.zaxxer</groupId>
        <artifactId>HikariCP</artifactId>
        <version>5.0.1</version>
    </dependency>
    
  2. 编译并运行:javac -cp ".:postgresql-42.6.0.jar:HikariCP-5.0.1.jar:slf4j-api-*.jar" Demo.java && java -cp ".:postgresql-42.6.0.jar:HikariCP-5.0.1.jar:slf4j-api-*.jar" Demo
代码
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import java.sql.*;

public class Demo {
    public static void main(String[] args) {
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl("jdbc:postgresql://localhost:5432/testdb");
        config.setUsername("postgres");
        config.setPassword("your_password");
        config.setAutoCommit(false);

        try (HikariDataSource ds = new HikariDataSource(config);
             Connection conn = ds.getConnection()) {

            // 1. 创建测试表
            try (Statement stmt = conn.createStatement()) {
                stmt.execute("DROP TABLE IF EXISTS accounts");
                stmt.execute("""
                    CREATE TABLE accounts (
                        id SERIAL PRIMARY KEY,
                        name VARCHAR(100) NOT NULL,
                        balance DECIMAL(10, 2) CHECK (balance >= 0)
                    );
                """);
                conn.commit();
                System.out.println("✓ 测试表创建成功");
            }

            // 2. 原子性演示
            System.out.println("\n--- 原子性演示:事务回滚 ---");
            try (Statement stmt = conn.createStatement()) {
                conn.setAutoCommit(false);
                stmt.execute("INSERT INTO accounts (name, balance) VALUES ('Alice', 1000.00)");
                stmt.execute("SAVEPOINT sp1");
                try {
                    stmt.execute("INSERT INTO accounts (name, balance) VALUES ('Bob', -100.00)");
                } catch (SQLException e) {
                    System.out.println("  捕获错误: " + e.getMessage());
                    stmt.execute("ROLLBACK TO SAVEPOINT sp1");
                    System.out.println("  已回滚到保存点 sp1,Bob 的数据被撤销");
                }
                conn.commit();

                ResultSet rs = stmt.executeQuery("SELECT * FROM accounts");
                System.out.print("  最终数据: ");
                while (rs.next()) {
                    System.out.print("[id=" + rs.getInt("id") +
                                     ", name=" + rs.getString("name") +
                                     ", balance=" + rs.getBigDecimal("balance") + "] ");
                }
                System.out.println();
            }

            // 3. 隔离性演示
            System.out.println("\n--- 隔离性演示 ---");
            try (Statement stmt = conn.createStatement()) {
                stmt.execute("TRUNCATE accounts RESTART IDENTITY");
                stmt.execute("INSERT INTO accounts (name, balance) VALUES ('Charlie', 500.00)");
                conn.commit();

                // 3.1 READ COMMITTED
                System.out.println("\n  [READ COMMITTED]");
                stmt.execute("BEGIN ISOLATION LEVEL READ COMMITTED");
                ResultSet rs = stmt.executeQuery("SELECT balance FROM accounts WHERE name = 'Charlie'");
                rs.next();
                System.out.println("    初始余额: " + rs.getBigDecimal("balance"));

                stmt.execute("SAVEPOINT mock_other");
                stmt.execute("UPDATE accounts SET balance = balance + 100 WHERE name = 'Charlie'");
                stmt.execute("RELEASE SAVEPOINT mock_other");

                rs = stmt.executeQuery("SELECT balance FROM accounts WHERE name = 'Charlie'");
                rs.next();
                System.out.println("    再次查询: " + rs.getBigDecimal("balance")); // 600.00
                stmt.execute("COMMIT");

                // 3.2 REPEATABLE READ
                System.out.println("\n  [REPEATABLE READ]");
                stmt.execute("TRUNCATE accounts RESTART IDENTITY");
                stmt.execute("INSERT INTO accounts (name, balance) VALUES ('Charlie', 500.00)");
                conn.commit();

                stmt.execute("BEGIN ISOLATION LEVEL REPEATABLE READ");
                rs = stmt.executeQuery("SELECT balance FROM accounts WHERE name = 'Charlie'");
                rs.next();
                System.out.println("    初始余额: " + rs.getBigDecimal("balance"));

                stmt.execute("SAVEPOINT mock_other2");
                stmt.execute("UPDATE accounts SET balance = balance + 100 WHERE name = 'Charlie'");
                stmt.execute("RELEASE SAVEPOINT mock_other2");

                rs = stmt.executeQuery("SELECT balance FROM accounts WHERE name = 'Charlie'");
                rs.next();
                System.out.println("    再次查询: " + rs.getBigDecimal("balance")); // 500.00
                stmt.execute("COMMIT");
            }

            // 4. WAL 配置查询
            System.out.println("\n--- 持久性说明 ---");
            System.out.println("  PostgreSQL 通过 WAL(预写日志)保证持久性");
            try (Statement stmt = conn.createStatement()) {
                ResultSet rs = stmt.executeQuery("""
                    SELECT name, setting, unit
                    FROM pg_settings
                    WHERE name IN ('fsync', 'synchronous_commit', 'checkpoint_timeout', 'max_wal_size')
                    ORDER BY name;
                """);
                System.out.println("  WAL 相关配置:");
                while (rs.next()) {
                    String name = rs.getString("name");
                    String setting = rs.getString("setting");
                    String unit = rs.getString("unit");
                    System.out.printf("    %s = %s%s%n", name, setting, unit == null ? "" : unit);
                }
            }

        } catch (SQLException e) {
            System.err.println("❌ 错误: " + e.getMessage());
            e.printStackTrace();
        }
    }
}

多语言对比表格

维度Node.js (pg)Go (pgx)Python (psycopg2)Java (JDBC + HikariCP)
驱动/库pg (纯 JavaScript)pgx (纯 Go,高性能)psycopg2-binary (C 扩展)postgresql JDBC 驱动
连接管理Pool 内置连接池手动管理或使用 pgxpool手动管理或使用 psycopg2.poolHikariCP (高性能连接池)
事务开始client.query('BEGIN') 或 client.query('START TRANSACTION')conn.Begin(ctx)cur.execute("BEGIN")conn.setAutoCommit(false)
保存点client.query('SAVEPOINT name')tx.Exec(ctx, "SAVEPOINT name")cur.execute("SAVEPOINT name")stmt.execute("SAVEPOINT name")
回滚到保存点ROLLBACK TO SAVEPOINT nametx.Exec(ctx, "ROLLBACK TO SAVEPOINT name")ROLLBACK TO SAVEPOINT nameROLLBACK TO SAVEPOINT name
提交client.query('COMMIT')tx.Commit(ctx)conn.commit()conn.commit()
错误处理try/catch,错误对象包含 SQLSTATEerror 返回值,可断言 pgx.PgErrortry/except,具体异常类(如 CheckViolation)try/catch,SQLException 含 SQLState
占位符$1, $2 (位置参数)$1, $2 (与 PostgreSQL 原生一致)%s 并用 cur.execute(sql, (val,))? (JDBC 标准) 或 $1 (需配置)
异步支持原生 async/await原生 context + 协程需 psycopg2 异步版本 (psycopg2-async) 或 asyncpg需 CompletableFuture + 异步驱动(如 vertx-pg-client)
连接字符串postgresql://user:pass@host:port/db相同关键词参数或 DSNJDBC URL: jdbc:postgresql://host:port/db
设置隔离级别SET TRANSACTION ISOLATION LEVEL ...同 SQL同 SQL同 SQL 或 conn.setTransactionIsolation()
类型映射自动映射 JavaScript 类型自动映射 Go 类型,支持 pgx 自定义映射 Python 类型(Decimal -> decimal.Decimal)通过 ResultSet.getXxx() 映射 Java 类型

技术点总结(多语言通用)

核心技术各语言实现方式
BEGIN / COMMIT / ROLLBACK所有驱动均支持标准 SQL 或 API 方法
SAVEPOINT / ROLLBACK TO SAVEPOINT均支持标准 SQL,部分驱动(如 JDBC)也提供 Savepoint 对象
隔离级别设置可通过 SQL SET TRANSACTION 或驱动 API
错误处理各语言都有对应的异常/错误类型,可识别约束违例等
连接池Node.js 用 pg.Pool,Go 用 pgxpool,Python 用 psycopg2.pool,Java 用 HikariCP
参数化查询防止 SQL 注入,所有驱动均支持

官方文档

参考链接

总结

PostgreSQL 通过 MVCC 和 WAL 两项核心技术全面保证了 ACID 特性。原子性由事务日志与 CLOG 保证,一致性由数据类型、约束和触发器保证,隔离性由 MVCC、锁机制、SSI 和快照视图保证,持久性由 WAL、fsync 和 checkpoint 保证。

子事务(SAVEPOINT)虽提供了细粒度的错误处理能力,但每个子事务都会消耗事务 ID,且超过 64 个时会引发性能问题,生产环境应谨慎使用。ON_ERROR_ROLLBACK 和 JDBC 的 autosave 本质上也是子事务机制的应用,需注意其性能代价。

数据库事务 ACID 特性深度剖析:从 MVCC 到 WAL 的 4 种实现机制对比 本文深度剖析数据库事务ACID特性的实现机制,对比MVCC和WAL等4种核心技术。通过MySQL、PostgreSQL等主流数据库的实践案例,揭示原子性、一致性、隔离性和持久性的底层原理,并提供性能调优建议,帮助开发者优化数据库系统事务处理能力。 阅读详情

相关推荐

【PostgreSQL数据分析实战:从数据清洗到可视化全流程】2.5 事务与锁机制(ACID特性/事务控制语句)

【PostgreSQL数据分析实战:从数据清洗到可视化全流程】2.5 事务与锁机制(ACID特性/事务控制语句)

kngines 616

PostgreSQL 事务(TRANSACTION)深度解析:ACID特性与实战应用

PostgreSQL事务深度解析:ACID特性与实战应用 本文全面剖析PostgreSQL事务机制,涵盖以下核心内容: 事务基础概念:深入解读ACID特性(原子性、一致性、隔离性、持久性) 事务控制.

✨ 欢迎来到【Seal ^_^ 的优快云博客】!✨ 6203

什么是事务?数据库事务的 ACID 特性分别指什么?

在现代应用开发中,数据一致性是系统可靠性的核心支柱。想象以下场景:用户在电商平台下单支付后,订单系统提示“支付成功”但库存未减少;银行转账时,转账方余额已扣但收款方未到账;或在票务系统中,同一座位被多个用户同时购买。这些问题的根源往往可以追溯到事务管理的缺失或不当。数据库事务正是解决此类问题的关键技术,它确保了数据库操作在并发环境和故障情况下的数据一致性与可靠性。定义:数据库事务(Transaction)是由一组SQL语句组成的逻辑执行单元。

AI天才研究院 318

PostgreSQL专栏 : 事务与并发控制深度解析

fill:#333;color:#333;color:#333;fill:none;数据一致性要求并发性能要求数据冲突频率强一致性读一致性基本一致性高并发优先平衡考虑一致性优先冲突频繁冲突较少隔离级别选择决策业务特征分析一致性级别性能优先级冲突概率READ COMMITTED + 应用处理悲观锁 + 高隔离级别乐观锁 + 低隔离级别应用场景映射Web应用报表分析金融核心数据同步批量处理权衡考虑性能 vs 一致性开发复杂度 vs 数据安全。

苦哈哈的 C++ 程序员,写这个技术博客不是为了装逼,也不是为了立人设,是为了收割流量 793

【Unity Shader】2D模型 3D模型混用的穿模问题(Spine在3D场景中出现前后穿模问题的解决办法)

问题描述: 在3D场景中使用Spine模型(2D), 因为Spine需要像Billboard类似的处理,永远是垂直与相机的, 所以一般情况下2D模型能正常显示且前后的阻挡关系也是没有问题的,如下图所示,一切都很美好.(这里用一个四边形,图中的福字表示2D模型) 可是如果后面蓝色的格子比较高,或者2D角色本身也比较高,那么就会出现模型穿插的问题,如下图所示: 如果吧2D模型竖立起来,前后关系肯定是没有问题的,所以也能避免模型穿插的问题,可是就没有Billboard效果,圆形的福字已经被压扁了,如下图:

Vitens的专栏 4594

登临科技Goldwasser加速卡Python SDK实战:从环境配置到YOLOv5模型性能压测

本文详细介绍了登临科技Goldwasser加速卡的Python SDK实战指南,从环境配置到YOLOv5模型性能压测的全过程。通过驱动安装、SDK环境搭建、模型转换与推理优化等步骤,帮助开发者高效利用AI加速卡提升深度学习模型性能。重点分享了批量推理压测技巧和常见问题解决方案,助力开发者充分发挥Goldwasser加速卡的算力优势。

weixin_29035147的博客 302

pg_duckdb事务机制深度剖析:ACID兼容与MVCC实现

在数据库系统中,事务机制是确保数据一致性和可靠性的核心组件。pg_duckdb作为一款融合DuckDB高性能分析能力与PostgreSQL生态的扩展,其事务处理机制在兼顾ACID(原子性、一致性、隔离性、持久性)特性的同时,针对混合存储场景做了特殊优化。本文将从架构设计、实现原理到实际应用,全面解析pg_duckdb的事务处理体系。 ## 事务模型架构概览 pg_duckdb采用双层事务管理架...

gitblog_01148的博客 449

创龙 瑞芯微 RK3588 国产工业开发板—Qt交叉编译环境搭建与GPU图形渲染实战

本文详细介绍了在创龙RK3588国产工业开发板上搭建Qt交叉编译环境并利用GPU进行图形渲染的完整实战流程。文章从环境准备入手,讲解了交叉编译工具链的配置,并提供了命令行与Qt Creator两种编译方法。通过部署运行和自启动设置,确保程序在板卡上稳定运行。最后,通过OpenGL案例实战,展示了如何调用RK3588的GPU进行高效图形渲染,释放其图形处理潜力,适用于工业HMI、智能终端等高要求图形界面开发。

weixin_28278377的博客 451

PostgreSQL WAL机制深度解析与优化

PostgreSQL的预写日志(WAL)机制通过"先写日志后写数据"原则确保数据持久性和崩溃恢复能力。WAL将事务修改先记录到内存缓冲区,强制刷盘后才写入实际数据,保障事务提交后不丢失。日志结构包含16MB的段文件和记录变更的WAL Record,通过LSN标识位置。WAL支持故障恢复(REDO/UNDO)、时间点恢复和主从复制,是物理复制的基础。配置参数如wal_level、fsync等可权衡性能与安全性,生产环境建议启用WAL归档和定期备份。该机制以顺序写替代随机写,成为Postgr

HTTP404_CN的博客 1246

语义分割系列7-Attention Unet(pytorch实现)

本文介绍了AttentionUnet模型和其主要中心思想,并在pytorch框架上构建了Attention Unet模型,构建了Attention gate模块,在数据集Camvid上进行复现。

yumaomi的博客 2万+

硬件基础(4):(7)ADC电压采集:软件校准(标定、拟合)

在实际项目中,**软件端校准**可以有效弥补硬件方面(分压电阻误差、放大器失调、参考源误差等)带来的系统性偏差,从而提高 ADC 测量精度。下面是一个常见的**多点实测校准**流程与思路,个人感觉传感器标定的原理也只这样。

FCZlll的博客 6248

PostgreSQL 核心特性深度解析:开源数据库的全能选手

PostgreSQL作为开源关系型数据库的标杆产品,具备SQL标准高度兼容、原生支持JSON/JSONB和GIS地理信息等核心优势。文章从技术特性、高可用架构及与MySQL对比三个维度进行剖析:PostgreSQL通过流复制和Patroni实现企业级高可用,支持复杂查询、混合数据存储等场景;与MySQL相比,PostgreSQL更擅长处理GIS、半结构化数据和企业级应用。未来,PostgreSQL将持续向云原生、大数据分析及AI融合方向发展,进一步巩固其在开源数据库领域的领先地位。

YongCheng_Liang的博客 1468

在 SAP ABAP 里可靠地对接 FTP 服务器:从连接、命令到传输与排错的完整实战

本文详细介绍了在SAP ABAP中实现FTP文件交换的完整技术方案。文章首先解析了FTP协议的双连接特性(控制连接与数据连接),强调保持控制连接的重要性。随后详细说明了ABAP中FTP操作的四步标准流程:建立连接(包含HTTP_SCRAMBLE加密)、执行命令、断开连接和关闭RFC通道。文章提供了实战案例代码,演示如何将FTP文件复制到应用服务器再读取进内表处理,特别指出后台作业与前台操作的关键差异。最后总结了常见错误排查要点,包括连接模型、路径设置和资源释放等问题。全文通过SAP标准函数模块的调用顺序、典

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 310

MySQL与PostgreSQL的对比与选择:技术深度解析

MySQL是一个流行的开源关系型数据库管理系统,由Oracle公司开发和维护。MySQL以其高性能、可靠性和易用性而闻名,特别适用于Web应用程序和中小型企业。它支持多种存储引擎,如InnoDB、MyISAM和Memory,允许用户根据需求选择合适的存储引擎。

这家伙很懒,什么都没有留下 4583

SAP 系统升级不再惧怕 SPAU 与 SPDD:把修改调整做成可控的工程

SAP系统升级中的SPAU/SPDD处理要点:首先明确SPDD处理数据字典对象,SPAU处理程序对象,核心是解决标准对象变更与历史修改的继承问题。关键原则是优先回归标准版本,减少后续升级负担。操作时应利用版本管理对比差异,批量处理可回退对象,并通过F9多选提高效率。对于提示"当前版本与建议一致"的对象直接回归标准。注意区分新旧SPAU界面功能差异,将Note下载移至SNOTE处理,并确保在SPDD/SPAU之后处理SPAU_ENH。最后,所有调整需记录日志并纳入传输请求,确保可追溯性。遵

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 392

手把手带你实现DAF-Net:零代码基础入门红外与可见光图像融合(从理论到实战)

本文介绍了红外与可见光图像融合技术及DAF-Net网络的应用。红外图像能捕捉热源但缺乏细节,可见光图像纹理丰富但在低光环境下表现不佳。DAF-Net采用双分支特征提取和动态注意力融合架构,能智能分配特征权重实现优势互补。文章详细讲解了环境配置、数据集准备(如TNO数据集)和预处理方法,并提供了DAF-Net的核心代码实现,包括特征提取、注意力融合和图像重建模块。通过该技术可在安防、自动驾驶等领域获得更优质的融合图像,提升视觉感知能力。

m0_71589036的博客 235

深度解析ZITADEL数据库事务实践:5大ACID特性保障与隔离级别应用

ZITADEL作为现代身份基础设施,其数据一致性保障依赖于完善的数据库事务管理。本文将系统剖析ZITADEL如何通过PostgreSQL事务机制实现ACID特性,并结合实际代码案例展示隔离级别在身份管理场景中的最佳实践。 ## 一、事务基础:ZITADEL的ACID实现架构 ZITADEL采用PostgreSQL作为主数据库,通过显式事务控制确保关键操作的数据一致性。在`cmd/setup/6

gitblog_00096的博客 410

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

Apache AGE:基于PostgreSQL的图数据库深度解析

Apache AGE是一个开源项目,最初由Bitnine Global Inc.提供,目前已经成为了Apache软件基金会的顶级项目,使用Apache License 2.0开源协议。它以扩展插件的形式为PostgreSQL提供了图数据库的功能,使得开发者可以在享受PostgreSQL先进的SQL查询功能和事务支持的同时,也能体验到图数据库的灵活性和可扩展性。Apache AGE的最终目标是为各种关系型数据库提供图数据处理和分析功能,包括MySQL和MariaDB等。

MrLi的博客 2385

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

EdgeDB事务处理完全指南:如何利用PostgreSQL的强大ACID特性构建可靠应用

EdgeDB作为新一代图关系数据库,在事务处理方面继承了PostgreSQL的全部优势,为开发者提供了完整的ACID(原子性、一致性、隔离性、持久性)保证。无论是金融交易系统还是电商平台,EdgeDB的事务机制都能确保数据操作的可靠性和一致性。😊 ## 🎯 EdgeDB事务的核心优势 EdgeDB建立在PostgreSQL之上,这意味着它天生就拥有PostgreSQL经过数十年验证的稳定事

gitblog_00911的博客 436

CrackingTheSQLInterview核心篇:事务ACID特性与并发控制实战解析

在数据库管理与SQL面试中,事务ACID特性和并发控制是每个开发者必须掌握的核心概念。无论是处理金融交易、电商订单还是用户数据管理,理解事务的原子性、一致性、隔离性和持久性(ACID)原理,以及如何在实际场景中应用并发控制机制,都是面试官重点考察的技术能力。本文将深入解析SQL事务的ACID特性,并通过实际案例展示并发控制的实现方法,帮助您在SQL面试中脱颖而出。 ## 🔍 什么是数据库事务?

gitblog_00249的博客 453

深入解析ACID原理:数据库事务的四大基石

在现代数据库系统中,ACID(原子性、一致性、隔离性、持久性)如同守护数据的四大骑士,确保即使在系统崩溃或并发访问的情况下,数据也能保持正确与可靠。本文将深入剖析主流数据库(MySQL/Oracle等)的实现机制,并通过原理图示和代码示例揭示ACID背后的黑科技。

weixin_37840904的博客 1075

2025年主流数据库PostgreSQL MySQL Oracle MongoDB深度解析:技术特性、选型指南与应用实践

产品入口:https://www.influxdata.com/get-influxdb专为时间序列数据设计的数据库,支持高写入吞吐量和灵活的查询语法,适用于物联网(IoT)和监控场景。官网:https://www.mongodb.com领先的文档型 NoSQL 数据库,支持水平扩展和灵活的数据模型,适合现代应用开发。中国区:https://www.oracle.com/cn商业数据库领域的领导者,提供企业级解决方案,支持分布式和云原生架构。

chenchuang0128的博客 557

深度详解 PostgreSQL:从核心原理、独有特性到性能优化与实战选型

支持 PL/pgSQL 专属编程语言,可自定义函数、存储过程,将高频、复杂的业务逻辑下沉至数据库层执行,减少应用与数据库的交互次数,提升业务响应速度。同时支持函数重载、参数自定义,适配各类个性化业务逻辑封装。

Solis的博客 672

PostgreSQL与MySQL核心差异深度解析:架构、特性与选型指南

数据库作为现代应用的核心组件,其底层架构与设计哲学决定了系统的扩展性、稳定性与适用场景。从技术原理层面看,数据库通过不同的并发控制、存储引擎和查询优化机制来保障数据一致性与访问效率。在众多开源数据库中,PostgreSQL和MySQL凭借各自鲜明的技术特点与活跃的生态,成为企业级应用中最常见的选择。PostgreSQL以其严格的SQL标准支持、丰富的内置数据类型和强大的扩展能力著称,尤其擅长处理复杂查询、地理空间数据和半结构化JSONB数据,其多进程架构为高稳定性与复杂分析场景提供了坚实基础。而MySQL则

weixin_34101229的博客 338

PostgreSQL深度解析:为什么它是2025年最值得选择的数据库?

PostgreSQL不仅仅是一个数据库,它是一个生态系统,一个社区,一种哲学。技术前瞻性:在AI和数据科学领域始终保持领先开放性:没有供应商锁定的自由,拥抱开源生态可靠性:经过近40年时间考验的稳定性可扩展性:从初创公司到AI独角兽都能支撑的扩展能力现在就开始你的PostgreSQL之旅吧!在这个AI驱动的数据世界里,PostgreSQL将成为你最可靠、最先进的数据伙伴。

weixin_45979388的博客 869
上一篇: PostgreSQL笔记27: 双缓存架构下的内存管理与性能观测实践
下一篇: PostgreSQL笔记29:事务隔离级别深度解析——从MVCC快照到SSI可串行化
Wang's Blog
博客等级 码龄12年 7438粉丝 2421原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wang's Blog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值