如何在高并发环境下设计出无锁的数据库操作(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版本)的更多相关文章
- 【高并发】高并发环境下构建缓存服务需要注意哪些问题?我和阿里P9聊了很久!
写在前面 周末,跟阿里的一个朋友(去年晋升为P9了)聊了很久,聊的内容几乎全是技术,当然了,两个技术男聊得最多的话题当然就是技术了.从基础到架构,从算法到AI,无所不谈.中间又穿插着不少天马行空的想象 ...
- MQ在高并发环境下,如果队列满了,如何防止消息丢失?
1.为什么MQ能解决高并发环境下的消息堆积问题? MQ消息如果堆积,消费者不会立马消费所有的消息,不具有实时性,所以可以解决高并发的问题. 性能比较好的消息中间件:Kafka.RabbitMQ,Roc ...
- 高并发场景系列(一) 利用redis实现分布式事务锁,解决高并发环境下减库存
原文:http://blog.csdn.net/heyewu4107/article/details/71009712 高并发场景系列(一) 利用redis实现分布式事务锁,解决高并发环境下减库存 问 ...
- 利用redis实现分布式事务锁,解决高并发环境下库存扣减
利用redis实现分布式事务锁,解决高并发环境下库存扣减 问题描述: 某电商平台,首发一款新品手机,每人限购2台,预计会有10W的并发,在该情况下,如果扣减库存,保证不会超卖 解决方案一 利用数据 ...
- 【高并发】高并发环境下如何优化Tomcat配置?看完我懂了!
写在前面 Tomcat作为最常用的Java Web服务器,随着并发量越来越高,Tomcat的性能会急剧下降,那有没有什么方法来优化Tomcat在高并发环境下的性能呢? Tomcat运行模式 Tomca ...
- 【高并发】高并发环境下如何防止Tomcat内存溢出?看完我懂了!!
写在前面 随着系统并发量越来越高,Tomcat所占用的内存就会越来越大,如果对Tomcat的内存管理不当,则可能会引发Tomcat内存溢出的问题,那么,如何防止Tomcat内存溢出呢?我们今天就来一起 ...
- 高并发环境下,Redisson实现redis分布式锁
原文:http://tlzl0526-gmail-com.iteye.com/blog/2378853 在一些高并发的场景中,比如秒杀,抢票,抢购这些场景,都存在对核心资源,商品库存的争夺,控制不好, ...
- 【实战Java高并发程序设计6】挑战无锁算法:无锁的Vector实现
[实战Java高并发程序设计 1]Java中的指针:Unsafe类 [实战Java高并发程序设计 2]无锁的对象引用:AtomicReference [实战Java高并发程序设计 3]带有时间戳的对象 ...
- 高并发环境下全局id生成策略
解决方案: 基于Redis的全局id生成策略:(推荐此方法) 基于雪花算法的全局id生成: https://www.cnblogs.com/kobe-qi/p/8761690.html 基于zooke ...
随机推荐
- .Net多线程编程—并发集合
并发集合 1 为什么使用并发集合? 原因主要有以下几点: System.Collections和System.Collections.Generic名称空间中所提供的经典列表.集合和数组都不是线程安全 ...
- Android SwipeRefreshLayout 下拉刷新——Hi_博客 Android App 开发笔记
以前写下拉刷新 感觉好费劲,要判断ListView是否滚到顶部,还要加载头布局,还要控制 头布局的状态,等等一大堆.感觉麻烦死了.今天学习了SwipeRefreshLayout 的用法,来分享一下,有 ...
- 在离线环境中使用.NET Core
在离线环境中使用.NET Core 0x00 写在开始 很早开始就对.NET Core比较关注,一改微软之前给人的印象,变得轻量.开源.跨平台.最近打算试着在工作中使用.但工作是在与互联网完全隔离的网 ...
- 在.Net中实现自己的简易AOP
RealProxy基本代理类 RealProxy类提供代理的基本功能.这个类中有一个GetTransparentProxy方法,此方法返回当前代理实例的透明代理.这是我们AOP实现的主要依赖. 新建一 ...
- Windows 常用运行库下载 (DirectX、VC++、.Net Framework等)
经常听到有朋友抱怨他的电脑运行软件或者游戏时提示缺少什么 d3dx9_xx.dll 或 msvcp71.dll.msvcr71.dll又或者是 .Net Framework 初始化之类的错误而无法正常 ...
- 步入angularjs directive(指令)--准备工作熟悉hasOwnProperty
在讲解directive之前,先做一下准备工作,为何要这样呢? 因为我们不是简单的说说directive怎么用,还要知道为什么这么用!(今天我们先磨磨刀!). 首先我们讲讲js 基础的知识--hasO ...
- Solr高级查询Facet
一.什么是facet solr种以导航为目的的查询结果成为facet,在用户查询的结果上根据分类增加了count信息,然后用户根据count信息做进一步搜索. facet主要用于导航实现渐进式精确搜索 ...
- 集合(set)-Python3
set 的 remove() 和 discard() 方法介绍. 函数/方法名 等价操作符 说明 所有集合类型 len(s) 集合基数:集合s中元素个数 set([obj]) 可变集合工 ...
- 【夯实PHP基础】nginx php-fpm 输出php错误日志
本文地址 原文地址 分享提纲: 1.概述 2.解决办法(解决nginx下php-fpm不记录php错误日志) 1. 概述 nginx是一个web服务器,因此nginx的access日志只有对访问页面的 ...
- NGINX引入线程池 性能提升9倍
1. 引言 正如我们所知,NGINX采用了异步.事件驱动的方法来处理连接.这种处理方式无需(像使用传统架构的服务器一样)为每个请求创建额外的专用进程或者线程,而是在一个工作进程中处理多个连接和请求.为 ...