3

我有一个多线程进程,它将多条记录插入到一​​个表中。插入在存储过程中执行,序列生成INTO一个变量,该变量稍后在INSERT.

鉴于我没有在mysequence.nextval其内部进行INSERT操作,这让我认为两个并发进程可以按一个顺序获取一个序列,然后以相反的顺序进行插入。如果是这种情况,那么序列号将不会反映插入的真实顺序。

我还将每个插入的列记录sysdate在一个DATE列中,但我注意到两个记录的日期经常匹配,我需要按序列号排序以打破平局。但是鉴于上一期,这似乎并不能保证实际的插入顺序。

如何确定插入数据库的绝对顺序?

4

7 回答 7

5

DATE 数据类型只到秒,而 TIMESTAMP 到毫秒。这能解决问题吗?

根据甲骨文的文档:

TIMESTAMP:日期的年、月和日值,以及时间的小时、分钟和秒值,其中 fractional_seconds_precision 是 SECOND 日期时间字段小数部分的位数。可接受的 fractional_seconds_precision 值为 0 到 9。默认值为 6。默认格式由 NLS_DATE_FORMAT 参数显式确定或由 NLS_TERRITORY 参数隐式确定。大小从 7 到 11 个字节不等,具体取决于精度。此数据类型包含日期时间字段 YEAR、MONTH、DAY、HOUR、MINUTE 和 SECOND。它包含小数秒,但没有时区。

date没有:

日期:有效日期范围从公元前 4712 年 1 月 1 日到公元 9999 年 12 月 31 日。默认格式由 NLS_DATE_FORMAT 参数显式确定或由 NLS_TERRITORY 参数隐式确定。大小固定为 7 个字节。此数据类型包含日期时间字段 YEAR、MONTH、DAY、HOUR、MINUTE 和 SECOND。它没有小数秒或时区。

当然,话虽如此,我不确定为什么写记录时很重要,但这是一种可能解决您的问题的方法。

于 2010-07-16T15:19:43.553 回答
2

序列应该是线程安全的:

create table ORDERTEST (
    ORDERID number not null ,
    COLA   varchar2(10) ,
    INSERTDATE date default sysdate,
    constraint ORDERTEST_pk primary key (orderid)
) ;

create sequence ORDERTEST_seq start with 1 nocycle nocache ;

insert into ORDERTEST (ORDERID, COLA, INSERTDATE)
                select ORDERTEST_SEQ.NEXTVAL , substr(OBJECT_NAME,1,10), sysdate 
                  from USER_OBJECTS 
                 where rownum <= 5; --just to limit results

select * 
  from ORDERTEST
 order by ORDERID desc ;

 ORDERID                COLA       INSERTDATE                
---------------------- ---------- ------------------------- 
5                      C_COBJ#    16-JUL-10 12.15.36        
4                      UNDO$      16-JUL-10 12.15.36        
3                      CON$       16-JUL-10 12.15.36        
2                      I_USER1    16-JUL-10 12.15.36        
1                      ICOL$      16-JUL-10 12.15.36  

现在在另一个会话中:

insert into ORDERTEST (ORDERID, COLA, INSERTDATE)
            select ORDERTEST_SEQ.NEXTVAL , substr(OBJECT_NAME,1,10), sysdate 
              from USER_OBJECTS 
             where rownum <= 5; --just to limit results

select * 
  from ORDERTEST
 order by ORDERID desc ;

 5 rows inserted
ORDERID                COLA       INSERTDATE                
---------------------- ---------- ------------------------- 
10                     C_COBJ#    16-JUL-10 12.17.23        
9                      UNDO$      16-JUL-10 12.17.23        
8                      CON$       16-JUL-10 12.17.23        
7                      I_USER1    16-JUL-10 12.17.23        
6                      ICOL$      16-JUL-10 12.17.23     

Oralce 序列是线程安全的:http: //download.oracle.com/docs/cd/B19306_01/server.102/b14231/views.htm#ADMIN020 “如果两个用户同时访问相同的序列,那么每个用户的序列号用户接收到的可能有间隙,因为序列号也是由其他用户生成的。” 数字可能不是 1,2,3,4,5(如我的示例中 --> 如果你担心这个你可以缓存)

这也有帮助,尽管他们没有找到他们的来源: http: //forums.oracle.com/forums/thread.jspa?threadID=910428 “无论您提交还是回滚事务,序列都会立即且永久地递增。对序列的 NextVal 的并发访问将始终向每个调用者返回单独的值。”

如果您担心插入会乱序并且您需要序列值,请使用返回子句:

declare 
x number ;
begin 
insert into ORDERTEST (ORDERID, COLA, INSERTDATE)
                values( ORDERTEST_SEQ.NEXTVAL , 'abcd', sysdate)
                returning orderid into x;

dbms_output.put_line(x);
end;

--11

然后你就知道它是在当时和那里插入的。

于 2010-07-16T16:26:15.067 回答
0

您应该 (a) 将时间戳添加到每条记录,并且 (b) 将序列 NEXTVAL 移动到 INSERT 语句。

这样,当您查询表时,您可以ORDER BY timestamp, id,这实际上将是实际插入行的顺序。

于 2010-07-19T06:01:09.520 回答
0

有几种影响正在发生。今天的计算机每秒可以执行如此多的操作,以至于计时器无法跟上。此外,获取当前时间是一项有点昂贵的操作,因此您有可能持续几毫秒的间隙,直到值发生变化。这就是为什么你sysdate对不同的行得到相同的结果。

现在来解决您的插入问题。保证调用nextval序列会从序列中删除该值。如果两个线程nextval多次调用,您可以获得交错的数字(即线程 1 将看到 1 3 4 7,线程 2 将看到 2 5 6 8)但您可以确定每个线程将获得不同的数字。

因此,即使您不nextval立即使用结果,您也应该是安全的。至于数据库中的“绝对”插入顺序,这可能很难说。例如,数据库可以在将行写入磁盘之前将它们保存在缓存中。这些行可以重新排序以优化磁盘访问。但是,只要您nextval按照插入它们的顺序将结果分配给您的行,这无关紧要,它们应该总是按顺序插入。

于 2010-07-16T15:22:26.620 回答
0

虽然在数据库中可能有一些插入顺序的概念,但肯定没有检索顺序的概念。从数据库返回的任何行都将以数据库认为适合返回它们的任何顺序返回,这可能与它们插入数据库的顺序有任何关系,也可能没有任何关系。此外,将行插入数据库的顺序可能与它们在磁盘上的物理存储方式几乎没有关系。

在不使用 ORDER BY 子句的情况下依赖来自数据库查询的任何订单是愚蠢的。如果您希望确定任何顺序,则需要在创建插入记录时在逻辑中的正式级别(序列、时间戳等)维护该关系。

于 2010-07-16T15:25:24.853 回答
0

鉴于您对您要解决的问题的描述,我认为这些序列会很好。如果您有两个进程调用相同的存储过程,而第二个(按时间顺序)由于某种原因首先完成,那真的相关吗?我认为调用过程的顺序(这将反映在序列中(除非您使用 RAC))将比它们写入数据库的顺序更有意义。

如果您真的担心插入行的顺序,那么您需要查看何时发出提交,而不是何时发出插入语句。否则,您可能会出现以下情况:

  1. 事务 1 开始
  2. 事务 2 开始
  3. 事务 3 开始
  4. 事务 2 插入
  5. 事务 1 插入
  6. 事务 3 插入
  7. 事务 3 提交
  8. 事务 1 提交
  9. 事务 2 提交

在这种情况下,首先启动事务 1,首先插入事务 2,然后首先提交事务 3。序列号可以让您很好地了解事务开始的顺序。时间戳字段将让您了解何时发出插入。获得提交顺序的唯一可靠方法是将写入序列化到表中,这通常是一个坏主意(它消除了可伸缩性)。

于 2010-07-16T16:32:43.633 回答
0

如果事务是分开的,您可以从表的 ora_rowscn 伪列中确定这一点。

[编辑]更多细节,如果这没有用,我将删除我的答案 - 除非您使用非默认的“rowdependencies”子句创建表,否则您将拥有块中的其他行标记为 scn,所以这可能会产生误导。如果您真的想要此信息而不更改应用程序,则必须使用此子句重建表。

于 2010-07-16T15:35:09.137 回答