`
nepenthe
  • 浏览: 22268 次
  • 性别: 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代码。

分享到:
评论

相关推荐

    java多个数据库实现数据同步

    在IT行业中,数据库同步是一个关键话题,特别是在分布式系统和企业级应用中,多个数据库的同步是确保数据一致性、完整性和高可用性的重要手段。本文将深入探讨如何使用Java来实现多个数据库之间的数据同步。 首先,...

    Oracle 数据库培训材料(1)

    1. **Oracle服务器体系结构**:Oracle服务器是一个数据库管理系统,它包含了一个Oracle实例和一个Oracle数据库。Oracle实例是访问数据库的途径,它始终打开并仅打开一个数据库,由内存结构(系统全局区SGA)和后台...

    Oracle数据库性能优化.pdf

    总的来说,Oracle数据库性能优化是一个系统性的工程,需要从多个角度综合考虑,不断调整和优化,以达到最佳运行状态。这需要数据库管理员具备深厚的理论知识,实践经验,以及对整个IT环境的深入理解。

    Oracle9i 数据库性能优化

    Oracle9i 是 Oracle 公司推出的一款功能强大的关系型数据库管理系统(RDBMS)。随着业务规模的增长和技术的发展,Oracle9i 的性能优化成为了提高系统响应速度、提升用户体验的关键环节。性能优化主要包括对数据库...

    oracle数据库经典题目

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

    Oracle照片导出

    总的来说,Oracle照片导出工具是一个实用的辅助工具,它结合了数据库查询和文件操作,使非技术用户也能轻松处理Oracle数据库中的图像数据。同时,通过开源的方式,它为开发者提供了学习和改进的机会,进一步促进了...

    Oracle公司内部数据库培训资料

    在Oracle中,数据存储在表空间内,表空间由一个或多个数据文件构成。表空间是逻辑存储单元,可以设置不同的存储参数以优化性能。数据文件是物理存储单元,用于保存数据库对象的实际数据。 五、备份与恢复 Oracle...

    oracle新手入门指导之八—数据库远程访问小知识.txt

    Database Link是一种在Oracle数据库之间建立连接的技术,允许用户从一个数据库直接访问另一个数据库中的数据。这对于需要从多个数据库中获取数据的应用场景非常有用。 - **创建Database Link**: ```sql CREATE ...

    oracle数据库实战

    以上是根据提供的文件摘要整理出的Oracle数据库实战中的核心知识点,涵盖了从数据库体系结构、安装配置、数据字典与动态性能视图到性能监控及调整等多个方面。希望这些详细的内容能帮助您更好地理解和掌握Oracle...

    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数据库实验操作知识点 #### 第一部分:SQL基础 ##### 基本查询语句 - **实验1:** 编写一个简单的SQL查询语句,用于查询表中的所有行和所有列。例如: ```sql SELECT * FROM 表名; ``` - **实验2:**...

    Oracle18c(18.3)数据库驱动ojdbc.jar

    Oracle18c是Oracle数据库的一个重要版本,其中18.3是这个版本的小型更新。这个版本引入了多项新特性,旨在提高性能、安全性和易用性。例如,自动索引优化(Automatic Indexing)是一项智能功能,能够自动创建、监控...

    局域网连接数据库程序

    数据库则是一个存储和管理数据的系统,例如MySQL、Oracle、SQL Server等。在局域网环境中,数据库服务器通常作为中央节点,处理来自多个客户端的查询和写入请求。客户端应用程序通过特定的数据库连接协议(如TCP/IP...

    Oracle数据库体系结构小结[定义].pdf

    客户端/服务器结构中,客户端负责提交数据库操作,而服务器端处理并发的共享数据访问。分布式处理结构是在一个计算机上拥有多个处理器,使得Oracle服务器和客户端可以分布在不同处理器上。多层结构进一步复杂化,...

    Oracle数据库10g-网格数据库.pptx

    Oracle数据库10g是Oracle公司推出的一个重要版本,其核心特色在于引入了网格计算的概念,旨在提高企业IT系统的效率和可扩展性。网格计算的基本理念是像使用公共设施一样使用计算资源,使得客户端可以轻松请求服务,...

    sql数据同步到oracle

    在IT行业中,数据库同步是...总之,实现“远程SQL数据库定时同步到Oracle数据库”涉及多个技术层面,需要根据具体业务需求和环境条件选择合适的方法,并进行详尽的测试和监控,以确保数据的准确无误和系统的稳定运行。

    oracle数据库g:-网格数据库完整版资料.ppt

    可传输的表空间功能进一步简化了数据的移动和共享,可以在多个数据库之间装载只读表空间,支持跨平台操作,极大地增强了数据管理的灵活性。 Oracle Grid计算的发展趋势与硬件和行业趋势相吻合,特别是在云计算和...

    Oracle服务器结构.pptx

    - **数据缓冲区(Data Buffer Cache)**:这部分内存用来缓存数据文件中的数据块,以提高数据访问速度。 - **日志缓冲区(Redo Log Buffer)**:用于临时存储用户对数据块的操作(如DDL、DML等),在满足一定条件后会...

Global site tag (gtag.js) - Google Analytics