热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

内存缓存与硬盘缓存访问速度的比较

   这两天在为一个应用做solr搜索方案定制的过程中,需要用到solr的fieldcache,在估算fieldcache需要的内存容量,缓存中key是int,value是两个64

      这两天在为一个应用做solr搜索方案定制的过程中,需要用到solr的fieldcache,在估算fieldcache需要的内存容量,缓存中key是int,value是两个64bit大小的long类型数组,数据量大约是8100w,64×8100w/1024/1024,大致需要10G的容量,

 然而服务器总共也只有8G内存,实在无法支持这么大容量的缓存数据。

         

    于是开始想是不是可以有其他的替换的方案,可以不需要使用这么大的缓存容量,有能满足缓存的需要。考虑是不是可以将fieldcache中的数据内存存放到硬盘中去,在调用的时候可以通过key值快速计算出文档中的偏移量从而量数据取出,因为直观感觉只要知道一个文件偏移量而取内存应该是很快的。

          

    光有感觉是不行的,还需要实际测试一下,测试硬盘访问速度到底和内存访问速度相差多大。

 

  初始化测试数据       

    分别写一个向内存中和向硬盘中写数据的代码,内容如下:

  1. 向内存中写

    Map data = new HashMap();
    for (int i = 0; i <2000000; i++) {
    data.put(i, new Long[] { (long) (i + 1), (long) (i + 2) });
    }

     

  2. 向硬盘中写

    import java.io.File;
    import java.io.RandomAccessFile;
    import org.apache.lucene.analysis.Analyzer;
    import org.apache.lucene.analysis.payloads.PayloadHelper;
    import org.apache.lucene.analysis.standard.StandardAnalyzer;
    import org.apache.lucene.document.Document;
    import org.apache.lucene.document.Field;
    import org.apache.lucene.index.IndexWriter;
    import org.apache.lucene.index.IndexWriterConfig;
    import org.apache.lucene.index.IndexWriterConfig.OpenMode;
    import org.apache.lucene.store.Directory;
    import org.apache.lucene.store.SimpleFSDirectory;
    import org.apache.lucene.util.Version;
    public class DocReplication {
    public static Analyzer analyzer;
    static {
    analyzer = new StandardAnalyzer(Version.LUCENE_34);
    }
    public static void main(String[] arg) throws Exception {
    RandomAccessFile randomFile = new RandomAccessFile(new File(
    "DocReplication.text"), "rw");
    Directory dir = new SimpleFSDirectory(new File("indexdir"));
    IndexWriterConfig iwc = new IndexWriterConfig(Version.LUCENE_34,
    analyzer);
    iwc.setOpenMode(OpenMode.CREATE_OR_APPEND);
    IndexWriter writer = new IndexWriter(dir, iwc);
    for (int i = 0; i <2000000; i++) {
    // 向一个随机访问文件中写
    randomFile.write(PayloadHelper.encodeInt(i));
    randomFile.write(long2Array(i + 1));
    randomFile.write(long2Array(i + 2));
    // 向lucene中document中写
    Document doc = new Document();
    doc.add(new Field("id", String.valueOf(i), Field.Store.YES,
    Field.Index.NOT_ANALYZED_NO_NORMS));
    doc.add(new Field("id2", String.valueOf(i), Field.Store.YES,
    Field.Index.NOT_ANALYZED_NO_NORMS));
    writer.addDocument(doc);
    System.out.println("point:" + randomFile.getFilePointer());
    }
    writer.commit();
    writer.close();
    randomFile.close();
    }
    static byte[] long2Array(long val) {
    int off = 0;
    byte[] b = new byte[8];
    b[off + 7] = (byte) (val >>> 0);
    b[off + 6] = (byte) (val >>> 8);
    b[off + 5] = (byte) (val >>> 16);
    b[off + 4] = (byte) (val >>> 24);
    b[off + 3] = (byte) (val >>> 32);
    b[off + 2] = (byte) (val >>> 40);
    b[off + 1] = (byte) (val >>> 48);
    b[off + 0] = (byte) (val >>> 56);
    return b;
    }
    }

     以上向内存中和向硬盘中写都是写200w条数据,在执行向硬盘中写的过程中分别是向lucene的索引文件和向RandomAccessFile随机文件中写,下面介绍一下用RandomAccessFile写的文件结构,文件中一条记录的数据结构,如图:
    《内存缓存与硬盘缓存访问速度的比较》
     一条记录的长度为20字节,只要拿到docid也就是key值就能计算出
    RandomAccessFile的文件偏移量=docid × 20。

     至于为什么要向lucene索引文件中写的原因是,想比较一下通过lucene的 indexread.get(docid) 方法取得到document的fieldvalue 的访问速度,和用RandomAccessFile访问缓存值的速度到底谁更快。

 

编写读数据测试案例

  1. 从自定义随机文件中读取

    public static void main(String[] args) throws Exception {
    RandomAccessFile randomFile = new RandomAccessFile(new File(
    "DocReplication.text"), "rw");
    long current = System.currentTimeMillis();
    for (int i = 0; i <100000; i++) {
    int docid = (int) (Math.random() * 2000000);
    randomFile.seek(docid * 20 + 4);
    randomFile.readLong();
    randomFile.readLong();
    }
    System.out.println((System.currentTimeMillis() - current) / 1000);
    randomFile.close();
    }

     

  2. 从内存中读取

    public static void main(String[] args) {
    Map data = new HashMap();
    for (int i = 0; i <2000000; i++) {
    data.put(i, new Long[] { (long) (i + 1), (long) (i + 2) });
    }
    long start = System.currentTimeMillis();
    Long[] row = null;
    long tmp = 0;
    for (int i = 0; i <100000; i++) {
    int doc = (int) (Math.random() * 2000000);
    row = data.get(doc);
    tmp = row[0];
    tmp = row[1];
    }
    System.out.println((System.currentTimeMillis() - start) );
    }

     

  3. 从lucene索引文件中随机访问

    public static void main(String[] args) throws Exception {
    Directory dir = new SimpleFSDirectory(new File("indexdir"));
    long start = System.currentTimeMillis();
    IndexReader reader = IndexReader.open(dir);
    Document doc = null;
    for (int i = 0; i <100000; i++) {
    int docid = (int) (Math.random() * 2000000);
    doc = reader.document(docid);
    doc.get("id");
    doc.get("id2");
    }
    System.out.println("consume:" + (System.currentTimeMillis() - start)/ 1000);
    }

     三个测试案例,都是从目标存储中从有200w数据量的cache中随机取出一个key,通过key取到value,这样的过程重复10w次,看看需要花费多少时间。

测试结果:

  从自定义随机文件中读取 从内存中读取 从lucene索引文件中随机访问
 总耗时 3717ms 75ms 1673ms

 

    从测试结果看,通过内存读cache是最快的,无疑和预想的结果是一致的,但是本来以为从自定义的随机文件中读取速度要比从lucene的indexreader来取要快些,但从测试结果看恰恰相反,从lucene的indexreader要比自定义随机文件快差不多一倍。

   比较之下,内存和磁盘的访问速度比是75比1673=1比22,速度还是相差挺大的,我很好奇,要是将磁盘改成SSD存储介质的话,磁盘访问速度会有多大提升,无奈现在测试环境中还没有SSD的服务器,改天找一台来测试一下,到时候再将测试结果公布一下。


推荐阅读
  • Spring框架《一》简介
    Spring框架《一》1.Spring概述1.1简介1.2Spring模板二、IOC容器和Bean1.IOC和DI简介2.三种通过类型获取bean3.给bean的属性赋值3.1依赖 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 深入理解Java虚拟机的并发编程与性能优化
    本文主要介绍了Java内存模型与线程的相关概念,探讨了并发编程在服务端应用中的重要性。同时,介绍了Java语言和虚拟机提供的工具,帮助开发人员处理并发方面的问题,提高程序的并发能力和性能优化。文章指出,充分利用计算机处理器的能力和协调线程之间的并发操作是提高服务端程序性能的关键。 ... [详细]
  • 本文介绍了解决Netty拆包粘包问题的一种方法——使用特殊结束符。在通讯过程中,客户端和服务器协商定义一个特殊的分隔符号,只要没有发送分隔符号,就代表一条数据没有结束。文章还提供了服务端的示例代码。 ... [详细]
  • 本文介绍了一个Java猜拳小游戏的代码,通过使用Scanner类获取用户输入的拳的数字,并随机生成计算机的拳,然后判断胜负。该游戏可以选择剪刀、石头、布三种拳,通过比较两者的拳来决定胜负。 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • OpenMap教程4 – 图层概述
    本文介绍了OpenMap教程4中关于地图图层的内容,包括将ShapeLayer添加到MapBean中的方法,OpenMap支持的图层类型以及使用BufferedLayer创建图像的MapBean。此外,还介绍了Layer背景标志的作用和OMGraphicHandlerLayer的基础层类。 ... [详细]
  • 在本教程中,我们将看到如何使用FLASK制作第一个用于机器学习模型的RESTAPI。我们将从创建机器学习模型开始。然后,我们将看到使用Flask创建AP ... [详细]
  • 基于PgpoolII的PostgreSQL集群安装与配置教程
    本文介绍了基于PgpoolII的PostgreSQL集群的安装与配置教程。Pgpool-II是一个位于PostgreSQL服务器和PostgreSQL数据库客户端之间的中间件,提供了连接池、复制、负载均衡、缓存、看门狗、限制链接等功能,可以用于搭建高可用的PostgreSQL集群。文章详细介绍了通过yum安装Pgpool-II的步骤,并提供了相关的官方参考地址。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • importjava.util.ArrayList;publicclassPageIndex{privateintpageSize;每页要显示的行privateintpageNum ... [详细]
  • 本文介绍了使用Spark实现低配版高斯朴素贝叶斯模型的原因和原理。随着数据量的增大,单机上运行高斯朴素贝叶斯模型会变得很慢,因此考虑使用Spark来加速运行。然而,Spark的MLlib并没有实现高斯朴素贝叶斯模型,因此需要自己动手实现。文章还介绍了朴素贝叶斯的原理和公式,并对具有多个特征和类别的模型进行了讨论。最后,作者总结了实现低配版高斯朴素贝叶斯模型的步骤。 ... [详细]
  • 关于如何快速定义自己的数据集,可以参考我的前一篇文章PyTorch中快速加载自定义数据(入门)_晨曦473的博客-CSDN博客刚开始学习P ... [详细]
author-avatar
凌彩霞_685
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有