`
nepenthe
  • 浏览: 21809 次
  • 性别: Icon_minigender_1
  • 来自: 西安
社区版块
存档分类
最新评论

多台服务器共享一个数据库(oracle数据库),防止同时访问一批数据

阅读更多

 

在处理数据时,经常会用定时任务间隔去跑一批数据。如果一台服务器去查询执行倒也没事,如果出于负载均衡考虑,多台服务器同时去跑这个数据库的一张表的数据时,就很有可能会取到相同数据,出现执行数据重复的问题。

 

贴出我的做法。

 

--创建表定时任务取模求余表记录表
create table T_TASKMODREMAINDER
(
 TASKNAME  varchar2(100) ,
 MODREMAINDER number(3) ,
 UPDATESEQ number(20) default 0,
 TASKIP  VARCHAR2(32),
 UPDATETIME date
)
/

-- Add comments to the columns 
comment on column T_TASKMODREMAINDER.TASKNAME
  is '定时任务类名称'
/
comment on column T_TASKMODREMAINDER.MODREMAINDER
  is '定时任务执行时使用取模余数。必须从0开始,按1递增并保持连续。备注:每台机器执行定时任务时,首先要获取updatetime时间最早的该余数并对该记录上锁(如果获取失败则获取下updatetime次早的余数,直如果所有余数都获取失败,定时任务返回),然后使用该余数从数据库获取数据,获取完毕提交或失败回滚时自动释放记录锁。'
/
comment on column T_TASKMODREMAINDER.UPDATESEQ
  is '按updateseq从小到大的顺序获取取模余数。获取余数上锁后,先更新此字段,然后再获取业务数据。'
/
comment on column T_TASKMODREMAINDER.TASKIP
  is '当前使用该余数的机器IP,如:10.10.10.10。备注:1.并不是只有该IP才可以使用该余数,而是谁获取到该余数则在此登记。2.IP在使用余数时自动更新,初始化脚本不需要填写'
/
comment on column T_TASKMODREMAINDER.UPDATETIME
  is '使用该余数的机器最近执行任务的时间。'
/
---初始化表插入数据,有三台服务器,则插入三条数据,MODREMAINDER从0开始







insert into T_TASKMODREMAINDER (TASKNAME, MODREMAINDER, UPDATESEQ, TASKIP, UPDATETIME)
values ('BatchOperateTask', 0, 0, '', sysdate)
/
insert into T_TASKMODREMAINDER (TASKNAME, MODREMAINDER, UPDATESEQ, TASKIP, UPDATETIME)
values ('BatchOperateTask', 1, 0, '', sysdate)
/
insert into T_TASKMODREMAINDER (TASKNAME, MODREMAINDER, UPDATESEQ, TASKIP, UPDATETIME)
values ('BatchOperateTask', 2, 0, '', sysdate)
/
commit
/
--创建T_TASKMODREMAINDER 表的序列
create sequence seq_TASKMODREMAINDER start with 1 increment by 1
/

 

--创建取模函数
create or replace function f_func_gettaskmodremainder
(
    str_in_taskname    in     T_TASKMODREMAINDER.TASKNAME%type,     --定时任务名称
    i_o_modebase       out    number,                               --取模基数
    i_o_remainder      out    number                                --取模余数
) return integer
--返回值:
--  0:异常 
--  1:成功 
--  2:取模余数全部被其他机器锁定
--  3:取模余数记录和取模基数不匹配 
--  4:无定时任务配置数据

------=========        注意       ================
--1、本存储过程执行过后,则会锁定某条取模余数记录(行锁),
--   调用程序在退出之前请一定要提交或回滚事务时以释放该行锁。
--2、调用方存储过程内只要一提交或回滚则会立即释放该行锁,
--   所以,调用方获取数据的模式为:获取锁、更新数据、提交,不允许出现获取锁、更新数据、提交、再获取数据的情况
--3、取数据过程中必须更新数据状态,防止多个定时任务重复获取数据或因竞争数据引发死锁
--4、调用方在所有更新相同表数据的地方都必须采用取模方式更新数据。

is
    --自定义异常,用于标志因资源忙导致锁定记录不成功
    resource_busy    exception;

    --将锁定记录异常和名字resource_busy绑定
    pragma exception_init(resource_busy, -54);

    i_l_remaindercount   number;
    i_l_locksuccess      number := 0;
    dt_l_nowtime         date := sysdate;
    i_l_return           number;
    i_l_updateseq        number;

begin

    --获取取模基数(获取数据定时任务总共有几个定时任务)
    begin         
         select configvalue into i_o_modebase from t_config where configname = 'portal_task_count'; --可以写死总共定时任务,为了使用方便可配置
    exception
        when no_data_found then
            --无定时任务配置数据
            return 4;
    end;

    --检查取模余数记录是否正常
    select count(*)
      into i_l_remaindercount
      from t_taskmodremainder
     where taskname = str_in_taskname
       and modremainder >=0
       and modremainder < i_o_modebase;
       
    --如果取模余数记录和取模基数不匹配,异常退出
    if ( i_l_remaindercount <> i_o_modebase ) then
       return 3;   
    end if;
    
    --获取获取数据序列
    select SEQ_TASKMODREMAINDER.nextval
      into i_l_updateseq
      from dual;
      
    --对于序列用完又重头开始的情况,需要将大于当前序列的记录更新为1,每次仅处理一条
    begin
        select modremainder
          into i_o_remainder
          from t_taskmodremainder
         where taskname = str_in_taskname
           and updateseq > i_l_updateseq
           and rownum = 1
           for update nowait;
        
           --获取成功,更新
           update t_taskmodremainder
              set updateseq = 1
            where taskname = str_in_taskname
              and modremainder = i_o_remainder;
                
    exception
        --如果没有从头开始的情况,不处理
        when no_data_found then
            null;
        --锁定不成功,不处理
        when resource_busy then
            null;
    end;  
      
    --按updateseq从小到大的顺序获取取模余数记录并上锁
    for i in 1..i_o_modebase loop
        begin
          select modremainder
            into i_o_remainder
            from t_taskmodremainder
           where taskname = str_in_taskname
             and modremainder = ( select modremainder
                                      from ( select modremainder, rownum rn
                                               from ( select modremainder from t_taskmodremainder
                                                       where taskname = str_in_taskname
                                                         and modremainder < i_o_modebase
                                                       order by updateseq
                                                    )
                                           )
                                     where rn = i
                                  )
             for update nowait;

             --锁定记录成功,更新
             update t_taskmodremainder
                set updatetime = dt_l_nowtime,
                    taskip = sys_context ('userenv', 'ip_address'),
                    updateseq = i_l_updateseq
              where taskname = str_in_taskname
                and modremainder = i_o_remainder;
             
             --设置锁定成功标志,跳出循环
             i_l_locksuccess := 1;
             exit;

        exception
            --锁定不成功,继续锁定updatetime次旧的一个
            when resource_busy then
                null;
        end;

    end loop;

    --如果锁定取模余数记录不成功,返回2,成功返回1
    return case i_l_locksuccess
               when 1 then 1
               else 2
           end;

exception
    when others then
         return 0;
end f_func_gettaskmodremainder;
/

 

=========================================

 

 

--创建存储数据表
create table t_batchoperatedata
(
  id            number primary key,
  username      varchar2(20) ,
  userid        varchar2(20) ,
  insertTime   date default sysdate,
  status        number ,
  roleCode      varchar2(20) ,
  batchid       number default -1,
  operatertime date
)
/
-- Add comments to the table 
comment on table t_batchoperatedata
  is '存储数据表'
/
comment on column t_batchoperatedata.username
  is '用户名'
/
comment on column t_batchoperatedata.userid
  is '用户id'
/
comment on column t_batchoperatedata.insertTime
  is '入库时间'
/
comment on column t_batchoperatedata.status
  is '当前数据状态 1.未处理;2.已处理;3已获取'
/

comment on column t_batchoperatedata.roleCode
  is '角色编号'
/


--获取批量执行数据
create or replace function f_getoperatedata
(
str_taskname in T_TASKMODREMAINDER.taskname%type, --定时任务名称
i_o_result   out integer
)

 return pkg_type.refcur --定义返回类型
 is
  results pkg_type.refcur; --声明游标

  get_batch_execute_maxs varchar2(20); --最大获取条数

  i_o_modebase  number; --模基数
  i_o_remainder number; --模余数
  i_funcresult  integer; --函数结果输出参数
  temp_id number;
  t_batchid     number;

begin

      --获取模基数与本次抢占到的模基数
    i_funcresult := f_func_gettaskmodremainder(  str_taskname,
                                                 i_o_modebase,
                                                 i_o_remainder
                                              );

    --对返回码处理
    begin
        case i_funcresult
            when 1 then
                i_o_result := 1;
            when 2 then
                i_o_result := 1;
            when 3 then
                i_o_result := 2;
            when 4 then
                i_o_result := 3;
            else
                i_o_result := 0;
         end case;
    exception
        when others then
        i_o_result := 0;
    end;

       if i_funcresult != 1 then
        rollback;
        open results for select * from dual where 2=1;
        return results;
    end if;

  --获取每次最大处理条数
  select configvalue
    into get_batch_execute_maxs
    from t_config
   where configname =
         'get_batch_execute_maxs';

   select seq_batchid.nextval into  t_batchid from dual;

   update t_batchoperatedata b
      set batchid = t_batchid, status = 3,operatertime=sysdate
    where rownum <= get_batch_execute_maxs
      and b.status = 1
	order by b.insertTime;

  open results for

  --查询出要处理的没个号码
    select b.id,                   --id
           b.username,           --用户名
           b.userid,            --用户id
           b.insertTime,           --入库时间
           b.roleCode,        --操作者角色账号
      from t_batchoperatedata b
     where b.batchid=t_batchid;
  commit;
  return results;
exception
  when others then
    return results;
end f_getoperatedata;
/

 

在类中调用f_getoperatedata函数来获取要操作的数据,每次在函数f_getoperatedata中都会调用f_func_gettaskmodremainder函数获取模基数与本次抢占到的模基数,并且锁定t_taskmodremainder
表,防止其他服务器定时任务使用,确保每台服务器定时任务每次获取的数据不是同一批。

 

如果有某台服务器的定时任务获取到数据后,并且以修改表t_batchoperatedata中status = 3时,服务器濡染荡掉。那么可以再加一个定时任务来定时判断如果operatertime时间大于你认为超时的时间,则去修改t_batchoperatedata中status = 3的数据,全部修改状态为1,等待下次继续执行。

或者可直接在数据库中加个job任务来执行,认为数据库job较方便些,还省去写java代码。

分享到:
评论

相关推荐

    oracle数据库经典题目

    当多个用户同时连接同一个实例时,SGA区数据供多个用户共享,所以SGA区又称为共享全局区。用户对数据库的各种操作主要在SGA区中进行。该内存区随数据库实例的创建而分配,随实例的终止而释放。PGA区是在用户进程连接...

    oracle数据库dba管理手册

    11.1.2 安全与数据访问控制 321 11.1.3 事务管理 322 11.1.4 文件定位 322 11.1.5 监控 322 11.1.6 版本考虑 323 11.1.7 DBA的角色 324 11.2 管理Oracle Financials软件包的特殊 准则 324 11.2.1 数据库结构 324 ...

    oracle 优化培训资料

    Oracle 服务器包括一个 Oracle 例程和一个 Oracle 数据库。 数据库实例就是一组oracle的后台进程/线程和共享内存区域的组合 Oracle 例程:Oracle 例程是后台进程和内存结构的组合。只有启动例程后,才能访问数据库中...

    ORACLE9i_优化设计与系统调整

    第13章 数据访问方法 160 §13.1 使用索引的访问方法 161 §13.1.1 何时创建索引 161 §13.1.2 索引列和表达式的选择 161 §13.1.3 选择复合索引的主列 161 §13.1.4 要用可选择性索引 162 §13.1.5 测量索引的可...

    oracle体系结构

    数据库:物理文件的集合实例:一组oracle后台进程/线程以及服务器分配的共享内存区。...是的,Oracle数据库由一到多个表空间组成:一个表空间由一组段(segment)组成一个段由一组盘区(extent)组成一个盘区由一批

    JAVA上百实例源码以及开源项目源代码

     Tcp服务端与客户端的JAVA实例源代码,一个简单的Java TCP服务器端程序,别外还有一个客户端的程序,两者互相配合可以开发出超多的网络程序,这是最基础的部分。 递归遍历矩阵 1个目标文件,简单! 多人聊天室 3...

    JAVA上百实例源码以及开源项目

     Tcp服务端与客户端的JAVA实例源代码,一个简单的Java TCP服务器端程序,别外还有一个客户端的程序,两者互相配合可以开发出超多的网络程序,这是最基础的部分。 递归遍历矩阵 1个目标文件,简单! 多人聊天室 3...

    C#程序开发范例宝典(第2版).part08

    精选570个典型范例,全面覆盖实用和热点技术,涉及面广,实用性强源于实际项目开发,帮助读者短时间掌握更多实用技术,提高编程水平范例经过精心编排,重点、难点突出,易学易懂书后附录提供快速索引,即查、即学、...

    C#程序开发范例宝典(第2版).part13

    精选570个典型范例,全面覆盖实用和热点技术,涉及面广,实用性强源于实际项目开发,帮助读者短时间掌握更多实用技术,提高编程水平范例经过精心编排,重点、难点突出,易学易懂书后附录提供快速索引,即查、即学、...

    C#程序开发范例宝典(第2版).part02

    精选570个典型范例,全面覆盖实用和热点技术,涉及面广,实用性强源于实际项目开发,帮助读者短时间掌握更多实用技术,提高编程水平范例经过精心编排,重点、难点突出,易学易懂书后附录提供快速索引,即查、即学、...

    C#程序开发范例宝典(第2版).part12

    精选570个典型范例,全面覆盖实用和热点技术,涉及面广,实用性强源于实际项目开发,帮助读者短时间掌握更多实用技术,提高编程水平范例经过精心编排,重点、难点突出,易学易懂书后附录提供快速索引,即查、即学、...

Global site tag (gtag.js) - Google Analytics