本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/Dreamcode/archive/2010/05/26/5624429.aspx
大量并发的应急处理方案与实践2——使用缓存
《大量并发的应急处理方案与实践》提供的方法,只能做应急时的一种参考,不推荐在设计之初时使用,设计之初我们应该采用更合理的架构,以避免文中所提及的问题发生,关于架构请参考我的另一篇文章《开餐馆与做软件——如何提高大型网站性能》。
资源可以分成两种,一种为禁止并发访问的资源,另一种为允许并发访问的资源。禁止并发访问的资源如高速公路收费站,每一个收费口一次只处理一个通过请求,当有多个车辆请求通过时,解决的办法有两种,一种是增设收费口,另一种就是让车辆排队等候,即不马上处理请求,让请求等待空闲资源,我把这种解决办法称作后向异步处理,即将需要处理的请求,延后一段时间处理,这种方法适用于用户不需要看到即使效果的情况。关于“后向异步处理” 的例子请参考《大量并发的应急处理方案与实践1——异步处理》。允许并发访问的资源如商场,任何顾客都可以随时购买需要的商品,早先的大部分商场商品前都有一个柜台,顾客通过售货员购买需要的商品,当顾客增多后,售货员就会应接不暇,让顾客排队是一个办法,但是没有哪个商场会强制顾客排队购买商品,这种“后向异步处理”不适合解决这样的问题,于是一种更好的解决方案产生了——“超市”,取出部分商品直接摆放在售货台上,让顾客自己去取,然后只需要安排一个服务人员随时观察,发现商品不全再到库房中取来相应商品进行补充。
即将如下流程:
顾客 -> 售货员1 -> 商品1 -> 付费
顾客 -> 售货员2 -> 商品2 -> 付费
顾客 -> 售货员3 -> 商品3 -> 付费
改造为
库房 -> 服务员 —> 售货台(缓存)(前向异步处理)
顾客 -> 商品(1、2、3)-> 排队付费(后向异步处理)
这是一个典型例子,顾客对于付费的请求不需要即时响应,我们可以采用后向异步处理的方法。但是对于查看商品的请求越早满足越好,因此我们采用另一种前向异步处理的办法,将商品(数据)提前放到售货台(缓存)里,这样当顾客(请求)需要的时候可以直接取走。下面我们来看一个你可能会遇到的例子:
有一天你的老板突然接到电话,客户抱怨说当登录人数增加的时候,页面打开的非常慢,这时老板找到你,你竭尽全力向老板解释,老板用无奈的眼神望着你,对你说:"用户很急,全靠你了。"
你垂头丧气的走回到座位上,开始一点点排查原因,这时你发现原来这些页面在打开时,伴随大量的数据库操作,
这就是页面打开速度慢的原因。遇到这样的问题,比较简单快速的改造方法就是使用缓存。
即是将如下结构,
用户-> 页面与逻辑 -> 数据库
改造为:
数据库 -> 监控程序 -> 缓存 (前向异步处理)
用户-> 页面与逻辑 -> 缓存
举例来说你发现需要加速的页面 test.jsp, 其通过TestDB.java 类获取数据。
public class TestDB{
...
// 读取数据库 1
String sql1 = "..."; //查询语句
List tmplist1 = queryBySQL(sql1); //queryBySQL 方法,执行sql 语句,执行结果放到一个List 中返回
...
// 读取数据库 2
String sql2 = "..."; //查询语句
List tmplist2 = queryBySQL(sql2); //queryBySQL 方法,执行sql 语句,执行结果放到一个List 中返回
...
// 读取数据库 3
String sql3 = "..."; //查询语句
List tmplist3 = queryBySQL(sql3); //queryBySQL 方法,执行sql 语句,执行结果放到一个List 中返回
...
}
首先,我们创建一个与页面相对应的缓存模块(例如Buffer)。为需要缓存数据的页面创建一个与之对应的类TestBuffer.java 用来存放数据。必须保证TestBuffer.java 中的数据与数据库中相应数据的一一对应关系,因此我们将TestBuffer.java 设计为一个单例。
#Buffer
-- TestBuffer.java
public class TestBuffer{
private volatile static TestBuffer singleton=null;
private TestBuffer(){}
public static TestBuffer getInstance()
{
if(singleton==null){
synchronized(TestBuffer.class)
{
singleton=new TestBuffer();
}
}
return singleton;
}
}
我们分析需要加速页面的数据库操作模块,找出所有数据库操作点。例如我们分析TestDB.java ,发现如下数据库
操作点:读取数据库 1、读取数据库 2、读取数据库 3。这三个操作点分别返回三个List 对象,这三个List 对象就是我们要缓存的数据。我们将这三个对象加入到testBuffer.java 中。
public class TestBuffer{
private volatile static TestBuffer singleton=null;
private TestBuffer(){}
private static List tmplist1 = null
private static List tmplist2 = null
private static List tmplist3 = null
public static TestBuffer getInstance()
{
if(singleton==null){
synchronized(TestBuffer.class)
{
singleton=new TestBuffer();
}
}
return singleton;
}
public List getTmplist1(){
return tmplist1;
}
public List setTmplist1(List tmplist1){
this.tmplist1 = tmplist1;
}
public List getTmplist2(){
return tmplist2;
}
public List setTmplist2(List tmplist2){
this.tmplist2 = tmplist2;
}
public List getTmplist3(){
return tmplist3;
}
public List setTmplist3(List tmplist3){
this.tmplist3 = tmplist3;
}
}
重新改造原先的数据库访问模块TestDB.java ,更改如下:
public class TestDB{
...
//String sql1 = "..."; //查询语句
//String sql2 = "..."; //查询语句
//String sql3 = "..."; //查询语句
//List tmplist1 = queryBySQL(sql1); //queryBySQL 方法,执行sql 语句,执行结果放到一个List 中返回
//List tmplist2 = queryBySQL(sql2); //queryBySQL 方法,执行sql 语句,执行结果放到一个List 中返回
//List tmplist3 = queryBySQL(sql3); //queryBySQL 方法,执行sql 语句,执行结果放到一个List 中返回
List tmplist1 = TestBuffer.getTmplist1();
List tmplist2 = TestBuffer.getTmplist2();
List tmplist3 = TestBuffer.getTmplist3();
...
}
然后我们添加从数据库到缓存的前向异步处理模块,即我们实现一个提前运行的监控程序,将数据库中的数据放到缓存中,并保持缓存与数据库中的数据同步,我们可以使用线程实现。
public class testThread implements Runnable{
private static long interval = 3000; //循环间隔
@Override
public void run(){
while(true){
...
String sql1 = "..."; //查询语句
String sql2 = "..."; //查询语句
String sql3 = "..."; //查询语句
List tmplist1 = queryBySQL(sql1);
TestBuffer.setTmplist1(tmplist1);
List tmplist2 = queryBySQL(sql2);
TestBuffer.setTmplist2(tmplist2);
List tmplist3 = queryBySQL(sql3);
TestBuffer.setTmplist3(tmplist3);
...
try {
Thread.sleep(interval);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
以上提供的方法只做您遇到类似问题时的一种参考,是术,方法千变万化而道理不离其宗,合理使用缓存和异步处理是解决这一类问题的核心。
分享到:
相关推荐
9.6 缓存使用模式实践 172 9.6.1 Cache-Aside 173 9.6.2 Cache-As-SoR 174 9.6.3 Read-Through 174 9.6.4 Write-Through 176 9.6.5 Write-Behind 177 9.6.6 Copy Pattern 181 9.7 性能测试 181 9.8 参考资料 182 10 ...
适用于处理大量并发事件的情况。 5. **服务网格架构**:专注于处理服务间的通信、监控和控制。提高了服务间交互的安全性和可靠性。 **五种架构模型的优缺点分析** - **单体架构**: - 优点:简单易懂,部署容易。...
通过系统工程的方法,拼多多可以识别性能瓶颈,使用性能监控工具进行调优,如数据库查询优化、缓存策略调整等,以提供流畅的用户体验。 9. **风险管理** 系统工程中的风险管理是不可或缺的,拼多多需要预测并应对...
1.12 中断级别与并发 18 1.13 源代码组织 20 1.14 测试网络 21 1.15 小结 22 第2章 mbuf:存储器缓存 24 2.1 引言 24 2.2 代码介绍 27 2.2.1 全局变量 27 2.2.2 统计 28 2.2.3 内核统计 28 2.3 mbuf的定义 29 2.4 ...
1.12 中断级别与并发 18 1.13 源代码组织 20 1.14 测试网络 21 1.15 小结 22 第2章 mbuf:存储器缓存 24 2.1 引言 24 2.2 代码介绍 27 2.2.1 全局变量 27 2.2.2 统计 28 2.2.3 内核统计 28 2.3 mbuf的定义 29 2.4 ...
《银行调度系统设计与优化——基于“张孝祥”实例解析》 银行调度系统是金融机构中不可或缺的关键组成部分,它负责高效、公平地处理各种客户服务请求,如存款、取款、转账等操作。在这个领域,张孝祥老师的名字常常...
在本题目中,我们面临的是一个关于数据结构与算法的实际应用——设计并实现一个飞机订票系统。数据结构是计算机科学中的核心概念,它涉及到如何有效地存储和组织数据,以便于快速访问和处理。而算法则是解决问题的...
《数据库调优:原理、实验与故障排除技术》是一本全面覆盖数据库调优领域的高级书籍,不仅提供了丰富的理论知识,还包含了大量实践经验和技术细节。通过对本书的学习,读者将能够掌握一系列可迁移的技能,从而有效地...
Java中如何使用多线程处理并发任务。 - **服务器协议** 多线程在服务器端的应用,例如处理多个客户端的请求。 - **一客户一线程** 为每个客户端分配一个单独的线程进行处理。 - **线程池** 使用线程池管理...
《淘宝技术这十年》这本书深度剖析了中国最大的电商平台——淘宝在技术领域的演进历程,从一个初创公司的简单网站发展到如今的超大规模互联网系统。在这个过程中,淘宝的技术团队面临着无数挑战,他们如何应对并实现...
这类系统需要在多个服务器之间保持数据的一致性,并且能够处理高并发的事务请求。 - **事务处理**:利用两阶段提交等协议确保跨节点操作的一致性。 - **身份验证**:采用多因素认证技术提高安全性。 - **灾难恢复**...
此外,针对高峰期的业务需求变化,饿了么还实施了如秒杀活动的改进策略,通过分级保护、用户端缓存等手段,有效应对了高并发场景下的挑战。 #### 经验教训与心得 - **事故处理**:饿了么在过去的发展过程中遇到了...
### Zabbix从入门到精通——核心知识点概览 #### 一、Zabbix简介 - **Zabbix概述**:Zabbix是一款开源的企业级监控工具,它能够监控网络中的各种设备和服务状态,包括但不限于服务器、网络设备、应用程序等。通过...
7. **服务器架构**:后端服务器可能采用了分布式架构,保证在高并发情况下仍能稳定运行,处理大量用户的请求。 "失联小助手"app作为另一款参赛作品,其源码可能涵盖了相似但不完全相同的技术点,比如更侧重于离线...
多线程与并发 GC机制 GC收集器类型 串行 CMS 并行 G1 算法 复制 标记清理 标记整理 分区 新生代 eden survivor 老年代(old区) 永久代(perm区) 版本变化 1.5 1.6 1.7 1.8 1.9 IO/NIO IO...
为了处理大量的实时位置数据,位置信使可能采用了缓存技术,如Redis,来减少数据库的读写压力;并可能通过负载均衡和分布式处理,提高系统的可扩展性和稳定性。 9. **消息推送** 实时位置更新需要即时通知用户,...