问题:原系统查询接口不支持分页也不可能加入分页支持,导致Ajax查询数据过多,返回数据达到2W多条记录时响应已经极慢,查询功能不要求数据实时性,页面反应速度极慢、体验不好;经排查是由于数据量过大导致写回页面的时候慢,实现是直接Servlet输出流写到页面上(output.write(buffer, 0, b));
需求变更:加快页面相应速度,页面要有分页功能,可以改变原接口
在这种情况下有两种解决方案:
1、前端js分页
2、加入第三方缓存部件(内存数据库等)
两种解决方案的缺点:
第一种方案的问题,虽然解决了分页问题但由于查询的数据量比较大,查询返回页面的数据达几万条有几兆甚至十多兆数据页面反应还是过慢,如果在前端使用js进行分页那查询反应过慢的问题还是没法决解,页面负担过重体验还是差;
第二种方案问题,相对来说会比第一种复杂点,编码量会增多,如是处理实时查询较为复杂但也不是不可以,要考虑清理缓存数据已保证数据的有效性。
经过权衡利弊,决定抛弃第一种方案,决定引入内存数据库已解决问题。
内存数据库现在也有不少种类选择如:redis、H2、HSQLDB等等,在这三个内存数据中redis和其他两个还是不一样的,redis是C语言实现的轻量的基于key-value的nosql数据库,而H2与HSQLDB都是用java实现的轻量关系型数据库;所以我想在nosql数据库和关系型数据库中各选一个,实现此功能最终看性能如何决定使用哪个。 H2相比HSQLDB有web操作界面,H2比HSQLDB更新也比较频繁,所以这里选择H2与redis。
一、redis缓存数据实现分页功能。
redis实现不太友好的地方,由于redis是C语言实现的所以redis无法实现嵌入Java代码中启动redis服务器(我没找到方案),需要另外启 动redis服务器,为了实现分页还需要再启动redis视乎有点过了;
思路:使用redis的zset(Sorted-Sets)有序集和数据类型来存储数据,SeeesionID+数据类型当作key,score为接口查询来的顺序,value为拼装好的单条json数据,可以根据Session失效或设置redis key的失效时间来清除数据。实现代码如下:
/**
* 处理分页,查出数据填充到redis,然后再从redis中分页查询,每次查询都覆盖redis中数据
* @param rtList
* @param callback
* @param response
* @throws IOException
* @linx
* @Date 2014-11-10
*/
public void processPager(List<CSTRTData> rtList, String callback,HttpServletRequest request,
HttpServletResponse response,String top,String skip) throws IOException {
Jedis jedis = new Jedis("127.0.0.1");
OutputStream output = null;
Set<String> setValues=new HashSet<String>();
String dataList = "";
String keyName=request.getSession().getId()+"troubleCode";
if(top==null){
top="1";
}
if(skip==null){
skip="0";
}
if (rtList.size() > 0) {
for (int x = 0; x < rtList.size(); x++) {
JSONObject temp = JSONObject.fromObject(rtList.get(x));
long recvTime = temp.getLong("recvTime");
long obdTime = temp.getLong("obdTime");
temp.remove("recvTime");
temp.put("recvTime", DateUtil.formatYYYYMMDDHHMMSS(recvTime));
temp.remove("obdTime");
temp.put("obdTime", DateUtil.formatYYYYMMDDHHMMSS(obdTime));
jedis.zadd(keyName, x, temp.toString());
}
int start=Integer.valueOf(skip); //开始行号,第一页开始行号为0, 第二页起为上一页skip+页大小
int end = Integer.valueOf(top)+Integer.valueOf(skip)-1; //结束行号 top=页大小, 结束行号=skip+top-1
//根据start、end 序号从小到大 从redis中读取出数据
setValues = jedis.zrange(keyName, start, end);
// Set<String> setValues2 = jedis.zrevrange("hackers", 0, -1);
//遍历拼装json
for (Iterator iter = setValues.iterator(); iter.hasNext();) {
if (iter.hasNext()) {
dataList += (String) iter.next() + ",";
} else {
dataList += (String) iter.next();
}
}
}else{
jedis.del(keyName);
}
//拼装页面最终需要的json串
dataList = callback + "({" + "\"d\" : {" + "\"results\" : [" + dataList
+ "]," + "\"__count\" : \"" + rtList.size() + "\"" + "}"
+ "})";
// 转成输入流
InputStream input = new ByteArrayInputStream(dataList.getBytes());
output = response.getOutputStream();
int b = 0;
byte[] buffer = new byte[1024];
//通过输出流写到页面
while ((b = input.read(buffer)) != -1) {
output.write(buffer, 0, b);
output.flush();
}
if (output != null)
output.close();
if (input != null)
input.close();
}
二、H2缓存数据实现分页功能。
H2有个好处就是可以嵌入在Java代码中启动H2数据库,由于它也是关系型数据库所以使用起来和我们常用的数据库没有什么区别,使用它的内存模式,你不用担心他的性能问题。
思路:在H2中添加张表,然后把每条数据都转成JSON,然后按顺序存入添加的表中,就可以使用我们经常使用的分页Sql语句进行查询,每天点第一页的时候都调用原接口把数据重新覆盖到H2的表中。可以根据Session失效清除表数据。实现代码如下:
public class H2Opertion {
public static void createTable(Connection conn) throws Exception {
Statement stmt = conn.createStatement();
stmt.executeUpdate("CREATE TABLE temp(ID INT PRIMARY KEY,NAME VARCHAR(2000));");
}
public static Connection open() throws Exception {
Class.forName("org.h2.Driver");
Connection conn = DriverManager.getConnection("jdbc:h2:./h2db/demo","sa", "");
return conn;
}
public static void insertData(Connection conn, List<String> list)
throws Exception {
Statement stmt = conn.createStatement();
for (int i = 0; i < list.size(); i++) {
stmt.executeUpdate("INSERT INTO temp VALUES(" + i + ", '" + list.get(i) + "');");
}
}
public static List query(Connection conn,int start,int end) throws Exception {
Statement stmt = conn.createStatement();
//分页查询
ResultSet rs = stmt
.executeQuery("select t2.* from (select rownum r,t1.* from temp t1 where rownum<="+end+") t2 where t2.r>"+start);
while (rs.next()) {
System.out.println(rs.getInt("ID") + "," + rs.getString("NAME"));
}
return null;
}
public static void deleteTable(Connection conn) throws Exception {
Statement stmt = conn.createStatement();
stmt.executeUpdate("delete temp");
}
public static void main(String[] args) {
H2Server server = new H2Server();
server.startServer();
Connection conn = null;
try {
conn = H2Opertion.open();
try {
H2Opertion.createTable(conn); //创建表
} catch (Exception e) {
e.printStackTrace();
H2Opertion.log.error("表存在", e);
}
H2Opertion.log.info("删除表数据");
H2Opertion.deleteTable(conn); //删除表数据
//模拟接口查询出来的数据
List<String> listData = new ArrayList<String>();
for (int i = 0; i < 30; i++) {
listData.add("content" + i);
}
//把数据插入到H2的表中
H2Opertion.insertData(conn, listData);
//分页查询H2表中的数据,10-20条数据
H2Opertion.query(conn,10,20);
} catch (Exception e) {
H2Opertion.log.error(e);
} finally {
try {
conn.close();
} catch (SQLException e) {
H2Opertion.log.error(e);
}
}
}
}
文章首发地址:Solinx
http://www.solinx.co/archives/67
- flask再学习-思考之怎么从数据库中查询数据在页面展示!
看别人视频觉得很简单,要自己做蒙蔽了!这样子.NO! 1. 流程: 首先要有和数据库连接的驱动!一般有PYMySQL mysqlclient 等 使用扩展Flask-SQLAlchemy 获得orm对 ...
- element ui 渲染超过上百条数据时页面卡顿,更流畅的加载大量数据
问题:element ui table渲染上百条数据,页面渲染开始出现延时 解决方案:使用pl-table 注意:设置use-virtual并给定table高度
- 用struts2标签如何从数据库获取数据并在查询页面显示。最近做一个小项目,需要用到struts2标签从数据库查询数据,并且用迭代器iterator标签在查询页面显示,可是一开始,怎么也获取不到数据,想了许久,最后发现,是自己少定义了一个变量,也就是var变量。
最近做一个小项目,需要用到struts2标签从数据库查询数据,并且用迭代器iterator标签在查询页面显示,可是一开始,怎么也获取不到数据,想了许久,最后发现,是自己少定义了一个变量,也就是var变 ...
- 在nodejs使用Redis缓存和查询数据及Session持久化(Express)
在nodejs使用Redis缓存和查询数据及Session持久化(Express) https://segmentfault.com/a/1190000002488971
- keep-alive 必须 页面有name 要不缓存不住数据
keep-alive 必须 页面有name 要不缓存不住数据
- 老司机带你玩转面试(1):缓存中间件 Redis 基础知识以及数据持久化
引言 今天周末,我在家坐着掐指一算,马上又要到一年一度的金九银十招聘季了,国内今年上半年受到 YQ 冲击,金三银四泡汤了,这就直接导致很多今年毕业的同学会和明年毕业的同学一起参加今年下半年的秋招,这个 ...
- 。。。Hibernate 查询数据 事务管理。。。
在Hibernate中,查询数据的时候,可能会需要事务的管理,为什么呢?因为在查询数据库的时候,Hibernate将数据从数据库里面查询出来之后,会先把数据放入Hibernate的session缓存里 ...
- ThinkPHP查询数据与CURD
一.创建连接 在配置文件中使用如下配置: /* 数据库设置 */ 'DB_TYPE' => 'mysql', // 数据库类型 'DB_HOST' => 'localhost', // 服 ...
- mongodb基础系列——数据库查询数据返回前台JSP(二)
上篇博客论述了,数据库查询数据返回前台JSP.博客中主要使用Ajax调用来显示JSON串,来获取其中某一个字段,赋给界面中的某一个控件. 那这篇博客中,我们讲解,把后台List传递JSP展示. Lis ...
随机推荐
- cnodejs社区论坛4--话题列表
- WinForm 窗体应用程序(进阶)之一
进程: 进程,简单的说,就是让你的程序启动另一个程序. 1.Process.Start("calc");//启动计算器 弊端:只认识系统自带的程序,如果写错系统会崩溃. 2. // ...
- 国际化支持(I18N)
本章译者:@nixil 使用国际化支持(I18N)能够使你的应用根据用户所在地区的不同选择不同的语言.下面介绍如何在引用中使用国际化. 只允许使用UTF-8 Play只支持UTF-8一种字符编码.这是 ...
- 哇 真的是一个好插件!!!Sublime Text编辑文件后快速刷新浏览器
http://9iphp.com/web/html/sublime-text-refresh-browser.html这篇博文咯 来源:[Tips]Sublime Text编辑文件后快速刷新浏览器 - ...
- 关于HTML面试题汇总之visibility
一.页面可见性(visibility) 主要提供两个属性,一个事件(都在document对象上):1. 属性: 1.1. hidden:获取或设置当前页面的可见性,boolean值: 1.2 ...
- Perfect Scrollbar – 完美的 jQuery 滚动条插件
Perfect Scrollbar 是一个很小的,但完美的 jQuery 滚动插件.滚动条不会影响原来的设计布局,滚动条的设计是完全可定制的.你可以改变几乎所有的 CSS 样式的滚动条,滚动条设计对脚 ...
- 茎叶图(stem)
介绍 茎叶图(Stem-and-Leaf display)又称“枝叶图”,由统计学家约翰托奇( Arthur Bowley)设计,它的思路是将数组中的数按位数进行比较,将数的大小基本不变或变化不大的位 ...
- javascript的浅拷贝和深拷贝
1.浅拷贝:复制一份引用,所有引用对象都指向一份数据,并且都可以修改这份数据. 2.深拷贝(复杂):复制变量值,对于非基本类型的变量,则递归至基本类型变量后,再复制. 这里画一个简单的图来加深理解: ...
- 《The Linux Command Line》 读书笔记04 Linux用户以及权限相关命令
Linux用户以及权限相关命令 查看身份 id:Display user identity. 这个命令的输出会显示uid,gid和用户所属的组. uid即user ID,这是账户创建时被赋予的. gi ...
- 微信小程序如何设置开发者和体验者
微信小程序需要在后台添加开发者和体验者 开发者:增加开发人员的,开发人员添加后,可上传代码,最多10个人,可以删除 体验者:添加为体验者,管理员发布体验版本后,通过扫码二维码可以下载体验版小程序,最多 ...