PostgreSQL 中如何处理数据的并发更新冲突解决?

news2024/9/23 11:21:33

文章目录

  • 一、并发更新冲突的场景
  • 二、PostgreSQL 中的并发控制机制
    • (一) 封锁机制
    • (二) 事务隔离级别
  • 三、并发更新冲突的解决方法
    • (一) 重试机制
    • (二) 使用乐观并发控制
    • (三) 使用悲观并发控制
    • (四) 应用版本字段
    • (五) 基于时间戳的冲突解决
  • 四、实际应用中的考虑因素
    • (一) 性能影响
    • (二) 业务逻辑适应性
    • (三) 数据分布和访问模式
  • 五、示例分析

美丽的分割线

PostgreSQL


在数据库并发操作环境中,多个事务同时尝试更新相同的数据可能导致冲突。PostgreSQL 提供了一系列机制来处理这些并发更新冲突,以确保数据的一致性和完整性。

美丽的分割线

一、并发更新冲突的场景

当两个或多个事务同时尝试对同一行数据进行修改时,就可能发生并发更新冲突。常见的场景包括:

  1. 同时修改同一行的不同列
  2. 同时对同一列进行不同的值更新

美丽的分割线

二、PostgreSQL 中的并发控制机制

PostgreSQL 主要使用 MVCC(多版本并发控制,Multiversion Concurrency Control ) 来处理并发事务。MVCC 允许事务读取到符合其隔离级别需求的数据版本,而不需要加锁阻塞其他事务的读操作。然而,在写操作时,仍可能出现冲突。

(一) 封锁机制

PostgreSQL 使用多种类型的锁来控制对数据的并发访问。常见的锁类型包括:

  1. 共享锁(Shared Lock):允许其他事务也获取共享锁,但阻止获取排他锁。常用于读取操作。
  2. 排他锁(Exclusive Lock):阻止其他事务获取任何类型的锁,常用于写入操作。

锁的粒度可以是行级(Row-Level)、页级(Page-Level)和表级(Table-Level)。

(二) 事务隔离级别

PostgreSQL 支持四种事务隔离级别:

  1. 读未提交(Read Uncommitted):这是最低的隔离级别,一个事务可以读取到其他事务未提交的数据修改,可能导致脏读、不可重复读和幻读等问题。
  2. 读已提交(Read Committed):事务只能读取已经提交的数据,避免了脏读,但仍可能出现不可重复读和幻读。
  3. 可重复读(Repeatable Read):在一个事务内多次读取相同的数据会得到相同的结果,避免了不可重复读,但可能出现幻读。
  4. 串行化(Serializable):最高的隔离级别,通过严格的并发控制确保事务的串行执行,避免了脏读、不可重复读和幻读。

美丽的分割线

三、并发更新冲突的解决方法

(一) 重试机制

一种简单的方法是当冲突发生时,让事务进行重试。示例如下:

DO
$$
DECLARE
    conflict_detected BOOLEAN := FALSE;
BEGIN
    LOOP
        -- 尝试执行更新操作
        UPDATE products SET price = 100 WHERE id = 1;

        -- 检查是否有冲突(例如,通过检查受影响的行数)
        IF NOT FOUND THEN
            conflict_detected := TRUE;
        ELSE
            EXIT;
        END IF;

        -- 若有冲突,等待一段时间并重试
        IF conflict_detected THEN
            PERFORM pg_sleep(1);
        END IF;
    END LOOP;
END;
$$;

在上述示例中,如果更新操作没有影响到任何行(表示可能存在冲突),则设置一个标志,等待一段时间后重试。

(二) 使用乐观并发控制

乐观并发控制假设并发冲突很少发生。在这种方式中,事务在更新数据时不进行加锁,而是在提交时检查数据是否被其他事务修改。如果没有冲突,事务成功提交;如果有冲突,事务回滚并根据需要重试。

-- 获取数据的初始版本
SELECT price AS original_price FROM products WHERE id = 1;

-- 进行业务处理和修改
UPDATE products SET price = 100 WHERE id = 1 AND price = original_price;

在上述示例中,更新操作仅在数据未被其他事务修改的情况下成功。

(三) 使用悲观并发控制

悲观并发控制则假设并发冲突很可能发生,在事务执行期间获取所需的锁来阻塞其他可能冲突的事务。

BEGIN;

-- 获取排他锁
LOCK TABLE products IN SHARE ROW EXCLUSIVE MODE;

-- 进行数据更新
UPDATE products SET price = 100 WHERE id = 1;

COMMIT;

(四) 应用版本字段

给表添加一个版本字段来跟踪数据的更改。

CREATE TABLE products (
    id SERIAL PRIMARY KEY,
    price DECIMAL(10, 2),
    version INT DEFAULT 0
);

在更新数据时,同时递增版本字段:

UPDATE products SET price = 100, version = version + 1 WHERE id = 1 AND version = <expected_version>;

如果更新影响的行数为 0,表示存在冲突,因为预期的版本与实际的版本不一致。

(五) 基于时间戳的冲突解决

为每行数据添加一个时间戳字段,记录数据的最后修改时间。

CREATE TABLE products (
    id SERIAL PRIMARY KEY,
    price DECIMAL(10, 2),
    last_modified TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

在更新时,仅更新时间戳比当前事务读取的时间戳更早的数据:

UPDATE products SET price = 100 WHERE id = 1 AND last_modified <= <read_timestamp>;

美丽的分割线

四、实际应用中的考虑因素

(一) 性能影响

  1. 不同的冲突解决方法对数据库性能有不同的影响。例如,使用封锁可能导致其他事务的等待,增加系统的阻塞时间,从而影响并发性。而乐观并发控制在冲突很少发生时性能较好,但在冲突频繁时可能导致大量的事务重试,增加了总体的执行时间。
  2. 应用版本字段或基于时间戳的方法可能需要额外的存储空间来维护版本或时间戳信息,并在更新时进行额外的判断和处理。

(二) 业务逻辑适应性

  1. 某些业务场景可能更适合某种特定的冲突解决方法。例如,如果业务对数据的一致性要求非常高,不能容忍任何不一致的情况,那么悲观并发控制或串行化隔离级别可能是更好的选择。
  2. 对于冲突不太频繁且对响应时间要求较高的场景,乐观并发控制可能更合适。

(三) 数据分布和访问模式

  1. 如果数据的访问是高度并发的,并且多个事务经常同时访问相同的数据行,那么需要更加谨慎地选择冲突解决方法,以避免过度的阻塞和冲突。
  2. 对于数据分布较为均匀,冲突概率较低的情况,可以采用相对简单和高效的方法,如乐观并发控制。

美丽的分割线

五、示例分析

假设我们有一个在线商店的库存管理系统,其中有一个 inventory 表来存储商品的库存数量。

CREATE TABLE inventory (
    product_id INT PRIMARY KEY,
    quantity INT,
    last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

现在有两个并发的事务:

事务 1:

BEGIN;
SELECT * FROM inventory WHERE product_id = 1;
-- 假设读取到的数量为 10
UPDATE inventory SET quantity = 5 WHERE product_id = 1 AND last_updated <= <read_timestamp>;
COMMIT;

事务 2:

BEGIN;
SELECT * FROM inventory WHERE product_id = 1;
-- 假设也读取到的数量为 10
UPDATE inventory SET quantity = 8 WHERE product_id = 1 AND last_updated <= <read_timestamp>;
COMMIT;

如果这两个事务几乎同时执行,可能会发生冲突。

如果我们采用基于时间戳的冲突解决方法:

  1. 事务 1 读取数据时获取了当前的时间戳(T1)。
  2. 事务 2 读取数据时获取了稍晚的时间戳(T2)。

当事务 1 尝试更新时,如果自它读取以来没有其他事务修改数据(即 last_updated <= T1),则更新成功。

当事务 2 尝试更新时,如果发现数据的 last_updated 大于 T2(说明在事务 2 读取之后被修改过),则更新失败,事务 2 可以选择回滚并重试,或者根据业务逻辑进行其他处理。


美丽的分割线

🎉相关推荐

  • 🍅关注博主🎗️ 带你畅游技术世界,不错过每一次成长机会!
  • 📚领书:PostgreSQL 入门到精通.pdf
  • 📙PostgreSQL 中文手册
  • 📘PostgreSQL 技术专栏

PostgreSQL

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1915587.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

一句歌词描述夏天

夏天总是带着一种奇特的魔力&#xff0c;既能让人沉醉在阳光和海浪的浪漫中&#xff0c;也能在炎热与燥热中让人心生烦闷。特别是在夏日里情绪低落时&#xff0c;那些可以抚平心情的歌曲显得尤为珍贵。音乐&#xff0c;这个神奇的存在&#xff0c;总能在最需要的时候带来心灵的…

[激光原理与应用-107]:南京科耐激光-激光焊接-焊中检测-智能制程监测系统IPM介绍 - 10 - 什么是虚焊,如何检测虚焊?

目录 一、前言&#xff1a;虚焊概述 1.定义与特征 2.产生原因 3.危害与影响 4.预防措施 二、虚焊的检测方法 2.1 概述 1. 直观检查法 2. 晃动法 3. 敲击法 4. 补焊法 5. 非破坏性检测方法 6. 电性能测试 2.2 示例 一、前言&#xff1a;虚焊概述 虚焊是一种常见的…

Java SQL 连接(初级)

实训Day3 记实 实训第三天&#xff0c;今天是头脑风暴的第二天&#xff0c;课程将SQL与Java&#xff08;idea&#xff09;代码结合&#xff0c;这是一项具有挑战性的代码课程。课程将两个应用结合起来&#xff0c;展现了Java代码的跨平台性&#xff0c;展现了Java语言的封装性…

关于数组的常见算法

一、案例一 案例说明 案例&#xff1a;定义一个int型的一维数组&#xff0c;包含10个元素&#xff0c;分别赋一些随机整数&#xff0c;然后求出所有元素的最大值&#xff0c;最小值&#xff0c;总和&#xff0c;平均值&#xff0c;并输出出来 要求&#xff1a;所有随机数都是两…

Windows环境人大金仓数据库命令常规操作

Windows环境人大金仓数据库命令常规操作 下文将介绍人大金仓数据库常见命令操作&#xff0c;包括具体使用命令如创建数据库、创建用户、授权等相关操作。 1、打开命令提示符窗口 找到数据库安装目录进入server/bin目录&#xff0c;输入cmd,打开命令提示符窗口&#xff0c;如…

如何批量更改很多个文件夹里的文件名中包含文件夹名?

&#x1f3c6;本文收录于《CSDN问答解惑-专业版》专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收…

【论文速读】《面向深度学习的联合消息传递与自编码器》

这篇文章来自华为的渥太华无线先进系统能力中心和无线技术实验室&#xff0c;作者中有大名鼎鼎的童文。 一、自编码架构的全局收发机面临的主要问题 文章对我比较有启发的地方&#xff0c;是提到自编码架构的全局收发机面临的主要问题&#xff1a; 问题一&#xff1a;基于随…

Nature Renderer 2022(植被渲染工具插件)

渲染大量详细的植被。 自然渲染器通过替换Unity的默认地形细节和树系统来提高植被渲染的质量。一切都适用于现有数据:使用相同的草地、植被和树木,并保留现有地形。我们只是升级您的渲染器。 Unity验证的解决方案 Nature Renderer受到25000多名开发人员的信任,是Unity验证的…

洛谷P1498 南蛮图腾[递归好题]

南蛮图腾 题目背景 自从到了南蛮之地&#xff0c;孔明不仅把孟获收拾的服服帖帖&#xff0c;而且还发现了不少少数民族的智慧&#xff0c;他发现少数民族的图腾往往有着一种分形的效果&#xff0c;在得到了酋长的传授后&#xff0c;孔明掌握了不少绘图技术&#xff0c;但唯独…

鼠标哪款好用?2024年选这几款鼠标不会出错

鼠标可是我们电脑的好伙伴&#xff0c;它能大大提高我们的工作效率。现在&#xff0c;无线鼠标变得越来越受欢迎&#xff0c;因为没有乱七八糟的线缆&#xff0c;用起来既方便又省空间。对于爱玩游戏的朋友们来说&#xff0c;挑选一个称心如意的鼠标可是件头疼的事。别急&#…

蓝牙控制小车

demo.main.c motor.c uart.c motor.h uart.h 实物图

c++:面向对象的继承特性

什么是继承 (1)继承是C源生支持的一种语法特性&#xff0c;是C面向对象的一种表现 (2)继承特性可以让派生类“瞬间”拥有基类的所有&#xff08;当然还得考虑权限&#xff09;属性和方法 (3)继承特性本质上是为了代码复用 (4)类在C编译器的内部可以理解为结构体&#xff0c;派…

初学SpringMVC之 RestFul 风格、重定向和转发

RestFul 风格改变 URL 形式 比如之前是&#xff1a;http://localhost:8080/add?a1&b2 现在是&#xff1a;http://localhost:8080/add/a/b&#xff08;全是斜杠&#xff09; package com.demo.controller;import org.springframework.stereotype.Controller; import org…

鸿蒙系统创建签名文件及使用创建签名文件打包并安装

* 第一步 第二步&#xff1a;创建.p12文件&#xff0c;点击New如果有的话就Choose Existing 填好下面信息 点击Next进入到下面界面 开始生成csr文件如下图 点击OK–>Finish 文件保存在了下面目录 第三步 1.访问华为开发者平台&#xff0c;登录开发者账号&#xff0c;进…

应急响应-ELK日志分析系统

&#x1f3bc;个人主页&#xff1a;金灰 &#x1f60e;作者简介:一名简单的大一学生;易编橙终身成长社群的嘉宾.✨ 专注网络空间安全服务,期待与您的交流分享~ 感谢您的点赞、关注、评论、收藏、是对我最大的认可和支持&#xff01;❤️ &#x1f34a;易编橙终身成长社群&#…

昇思25天学习打卡营第15天|基于 MindSpore 实现 BERT 对话情绪识别

文章目录 昇思MindSpore应用实践1、基于 MindSpore 实现 BERT 对话情绪识别BERT 模型简介数据集数据加载和数据预处理 2、模型训练模型验证 3、模型推理 Reference 昇思MindSpore应用实践 本系列文章主要用于记录昇思25天学习打卡营的学习心得。 1、基于 MindSpore 实现 BERT…

尚品汇-(十六)

商品详情功能开发 &#xff08;1&#xff09;搭建service-item 点击service&#xff0c;选择New–>Module,操作如下 修改配置pom.xml 添加配置文件bootstrap.properties spring.application.nameservice-item spring.profiles.activedev spring.cloud.nacos.discovery.se…

centOS79中安装nginx12.15

##red## &#x1f534; 大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff0c;雄雄的小课堂。 前言 装了这么多&#xff0c;发现Nginx是最简单的&#xff0c;一次性就搞定了。下面我们来看看如何安装 安装Nginx 安装gcc-c编译器 分开运行&#xff1a; yum…

医疗机器人中的具身智能进展——自主超声策略模型的任务编码和局部探索

医疗机器人一直是具身智能的研究热点。医学图像、医疗触诊、血压血氧、心率脉搏和生物电信号等多模态生物医学信息&#xff0c;不断丰富着医疗机器人的感知范畴。 自主超声 “自主超声”属于具身智能医疗机器人领域中话题度较高的研究方向。作为临床检查的重要手段之一&#…

27. 738.单调递增的数字,968.监控二叉树,贪心算法总结

class Solution { public:int monotoneIncreasingDigits(int n) {string strNum to_string(n);// flag用来标记赋值9从哪里开始// 设置为这个默认值&#xff0c;为了防止第二个for循环在flag没有被赋值的情况下执行int flag strNum.size();for(int i strNum.size() - 1; i &…