服务器之家:专注于服务器技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|

服务器之家 - 编程语言 - JAVA教程 - 使用JAVA实现高并发无锁数据库操作步骤分享

使用JAVA实现高并发无锁数据库操作步骤分享

2019-10-21 12:05java教程网 JAVA教程

一个在线2k的游戏,每秒钟并发都吓死人。传统的hibernate直接插库基本上是不可行的。我就一步步推导出一个无锁的数据库操作,详情看下文

1. 并发中如何无锁。
一个很简单的思路,把并发转化成为单线程。Java的Disruptor就是一个很好的例子。如果用java的concurrentCollection类去做,原理就是启动一个线程,跑一个Queue,并发的时候,任务压入Queue,线程轮训读取这个Queue,然后一个个顺序执行。 
在这个设计模式下,任何并发都会变成了单线程操作,而且速度非常快。现在的node.js, 或者比较普通的ARPG服务端都是这个设计,“大循环”架构。
这样,我们原来的系统就有了2个环境:并发环境 + ”大循环“环境
并发环境就是我们传统的有锁环境,性能低下。
"大循环"环境是我们使用Disruptor开辟出来的单线程无锁环境,性能强大。

2. ”大循环“环境 中如何提升处理性能。
一旦并发转成单线程,那么其中一个线程一旦出现性能问题,必然整个处理都会放慢。所以在单线程中的任何操作绝对不能涉及到IO处理。那数据库操作怎么办?
增加缓存。这个思路很简单,直接从内存读取,必然会快。至于写、更新操作,采用类似的思路,把操作提交给一个Queue,然后单独跑一个Thread去一个个获取插库。这样保证了“大循环”中不涉及到IO操作。

问题再次出现:
如果我们的游戏只有个大循环还容易解决,因为里面提供了完美的同步无锁。
但是实际上的游戏环境是并发和“大循环”并存的,即上文的2种环境。那么无论我们怎么设计,必然会发现在缓存这块上要出现锁。

3. 并发与“大循环”如何共处,消除锁?
我们知道如果在“大循环”中要避免锁操作,那么就用“异步”,把操作交给线程处理。结合这2个特点,我稍微改下数据库架构。
原本的缓存层,必然会存在着锁,例如:

 

复制代码代码如下:

public TableCache
{
  private HashMap<String, Object> caches = new ConcurrentHashMap<String, Object>();
}

 

这个结构是必然的了,保证了在并发的环境下能够准确的操作缓存。但是”大循环“却不能直接操作这个缓存进行修改,所以必须启动一个线程去更新缓存,例如:

 

复制代码代码如下:


private static final ExecutorService EXECUTOR = Executors.newSingleThreadExecutor();
EXECUTOR.execute(new LatencyProcessor(logs));

 

class LatencyProcessor implements Runnable
{
  public void run()
  { 
    // 这里可以任意的去修改内存数据。采用了异步。
  }
}

 

OK,看起来很漂亮。但是又有个问题出现了。在高速存取的过程中,非常有可能缓存还没有被更新,就被其他请求再次获取,得到了旧的数据。

4. 如何保证并发环境下缓存数据的唯一正确?
我们知道,如果只有读操作,没有写操作,那么这个行为是不需要加锁的。
我使用这个技巧,在缓存的上层,再加一层缓存,成为”一级缓存“,原来的就自然成为”二级缓存“。有点像CPU了对不?
一级缓存只能被”大循环“修改,但是可以被并发、”大循环“同时获取,所以是不需要锁的。
当发生数据库变动,分2种情况:
1)并发环境下的数据库变动,我们是允许有锁的存在,所以直接操作二级缓存,没有问题。
2)”大循环“环境下数据库变动,首先我们把变动数据存储在一级缓存,然后交给异步修正二级缓存,修正后删除一级缓存。
这样,无论在哪个环境下读取数据,首先判断一级缓存,没有再判断二级缓存。
这个架构就保证了内存数据的绝对准确。
而且重要的是:我们有了一个高效的无锁空间,去实现我们任意的业务逻辑。

最后,还有一些小技巧提升性能。
1. 既然我们的数据库操作已经被异步处理,那么某个时间,需要插库的数据可能很多,通过对表、主键、操作类型的排序,我们可以删除一些无效操作。例如:
a)同一个表同一个主键的多次UPdate,取最后一次。
b)同一个表同一个主键,只要出现Delete,前面所有操作无效。
2. 既然我们要对操作排序,必然会存在一个根据时间排序,如何保证无锁呢?使用
private final static AtomicLong _seq = new AtomicLong(0);
即可保证无锁又全局唯一自增,作为时间序列。

延伸 · 阅读

精彩推荐
  • JAVA教程java动态代理(jdk与cglib)详细解析

    java动态代理(jdk与cglib)详细解析

    静态代理:由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了 ...

    java之家4792019-10-14
  • JAVA教程在spring-boot工程中添加spring mvc拦截器

    在spring-boot工程中添加spring mvc拦截器

    这篇文章主要介绍了在spring-boot工程中添加spring mvc拦截器,Spring MVC的拦截器(Interceptor)不是Filter,同样可以实现请求的预处理、后处理。,需要的朋友可以...

    郭寻抚4592019-06-26
  • JAVA教程Java同步容器和并发容器详解

    Java同步容器和并发容器详解

    这篇文章主要介绍了Java同步容器和并发容器详解,容器是Java基础类库中使用频率最高的一部分,Java集合包中提供了大量的容器类来帮组我们简化开发,下面...

    一入码坑深似海1792019-06-28
  • JAVA教程JavaI/O深入学习之输入和输出

    JavaI/O深入学习之输入和输出

    这篇文章主要介绍了JavaI/O深入学习之输入和输出,Java类库中的I/O类分成输入和输出两部分,可以在JDK文档里的类层次结构中查看到。,需要的朋友可以参考...

    木瓜芒果4292019-07-07
  • JAVA教程Java掩码的几种使用例举

    Java掩码的几种使用例举

    今天小编就为大家分享一篇关于Java掩码的使用,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧...

    Alan_阿兰1962019-06-23
  • JAVA教程elasticsearch启动警告无法锁定JVM内存

    elasticsearch启动警告无法锁定JVM内存

    今天小编就为大家分享一篇关于elasticsearch启动警告无法锁定JVM内存,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随...

    Scub5662019-06-21
  • JAVA教程基于Ok+Rxjava实现断点续传下载

    基于Ok+Rxjava实现断点续传下载

    这篇文章主要为大家详细介绍了基于Ok+Rxjava实现断点续传下载,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    Super_Ks3852019-06-25
  • JAVA教程501 Command "HELO" requires an argument问题的解决方法

    501 Command "HELO" requires an argument问题的解决方法

    换一个windows服务器,发现就没这样的问题,仅在一台Linux服务器上可以重现,直观感觉就是这台Linux服务器某些配置有问题 ...

    java教程网3312019-10-11