• Oracle中序列(Sequence)详解


    一 序列定义

    序列(SEQUENCE)是序列号生成器,可以为表中的行自动生成序列号,产生一组等间隔的数值(类型为数字)。不占用磁盘空间,占用内存。

    其主要用途是生成表的主键值,可以在插入语句中引用,也可以通过查询检查当前值,或使序列增至下一个值。

    二 创建序列

    创建序列需要CREATE SEQUENCE系统权限。序列的创建语法如下:

      CREATE SEQUENCE 序列名

      [INCREMENT BY n]

      [START WITH n]

      [{MAXVALUE/ MINVALUE n| NOMAXVALUE}]

      [{CYCLE|NOCYCLE}]

      [{CACHE n| NOCACHE}];

        其中:

    1)  INCREMENT BY用于定义序列的步长,如果省略,则默认为1,如果出现负值,则代表Oracle序列的值是按照此步长递减的。

    2)  START WITH 定义序列的初始值(即产生的第一个值),默认为1。

    3)  MAXVALUE 定义序列生成器能产生的最大值。选项NOMAXVALUE是默认选项,代表没有最大值定义,这时对于递增Oracle序列,系统能够产生的最大值是10的27次方;对于递减序列,最大值是-1。

    4)  MINVALUE定义序列生成器能产生的最小值。选项NOMAXVALUE是默认选项,代表没有最小值定义,这时对于递减序列,系统能够产生的最小值是?10的26次方;对于递增序列,最小值是1。

    5)  CYCLE和NOCYCLE 表示当序列生成器的值达到限制值后是否循环。CYCLE代表循环,NOCYCLE代表不循环。如果循环,则当递增序列达到最大值时,循环到最小值;对于递减序列达到最小值时,循环到最大值。如果不循环,达到限制值后,继续产生新值就会发生错误。

    6)  CACHE(缓冲)定义存放序列的内存块的大小,默认为20。NOCACHE表示不对序列进行内存缓冲。对序列进行内存缓冲,可以改善序列的性能。

    大量语句发生请求,申请序列时,为了避免序列在运用层实现序列而引起的性能瓶颈。Oracle序列允许将序列提前生成 cache x个先存入内存,在发生大量申请序列语句时,可直接到运行最快的内存中去得到序列。但cache个数也不能设置太大,因为在数据库重启时,会清空内存信息,预存在内存中的序列会丢失,当数据库再次启动后,序列从上次内存中最大的序列号+1 开始存入cache x个。这种情况也能会在数据库关闭时也会导致序号不连续。

    7)  NEXTVAL 返回序列中下一个有效的值,任何用户都可以引用。

    8)  CURRVAL 中存放序列的当前值,NEXTVAL 应在 CURRVAL 之前指定 ,二者应同时有效。

    例子:

    复制代码
    SQL> create sequence t1_seq increment by 1 start with 1;
    
    SYS@orclasm > drop sequence t1_seq;
    
    
    Sequence dropped.
    
    
    SYS@orclasm > create sequence t1_seq increment by -1 start with  -5;
    
    
    Sequence created.
    
    
    SYS@orclasm > select t1_seq.currval,t1_seq.nextval from dual;
    
    
       CURRVAL    NEXTVAL
    ---------- ----------
            -5         -5
    
    
    SYS@orclasm > select t1_seq.currval,t1_seq.nextval from dual;
    
    
       CURRVAL    NEXTVAL
    ---------- ----------
            -6         -6
    
    
    SYS@orclasm > select t1_seq.currval,t1_seq.nextval from dual;
    
    
       CURRVAL    NEXTVAL
    ---------- ----------
            -7         -7
    复制代码

    三 使用序列

    调用NEXTVAL将生成序列中的下一个序列号,调用时要指出序列名,即用以下方式调用: 序

    列名.NEXTVAL

    CURRVAL用于产生序列的当前值,无论调用多少次都不会产生序列的下一个值。如果序列还

    没有通过调用NEXTVAL产生过序列的下一个值,先引用CURRVAL没有意义。调用CURRVAL

    的方法同上,要指出序列名,即用以下方式调用:序列名.CURRVAL

    复制代码
    SQL> create table t1(id number,qq number,ww number);
    
    SQL> insert into t1 values(t1_seq.nextval,1,1);
    
    1 row created.
    
    SQL> /
    
    1 row created.
    
    SQL> /
    
     
    
    SQL> /
    
    SQL> /
    
    1 row created.
    
     
    
    SQL> select * from t1;
    
     
    
            ID         QQ         WW
    
    ----------     ----------     ----------
    
             1          1          1
    
             2          1          1
    
             3          1          1
    
             4          1          1
    
             5          1          1
    
     
    
    SQL> select t1_seq.currval from dual;
    
     
    
       CURRVAL
    
    ----------
    
             5
    
     
    
    SQL> select t1_seq.nextval from dual;
    
     
    
       NEXTVAL
    
    ----------
    
             6
    
     
    
    SQL> select t1_seq.nextval from dual;
    
     
    
       NEXTVAL
    
    ----------
    
             7
    复制代码

    四 修改序列

    修改序列的注意事项:

    1  必须是序列的拥有者或对序列有 ALTER any sequence权限

    2  只有将来的序列值会被改变

    3  改变序列的初始值只能通过删除序列之后重建序列的方法实现

    Alter sequence示例:

    ALTER SEQUENCE emp_sequence INCREMENT BY 10 MAXVALUE 10000 CYCLE -- 到10000后从头开始 NOCACHE ;

     可以影响Sequence的初始化参数:

     SEQUENCE_CACHE_ENTRIES =设置能同时被cache的sequence数目。

    复制代码
    SQL> alter sequence t1_seq increment by 2 maxvalue 10 cycle;
    
    alter sequence t1_seq increment by 2 maxvalue 10 cycle
    
    *
    
    ERROR at line 1:
    
    ORA-04013: number to CACHE must be less than one cycle
    
     
    
    SQL> alter sequence t1_seq increment by 2 maxvalue 40 cycle;
    
    Sequence altered.
    
     
    
    SQL> insert into t1 values(t1_seq.nextval,1,1);
    
    /
    
    /
    
    ……….
    
    /
    
    SQL> select * from t1;
    
            ID         QQ         WW
    
    ----------     ----------     ----------
    
             1          1          1
    
             2          1          1
    
             3          1          1
    
             4          1          1
    
             5          1          1
    
             9          1          1
    
            11          1          1
    
            13          1          1
    
            15          1          1
    
            17          1          1
    
           ………………………………………………..
    
            37          1          1
    
            39          1          1
    
             1          1          1
    
             3          1          1
    
             5          1          1
    
     
    
    24 rows selected.
    复制代码

    五 查询序列

    1  通过数据字典USER_OBJECTS可以查看用户拥有的序列。

    2  通过数据字典USER_SEQUENCES可以查看序列的设置。

      例:查看用户的序列:

    SELECT SEQUENCE_NAME,MIN_VALUE,MAX_VALUE,INCREMENT_BY,LAST_NUMBER FROM USER_SEQUENCES;

      执行结果:

      

    SEQUENCE_NAME MIN_VALUE MAX_VALUE INCREMENT_BY LAST_NUMBER
    
     
    复制代码
    SQL> select object_name,object_type from user_objects;
    
     
    
    OBJECT_NAM   OBJECT_TYPE
    
    ----------        -------------------
    
    T1_SEQ        SEQUENCE
    
    T1            TABLE
    
     
    
    SQL> select sequence_name,increment_by,cycle_flag,cache_size from user_sequences;
    
    SEQUENCE_N  INCREMENT_BY  CYCLE_FLAG   CACHE_SIZE
    
    ----------         ------------       ----------       ----------
    
    T1_SEQ            2             Y            20
    复制代码

    六 删除序列

    SQL> drop sequence t1_seq;



    序列作为主键使用的原理、优缺点讨论 

    这几天和同事一直在讨论关于表设计中主键选择的问题,用sequence作为主键究竟有什么好处,又有什么缺点,尤其是有些事务场景上下文需要用到创建的序列值,如何用?其实我想说的是,可能只是一个很简单的概念,可能深入理解,还是有很多未知的知识,当然也就可能会有一些容易忽略但又可能很关键的坑,只有碰了才知道。。。
    以下是总结摘要,如有疏漏,还请过路的各位大侠赐教。

     
    1、首先说下seq.nextval主要有以下两种使用场景:
    (1). 如果一个事务中只是INSERT时需要序列,其他地方不会需要这个序列,那么只需要在INSERT ... VALUES (seq.nextval ...)语句中使用即可。
    (2). 如果一个事务中INSERT一张表后,还需要插入时的主键ID值,作为外键插入其他表,那么就需要在INSERT第一张表前使用select seq.nextval from dual提前获取可用的ID保存到一个变量中,为后面使用。

    2、其次可以简单说下调用序列的原理,只有理解了序列的原理,才能有助于我们知道如何正确使用序列。
    使用序列时Oracle内部大体是按照如下步骤进行:
    (1). 一个序列会被定义到Oracle内部的一张数据字典表(seq$)的一行。
    (2). 第一次使用序列,序列的起始值会加上缓存大小,然后更新回行。
    (3). Oracle内部会自动跟踪内存中的两个值,当前值和目标值。
    (4). 每次有回话调用seq.nextval,Oracle会递增当前值,然后检查是否超过了目标值,再返回结果。
    (5). 如果当前值和目标值相同,Oracle会更新数据字典表中的行,为目标值加上缓存大小,同时内存中产生了一个新的目标值。
    例如create sequence seq cache 20;这样一序列。名称为seq的序列,缓存大小是20,默认初始值是1,步长默认是1。

    当使用了一次seq.nextval后,可以看HIGHWATER字段值为21,即目标值1+缓存大小20=21。

    当执行20次后,seq.nextval值变为21,此时HIGHWATER字段值是41,即目标值21+缓存大小20=41。

    也就是每调用seq.nextval值20次,会更新一次seq$表,那么问题来了,如果cache值较小,且序列使用的频率较高,那么会对seq$表有频繁的更新操作,日志量会增加,尤其在RAC下,更新该行的时候,该数据块会在节点间不停的传送,就会产生可能的争用,这种问题会被放大。因此为了减少这种情况,我们可以将cache缓存值设置大一些,例如1000,减少对字典表的更新。
    序列还有一个问题,就是cache缓存是实例级的,对于RAC,比如第一个节点使用序列时会分配1-20,第二个节点会被分配21-40,Oracle保证不会重复,但若节点crash了,比如节点1坏了,那么序列就会出现断号,节点1再次使用时,只会从41-60,由于我们用主键只为了标示唯一,不关心段号,也不关心产生的顺序,所以这些可以忽略。
     
    :最近在讨论某系统和一个外系统做全局事务的事情,本想用这个主键作为两系统传输的一部分,用于控制全局事务,且用其作为判断交易先后顺序的依据,这是不太符合要求的,因为是RAC,序列是基于实例级cache,那么如果不能保证某一类型的交易总在一个节点上执行,那么不同次交易产生的主键序列值可能不是递增的,例如节点1处理一次交易,产生序列是1,节点2处理一次交易,产生序列是21,此时节点1再处理一次交易,产生序列是2;除非设置序列为order,但这样在RAC就有可能产生资源争用的问题,因为为了保证多节点间每次产生的序列值是递增的,每次产生就需要多节点间判断当前值后,才能知道下一个值是多少,而且会有额外的锁,保证同一时间只有一个节点在做这个操作,当然究竟是否会产生资源争用,还是要依据实际的业务并发量,或者压力测试才能证明,这里只是说可能会这样的问题,不是一定会,否则Oracle就不会有提供这种order的创建属性,凡事不绝对。

    3、结合(1)的场景,
    (1). 如果一个事务中只是INSERT时需要序列,其他地方不会需要这个序列,那么只需要在INSERT ... VALUES (seq.nextval ...)语句中使用即可。
    > 这个场景下,如果序列cache设置为1000,调用100次nextval几乎没有影响。
    (2). 如果一个事务中INSERT一张表后,还需要插入时的主键ID值,作为外键插入其他表,那么就需要在INSERT第一张表前使用select seq.nextval from dual提前获取可用的ID保存到一个变量中,为后面使用。
    > 这个场景,就有些说的了。如果是同一事务中需要用到之前的序列值,那么就需要提前用select seq.nextval from dual保存到变量中,(当然,如果是用PLSQL语句,则可以不用提前保存变量这步,但咱们的应用中不适用,就不多说了)。
  • 相关阅读:
    Entity Framework Code First属性映射约定 转载https://www.cnblogs.com/libingql/p/3352058.html
    EntityFrame Work 6 Code First 配置字段为varchar 类型
    【配置属性】—Entity Framework实例详解
    Repository模式--采用EF Fluent API使用EntityTypeConfiguration分文件配置Model映射关系
    JS中$含义及用法
    mvc中@RenderSection()研究 转载https://www.cnblogs.com/rrxc/p/4062827.html
    ASP.Net MVC开发基础学习笔记:三、Razor视图引擎、控制器与路由机制学习
    Windows API中的坑
    Machine Learning—The k-means clustering algorithm
    android BaseAdapter getView 理解
  • 原文地址:https://www.cnblogs.com/JAYIT/p/12532909.html
Copyright © 2020-2023  润新知