`

阻止同一表中,两个并发会话插入相同记录的解决方案(原创)

阅读更多

阻止同一表中,两个并发会话插入相同记录的解决方案

背景

    两个并发会话,要并发访问一张表,并向表中插入记录,主键 ID 为自增长的序号,所以两个会话肯定会得到不同的主键 Id ,靠这个 ID 主键约束不能解决阻止插入重复记录的问题。

举例

系统存在如下一张表

create table test (

   id integer ,

   value varchar ( 20 ),

   primary key ( id )

);

表中 ID 值来自业务需求上的自增长序列。

方案

方案一

test 表的 value 字段上建立一个唯一约束(如果表中的字段为多个,则表这些字段全部加到唯一约束上去即可)。

alter table test add constraints test_001 unique ( value );

优缺点

优点:此种方案最简单,几乎接触过数据库的人都可以做,而且省时省力。

缺点:影响性能,如果大批量的数据通过多个会话并发插入的话,数据库会成为性能瓶颈,因为每插入一条数据,数据库都要去检查唯一约束。

方案二

步骤 1 ,分配权限

为用数据库用户分配权限,因为下面的 trigger ,使用到了 dbms_lock dbms_utility 包。

否则将报错误。(图片没法显示,错误码信息是:ORA-04098:触发器XXX无效且未通过重新验证)


sysdba 身份登陆数据库,为 imeg 用户(本人的数据库用户名为 imeg )分配权限,命令如下:

huawei:~ # su - oracle

oracle@huawei:~> sqlplus '/as sysdba';

SQL> grant execute on dbms_lock to imeg;

Grant succeeded.

SQL> grant execute on dbms_utility to imeg;

Grant succeeded.

-- 可能上面的 grant 是多余的(没测试),只要下面这句就可以了。在此有点脱裤子放屁的感觉。

SQL> grant execute any procedure to imeg;

Grant succeeded.

注意:权限分配了以后,可能要过一段时间才会生效,我操作上就是这样,分配完直接操作会报错。不知是 Oracle 的什么原因所至。作为一个疑问,希否定知道的可以告知。在此留下答题卡:

答: ____________________________________________________________________________

步骤 2 ,建立触发器

使用 trigger ,在 test 表上建立如下的 trigger

create or replace trigger test_trigger

before insert on test

for each row

declare

    l_lock_id   number ;

    resource_busy   exception ;

    pragma exception_init ( resource_busy, - 54 );

begin

    l_lock_id :=

       dbms_utility.get_hash_value( :new.value, 0 , 1024 );

    if ( dbms_lock.request

             (  id                 => l_lock_id,

                lockmode          => dbms_lock.x_mode,

                --timeout           => 0,

-- 此处的 timeout 是为了让第二个会话如果存在唯一值冲突时,是否立即返回,默认为 oracle 的最大时间,等第一个会话提交时返回。如果为 0 ,则表示如果有冲突,不等第一个会话提交,直接返回错误信息。

                release_on_commit => TRUE ) <> 0 )

    then

        raise resource_busy;

    end if ;

end ;

 

步骤 3 ,将应中的 SQL 语句改为 merge

会话 1

merge into test_meger t1 using ( select 'value' value from dual) t2 on (t1.value=t2.value)

   --when matched then

   --   dbms_output.put_line('null')

   when not matched then

      insert (t1.id,t1.value) values ( 2 , 'value' );

 

会话 2

merge into test_meger t1 using ( select 'value' value from dual) t2 on (t1.value=t2.value)

   --when matched then

   --   dbms_output.put_line('null')

   when not matched then

      insert (t1.id,t1.value) values ( 3 , 'value' );

会话执行描述

1、  会话 1 提交后,会话 2 执行:

由于会话 2 merge on 条件, t1.value=t2.value 此时可以看到会话 1 提交的数据,所以会话 2 什么也不错。

可以消除 ID 不同的重复记录。

2、  会话 1 执行,但未提交,会话 2 执行:

由于会话 1 没有提交,会话 2 merge on 条件, t1.value=t2.value 此时可以看不到会话 1 提交的数据,但是由于步骤 2 的触发器,使会话 2 处于阻塞状态,得不到执行。如果此时去掉触发器关于 timeout 的注释,会话 2 将直接报错返回,而不会阻塞。

3、   

优缺点

优点:将 timeout=> 0 加上,可以让会话 2 不用等待会话 1 提交,如果存在相同的值,则直接返回。减少了会话 2 的等待时间。

缺点:如果会话 1 没提交,而会话 2 由于 timeout=> 0 而报错返回了,则会丢失本来应该插入的记录。另外,这个同样存在效率问题,如果电信级的应用,不推荐用这种做法,可能需要在应用层进行序列化控制,应用层同样有缺点,比如:怎么解决应用层集群并发写入的问题。有好的解决方案,还请不吝赐教啊。

总结

上述的解决方案二,仅适合于 oracle 的数据库,对其它的数据库,可能要从数据库隔离级别入手去解决了,比如:将隔离级别设为序列化的,在事务开始时,先去查询一下是否有同值记录存在,存在则退出,否则插入,序列化的隔离级别是性能大忌。

 

以上仅是本个的一些浅建,如有更好的解决方案可以说出来,大家探讨啊。

 

分享到:
评论

相关推荐

    MySQL 死锁产生原因和解决办法

    ### MySQL 死锁产生原因与解决方案详解 #### 一、MySQL锁类型介绍 MySQL支持多种锁机制来确保数据的一致性和事务的隔离性。锁的主要目的是控制并发操作时的资源访问,防止数据不一致的问题。根据锁的作用范围不同...

    oracle错误一览表

    - **描述**: 当两个或多个会话设置了不同的DML_LOCKS时触发。 - **解决方法**: 确认所有会话设置相同的DML_LOCKS。 #### 36. ORA-00062: 无法获得DML全表锁定;DML_LOCKS为0 - **描述**: 当尝试获取全表锁时发现`...

    latch Contention

    简单来说,锁的作用是防止两个或多个进程同时更新相同的SGA区域,从而避免数据损坏或不一致的问题。 Oracle会话在执行几乎所有的数据库操作时都需要访问或修改SGA中的数据。例如: - 当一个会话从磁盘读取数据块时...

    分布式.pdf_电子版_pdf版

    Redis提供了更优的解决方案,如SETNX指令,可以原子性地设置一个键,如果键不存在。配合EXPIRE指令设置过期时间,可以解决锁的自动释放问题。RedLock算法进一步增强了分布式锁的可靠性,通过在多个Redis实例之间获取...

    net面试题及答案(一)归类.pdf

    解决方案包括分批插入、优化事务大小、增加内存或磁盘空间,以及调整数据库参数以支持更大并发。 9. 数据去重:在有主键和外键约束的数据库中,不能有重复数据。可以通过添加自增ID字段,然后删除重复行,或者使用...

    HyperSQL_User_Guide.pdf

    通过以上内容,我们可以看出HSQLDB不仅具备现代关系型数据库管理系统的核心功能,而且还提供了一系列高级特性,使其成为一个强大且灵活的数据库解决方案。无论是用于开发应用程序还是作为教学工具,HSQLDB都能满足...

    PGSQL常见的问题

    这通常是因为临时表的生命周期与函数调用不匹配,或是在不同的会话中尝试访问同一临时表。正确处理临时表的生命周期和作用域可以解决这类问题。 #### 48. 目前有哪些数据复制方案可用? PostgreSQL支持多种复制...

    mysql面试题整理资源

    - **幻读**:在可重复读隔离级别下,可能导致同一事务两次读取到不同数量的数据,因为有新的插入。 - **间隙锁(Next-Key Locks)**:防止幻读,锁定索引记录及其之间的间隙,只有在可重复读隔离级别下有效。 11....

    基于C#和SQL的网上选课系统

    【基于C#和SQL的网上选课系统】是一种利用现代技术改造传统选课流程的解决方案。C#,作为微软.NET框架的主要编程语言,以其高效、安全和面向对象的特性,常用于开发Web应用程序。SQL(Structured Query Language)是...

    有关Oracle常见的性能优化的方法

    - **SGA (System Global Area)** 和 **PGA (Program Global Area)** 是Oracle数据库中的两个主要内存区域。SGA由多个组成部分组成,如数据缓冲区、共享池、redo日志缓冲区等。PGA则包含每个用户会话的私有内存。 -...

    .NET面试题.

    8. **海量数据插入**:大量数据插入可能导致数据库性能瓶颈,解决方案包括批量插入、分批处理、使用存储过程、优化索引、调整数据库配置(如缓存大小、日志文件等)以及利用数据库的批量导入功能(如BULK INSERT)。...

    20 数据库高频知识点.docx

    - **内连接(INNER JOIN)**:仅返回两个表中匹配的行。 - **外连接(LEFT JOIN/RIGHT JOIN)**:返回左表或右表的所有行,即使在另一表中没有匹配。 - **交叉连接(CROSS JOIN)**:返回所有可能的组合,即笛卡尔积...

    net 笔试题汇总

    1. **数据交换**:在不使用第三方变量的情况下,可以通过加减运算或异或操作来交换两个变量的值。如`a=a+b; b=a-b; a=a-b;`或者使用异或操作`a^=b^(b^=a^b);`,异或操作交换变量的原理是异或两次同一个数等于零。 2...

    hibernate二级缓存实例

    以Ehcache为例,它是Hibernate常用的一种二级缓存解决方案,提供了内存和磁盘两层缓存,支持自动过期策略,确保数据的实时性。 配置二级缓存主要包括以下几个步骤: 1. 引入依赖:在项目的pom.xml或build.gradle...

    oracal概念手册中英文版

    Oracle提供了多种高可用性解决方案,如Real Application Clusters (RAC)、Data Guard等,确保数据库的连续运行。 #### 第18章 分区表及分区索引 分区是一种将大表分割成较小、更易于管理的部分的技术。本章介绍了...

    (c++)笔试题

    - **解决方案**:使用数组模拟大数的存储与运算。 - **实现细节**: - 定义数组,每个元素表示一位数字。 - 实现加法、乘法等基本运算规则。 - 通过模拟进位机制处理大数运算。 ### 6. 进程与线程的区别 - **...

    java程序猿 问题总结.txt

    当发生冲突时,即两个不同的键具有相同的哈希值时,新的键值对将被添加到对应的链表末尾。这样可以保证每个键值对都有唯一的存储位置,即使它们的哈希值相同。 以上是对Java程序猿在面试过程中可能会遇到的一些核心...

    java中高级面试必备技术

    Dubbo是一个高性能、轻量级的开源服务框架,它提供了一整套解决方案,包括服务发布、发现、调用、负载均衡、容错、限流等。Dubbo支持多种服务注册与发现方式,其中Zookeeper是最常用的一种。 **1.2 Dubbo工作原理**...

    spring 事务

    #### 四、并发事务带来的问题及解决方案 在多用户环境中,多个事务并发执行可能会带来一系列问题,如脏读、不可重复读、幻读等。 1. **脏读**:一个事务读取了另一个事务未提交的数据。 2. **不可重复读**:在一个...

    CON1399-利用OracleDatabase12c整合数据库.pptx

    **DBA的挑战与解决方案** Oracle Database 12c 的多租户架构解决了DBA在传统环境中面临的挑战,如频繁的修补和升级、供应压力、性能和可扩展性的需求。通过标准化服务和自助服务,DBA可以更轻松地管理大量的数据库...

Global site tag (gtag.js) - Google Analytics