昌平电脑培训分享在Java程序中处理数据库超时与死锁

昌平电脑培训分享在Java程序中处理数据库超时与死锁,第1张

每个使用关系型数据库的程序都可能遇到数据死锁或不可用的情况,而这些情况需要在代码中编程来解决;本文主要介绍与数据库事务死锁等情况相关的重试逻辑概念,此外,还会探讨如何避免死锁等问题,文章以DB2(版本9)与为例进行讲解。

什么是数据库锁定与死锁

锁定(Locking)发生在当一个事务获得对某一资源的“锁”时,这时,其他的事务就不能更改这个资源了,这种机制的存在是为了保证数据一致性;在设计与数据库交互的程序时,必须处理锁与资源不可用的情况。锁定是个比较复杂的概念,仔细说起来可能又需要一大篇,所以在本文中,只把锁定看作是一个临时事件,这意味着如果一个资源被锁定,它总会在以后某个时间被释放。而死锁发生在当多个进程访问同一数据库时,其中每个进程拥有的锁都是其他进程所需的,由此造成每个进程都无法继续下去。

如何避免锁

我们可利用事务型数据库中的隔离级别机制来避免锁的创建,正确地使用隔离级别可使程序处理更多的并发事件(如允许多个用户访问数据),还能预防像丢失修改(LostUpdate)、读“脏”数据(DirtyRead)、不可重复读(NonrepeatableRead)及“虚”(Phantom)等问题。

隔离级别问题现象

丢失修改读“脏”数据不可重复读“虚”

可重复读取NoNoNoNo

读取稳定性NoNoNoYes

光标稳定性NoNoYesYes

未提交的读NoYesYesYes

表1:DB2的隔离级别与其对应的问题现象

在只读模式中,就可以防止锁定发生,而不用那些未提交只读隔离级别的含糊语句。昌平电脑培训发现一条SQL语句当使用了下列命令之一时,就应该考虑只读模式了

private boolean isLocked() {

try {// if the lock was null, anthor program was locking the file

lock=new FileOutputStream(new File(lockPath))getChannel()tryLock();

} catch (Exception e) {

return false;

}

// do not close stream or file will be unlocked

return lock==null;

}

在 *** 作数据库的时候,有时候会由于 *** 作不当引起数据库表被锁定,这么我们经常不知所措,不知怎么给这些表解锁,在pl/sql Developer工具的的菜单“tools”里面的“sessions”可以查询现在存在的会话,但是我们很难找到那个会话被锁定了,想找到所以被锁的会话就更难了,下面这叫查询语句可以查询出所以被锁的会话。如下:

SELECT snusername, mSID,snSERIAL#, mTYPE,

DECODE (mlmode,

0, 'None',

1, 'Null',

2, 'Row Share',

3, 'Row Excl',

4, 'Share',

5, 'S/Row Excl',

6, 'Exclusive',

lmode, LTRIM (TO_CHAR (lmode, '990'))

) lmode,

DECODE (mrequest,

0, 'None',

1, 'Null',

2, 'Row Share',

3, 'Row Excl',

4, 'Share',

5, 'S/Row Excl',

6, 'Exclusive',

request, LTRIM (TO_CHAR (mrequest, '990'))

) request,

mid1, mid2

FROM v$session sn, v$lock m

WHERE (snSID = mSID AND mrequest != 0) --存在锁请求,即被阻塞

OR ( snSID = mSID --不存在锁请求,但是锁定的对象被其他会话请求锁定

AND mrequest = 0

AND lmode != 4

AND (id1, id2) IN (

SELECT sid1, sid2

FROM v$lock s

WHERE request != 0 AND sid1 = mid1

AND sid2 = mid2)

)

ORDER BY id1, id2, mrequest;

通过以上查询知道了sid和 SERIAL#就可以开杀了

alter system kill session 'sid,SERIAL#';

我介绍一下Redis分布式锁吧:

一、定义redis实现分布式锁的接口

[java] view plain copy print

package comiolcommonutilconcurrentlocks;  

  

import javaioSerializable;  

  

  Description: 定义redis实现分布式锁的算法<br /> 

  This program is protected by copyright IOL_SMALL_TAIL<br /> 

  Program Name: IOL_SMALL_TAIL<br /> 

  Date: 2015年11月8日 

  

  @author 王鑫 

  @version 10 

 /  

public interface IRedisLockArithmetic extends Serializable {  

    / 

      加锁算法<br /> 

      @param key 

      @return 

     /  

    public boolean lock(String key);  

      

    / 

      解锁算法<br /> 

      @param key 

      @return 

     /  

    public boolean unLock(String key);  

}

二、redis分布式锁基础算法实现

[java] view plain copy print

package comiolcommonutilconcurrentlocksarithmetic;  

  

import orgslf4jLogger;  

import orgslf4jLoggerFactory;  

  

import comiolcommonutilconcurrentlocksIRedisComponent;  

import comiolcommonutilconcurrentlocksIRedisLockArithmetic;  

  

  Description: redis分布式锁基础算法实现<br /> 

  This program is protected by copyright IOL_SMALL_TAIL<br /> 

  Program Name: IOL_SMALL_TAIL<br /> 

  Date: 2015年11月9日 

  

  @author 王鑫 

  @version 10 

 /  

public class RedisLockBaseArithmetic implements IRedisLockArithmetic {  

    / 

     serialVersionUID 

     /  

    private static final long serialVersionUID = -8333946071502606883L;  

      

    private Logger logger = LoggerFactorygetLogger(RedisLockBaseArithmeticclass);  

  

    / 

      redis *** 作方法 

     /  

    private IRedisComponent redisComp;  

      

    / 

      超时时间,以毫秒为单位<br /> 

      默认为5分钟 

     /  

    private long overtime = 5  60  1000L;  

      

    / 

      休眠时长,以毫秒为单位<br /> 

      默认为100毫秒 

     /  

    private long sleeptime = 100L;  

      

    / 

      当前时间 

     /  

    private long currentLockTime;  

      

    / 

      @param redisComp the redisComp to set 

     /  

    public void setRedisComp(IRedisComponent redisComp) {  

        thisredisComp = redisComp;  

    }  

  

    / 

      @param overtime the overtime to set 

     /  

    public void setOvertime(long overtime) {  

        thisovertime = overtime;  

    }  

  

    / 

      @param sleeptime the sleeptime to set 

     /  

    public void setSleeptime(long sleeptime) {  

        thissleeptime = sleeptime;  

    }  

  

    / (non-Javadoc) 

      @see comiolcommonutilconcurrentlocksIRedisLockArithmetic#lock(javalangString, javalangLong) 

     /  

    @Override  

    public boolean lock(String key) {  

        while(true) {  

            // 当前加锁时间  

            currentLockTime = SystemcurrentTimeMillis();  

              

            if(redisCompsetIfAbsent(key, currentLockTime)) {  

                // 获取锁成功  

                loggerdebug("直接获取锁{key: {}, currentLockTime: {}}", key, currentLockTime);  

                return true;  

            } else {  

                //其他线程占用了锁  

                loggerdebug("检测到锁被占用{key: {}, currentLockTime: {}}", key, currentLockTime);  

                Long otherLockTime = redisCompget(key);  

                if(otherLockTime == null) {  

                    // 其他系统释放了锁  

                    // 立刻重新尝试加锁  

                    loggerdebug("检测到锁被释放{key: {}, currentLockTime: {}}", key, currentLockTime);  

                    continue;  

                } else {  

                    if(currentLockTime - otherLockTime >= overtime) {  

                        //锁超时  

                        //尝试更新锁  

                        loggerdebug("检测到锁超时{key: {}, currentLockTime: {}, otherLockTime: {}}", key, currentLockTime, otherLockTime);  

                        Long otherLockTime2 = redisCompgetAndSet(key, currentLockTime);  

                        if(otherLockTime2 == null || otherLockTimeequals(otherLockTime2)) {  

                            loggerdebug("获取到超时锁{key: {}, currentLockTime: {}, otherLockTime: {}, otherLockTime2: {}}", key, currentLockTime, otherLockTime, otherLockTime2);  

                            return true;  

                        } else {  

                            sleep();  

                            //重新尝试加锁  

                            loggerdebug("重新尝试加锁{key: {}, currentLockTime: {}}", key, currentLockTime);  

                            continue;  

                        }  

                    } else {  

                        //锁未超时  

                        sleep();  

                        //重新尝试加锁  

                        loggerdebug("重新尝试加锁{key: {}, currentLockTime: {}}", key, currentLockTime);  

                        continue;  

                    }  

                }  

            }  

        }  

    }  

  

    / (non-Javadoc) 

      @see comiolcommonutilconcurrentlocksIRedisLockArithmetic#unLock(javalangString) 

     /  

    @Override  

    public boolean unLock(String key) {  

        loggerdebug("解锁{key: {}}", key);  

        redisCompdelete(key);  

        return true;  

    }  

      

    / 

      休眠<br /> 

      @param sleeptime 

     /  

    private void sleep() {  

        try {  

            Threadsleep(sleeptime);  

        } catch (InterruptedException e) {  

            throw new LockException("线程异常中断", e);  

        }  

    }  

}

对象是一个锁标志。按照先到先得的原则,如果有多个线程都会执行代码,并使用同一个对象作为锁,

synchronize(对象){ }

那么,先执行这段代码的那个线程,将会获得这个对象锁,而当这个线程执行这段代码的时候,其他线程也是使用这个对象作为锁的,就不能执行这段代码,知道最初得到这个锁的线程运行完这段代码,然后再把锁分配给下一个线程执行。

每个使用关系型数据库的程序都可能遇到数据死锁或不可用的情况,而这些情况需要在代码中编程来解决;本文主要介绍与数据库事务死锁等情况相关的重试逻辑概念,此外,还会探讨如何避免死锁等问题,文章以DB2(版本9)与为例进行讲解。

什么是数据库锁定与死锁锁定(Locking)发生在当一个事务获得对某一资源的“锁”时,这时,其他的事务就不能更改这个资源了,这种机制的存在是为了保证数据一致性;在设计与数据库交互的程序时,必须处理锁与资源不可用的情况。

锁定是个比较复杂的概念,仔细说起来可能又需要一大篇,所以在本文中,只把锁定看作是一个临时事件,这意味着如果一个资源被锁定,它总会在以后某个时间被释放。

而死锁发生在当多个进程访问同一数据库时,其中每个进程拥有的锁都是其他进程所需的,由此造成每个进程都无法继续下去。

如何避免锁我们可利用事务型数据库中的隔离级别机制来避免锁的创建,正确地使用隔离级别可使程序处理更多的并发事件(如允许多个用户访问数据),还能预防像丢失修改(LostUpdate)、读“脏”数据(DirtyRead)、不可重复读(NonrepeatableRead)及“虚”(Phantom)等问题。

隔离级别问题现象丢失修改读“脏”数据不可重复读“虚”可重复读取NoNoNoNo读取稳定性NoNoNoYes光标稳定性NoNoYesYes未提交的读NoYesYesYes表1:DB2的隔离级别与其对应的问题现象在只读模式中,就可以防止锁定发生,而不用那些未提交只读隔离级别的含糊语句。

南京电脑培训>

对于账户锁定有很多方法,最简单就是把逻辑放在程序端控制。

新增一张登陆记录表(login_record),每次登陆做一次记录,不管是否登陆成功

在此表中可以设置一个字段来标识是否登陆成功

所以每次登陆是否通过的条件就有两个:

1登陆账号和密码要正确

2login_record中本日是否存在三条以上登陆失败记录

还有其他办法,可以利用spring的事务调度或sqlserver存储过程都可以解决这个问题。

以上就是关于昌平电脑培训分享在Java程序中处理数据库超时与死锁全部的内容,包括:昌平电脑培训分享在Java程序中处理数据库超时与死锁、java XML文件加锁并实现读写 *** 作、如何将数据库中被锁表解锁等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/sjk/9548587.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-04-29
下一篇 2023-04-29

发表评论

登录后才能评论

评论列表(0条)

保存