Sử dụng Protostuff để tuần tự hóa, đọc và ghi tệp nhanh chóng.

Đây là phương pháp nhanh nhất trong số các phương pháp mà tôi đã thử nghiệm. Nó có thể được sử dụng như một cơ sở dữ liệu tệp cục bộ đơn giản và có thể lưu trữ các tệp văn bản nhị phân hoặc thuần túy.

Có nhiều cách để đọc ghi các công cụ file thông thường nhưng để đọc ghi đơn giản thì phương pháp JAVA NIO hiện nay là nhanh nhất.

FileUtil.java

package protoBuf;

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.List;

public class FileUtil {

    public static final int BUFSIZE = 1024 * 8;

    /**
     * Ghi dữ liệu nhị phân bằng cách nối thêm
     */
    public static void writeByte2File(byte[] bytes, String writePath) {
        try {
            FileOutputStream fos = new FileOutputStream(writePath, true);
            fos.write(bytes);
            fos.flush();
            fos.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Đã thêm ghi dữ liệu nhị phân mà không đóng tệp
     */
    public static void writeByte2FileFlush(byte[] bytes, String writePath) {
        try {
            FileOutputStream fos = new FileOutputStream(writePath, true);
            fos.write(bytes);
            fos.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Java NIO Mẫu viết thêm
     * @param filepath
     * @param contentList Nội dung file cần ghi
     * @param bufferSize  Kích thước bộ đệm ghi đơn mặc định 4M 1024 * 1024 * 4
     */
    public static void write2FileChannel(String filepath, List<String> contentList, Integer bufferSize) {

        bufferSize = null == bufferSize ? 4194304 : bufferSize;
        ByteBuffer buf = ByteBuffer.allocate(bufferSize);
        FileChannel channel = null;
        try {
            File fileTemp = new File(filepath);
            File parent = fileTemp.getParentFile();
            if (!parent.exists()) parent.mkdirs();
            if (!fileTemp.exists()) fileTemp.createNewFile();

            channel = new FileOutputStream(filepath, true).getChannel();

            for (int i = 0; i < contentList.size(); i++) {
                buf.put((contentList.get(i) + "\r\n").getBytes());
            }

            buf.flip();   // Chuyển sang chế độ có thể đọc được

            while (buf.hasRemaining()) {
                channel.write(buf);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                channel.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


    /**
     * Tập tin hợp nhất chế độ NIO
     */
    public static void mergeFiles(File outFile, String[] files) {
        FileChannel outChannel = null;
        try {
            outChannel = new FileOutputStream(outFile).getChannel();
            for (String f : files) {
                if (null != f) {
                    FileChannel fc = new FileInputStream(f).getChannel();
                    ByteBuffer bb = ByteBuffer.allocate(BUFSIZE);
                    while (fc.read(bb) != -1) {
                        bb.flip();
                        outChannel.write(bb);
                        bb.clear();
                    }
                    fc.close();
                }
            }
        } catch (IOException ioe) {
            ioe.printStackTrace();
        } finally {
            try {
                if (outChannel != null) {
                    outChannel.close();
                }
            } catch (IOException ignore) {
            }
        }
    }

    /**
     * Thêm ghi dữ liệu nhị phân, sử dụng luồng cố định, không đóng file
     */
    public static FileOutputStream writeByte2FileFlush2Stream(FileOutputStream fos, byte[] bytes, String writePath) {
        try {
            if (fos == null) {
                fos = new FileOutputStream(writePath, true);
            }
            fos.write(bytes);
            fos.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return fos;
    }

    /**
     * Chế độ NIO đọc nội dung tập tin vào bộ nhớ cùng một lúc
     */
    public static byte[] readDataFromFile(String filePath) throws Exception {
        //get all data from file
        RandomAccessFile file = new RandomAccessFile(filePath, "rw");
        FileChannel fileChannel = file.getChannel();
        MappedByteBuffer buffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileChannel.size());

        byte res[] = new byte[buffer.capacity()];
        buffer.get(res);
        return res;
    }

    public static void main(String[] args) {
        for(int i=0;i<100;i++){
            writeByte2FileFlush(new String("test"+i).getBytes(), "E:\\testNull.txt");
        }

        try{
            byte[] file = readDataFromFile("E:\\testNull.txt");
            System.out.println("file="+new String(file));
        }catch(Exception e){
            e.printStackTrace();
        }

    }

}

Lớp bao bọc đối tượng đơn giản

WrapperUtil.java

package protoBuf;

public class WrapperUtil<T> {
    private T data;

    public static <T> WrapperUtil<T> builder(T data) {
        WrapperUtil<T> wrapper = new WrapperUtil<>();
        wrapper.setData(data);
        return wrapper;
    }


    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }
}

Các lớp công cụ chính của Protostuff bao gồm các phương pháp tuần tự hóa và giải tuần tự hóa, là chìa khóa để tăng tốc độ xử lý dữ liệu.

ProtoBufUtil.java 

package protoBuf;

import com.google.common.collect.Maps;
import io.protostuff.LinkedBuffer;
import io.protostuff.ProtostuffIOUtil;
import io.protostuff.Schema;
import io.protostuff.runtime.RuntimeSchema;
import org.springframework.objenesis.Objenesis;
import org.springframework.objenesis.ObjenesisStd;

import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;

public class ProtoBufUtil {

    private static Objenesis objenesis = new ObjenesisStd(true);

    /**
     * Một tập hợp các lớp cần được tuần tự hóa/giải tuần tự hóa bằng cách sử 
     * dụng các lớp bao bọc
     */
    private static final Set<Class<?>> WRAPPER_SET = new HashSet<>();

    /**
     * Tuần tự hóa/giải tuần tự hóa các đối tượng lớp bao bọc
     */
    private static final Class<WrapperUtil> WRAPPER_CLASS = WrapperUtil.class;

    /**
     * Tuần tự hóa/giải tuần tự hóa các đối tượng mẫu lớp bao bọc
     */
    private static final Schema<WrapperUtil> WRAPPER_SCHEMA = RuntimeSchema.createFrom(WRAPPER_CLASS);

    /**
     * Thu thập thông tin lược đồ đối tượng và đối tượng bộ đệm
     */
    private static final Map<Class<?>, Schema<?>> CACHE_SCHEMA = Maps.newConcurrentMap();

    /**
     * Xác định trước một số đối tượng mà Protostuff không thể tuần tự hóa/giải 
     * tuần tự hóa trực tiếp
     */
    static {
        WRAPPER_SET.add(List.class);
        WRAPPER_SET.add(ArrayList.class);
        WRAPPER_SET.add(CopyOnWriteArrayList.class);
        WRAPPER_SET.add(LinkedList.class);
        WRAPPER_SET.add(Stack.class);
        WRAPPER_SET.add(Vector.class);

        WRAPPER_SET.add(Map.class);
        WRAPPER_SET.add(HashMap.class);
        WRAPPER_SET.add(TreeMap.class);
        WRAPPER_SET.add(Hashtable.class);
        WRAPPER_SET.add(SortedMap.class);
        WRAPPER_SET.add(Map.class);

        WRAPPER_SET.add(Object.class);
    }


    public ProtoBufUtil() {
    }

    @SuppressWarnings({"unchecked"})
    public static <T> byte[] serializer(T obj) {
        Class<T> cls = (Class<T>) obj.getClass();
        LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
        try {
            Schema<T> schema = getSchema(cls);
            return ProtostuffIOUtil.toByteArray(obj, schema, buffer);
        } catch (Exception e) {
            System.out.println("protobuf serializer fail");
            throw new IllegalStateException(e.getMessage(), e);
        } finally {
            buffer.clear();
        }
    }

    public static <T> T deserializer(byte[] bytes, Class<T> clazz) {
        try {
            T message = (T) objenesis.newInstance(clazz);
            Schema<T> schema = getSchema(clazz);
            ProtostuffIOUtil.mergeFrom(bytes, message, schema);
            return message;
        } catch (Exception e) {
            System.out.println("protobuf deserializer fail");
            throw new IllegalStateException(e.getMessage(), e);
        }
    }

    /**
     * Đăng ký một đối tượng Lớp cần được tuần tự hóa/giải tuần tự hóa bằng cách
     * sử dụng lớp bao bọc
     *
     * @param clazz Đối tượng loại Class cần được bọc
     */
    public static void registerWrapperClass(Class clazz) {
        WRAPPER_SET.add(clazz);
    }

    /**
     *Lấy lược đồ của loại đối tượng được tuần tự hóa
     *
     * @param cls đối tượng được tuần tự hóaclass
     * @param <T> Loại đối tượng được tuần tự hóa
     * @return Kiểu đối tượng được tuần tự hóaschema
     */
    @SuppressWarnings({"unchecked", "rawtypes"})
    private static <T> Schema<T> getSchema(Class<T> cls) {
        Schema<T> schema = (Schema<T>) CACHE_SCHEMA.get(cls);
        if (schema == null) {
            schema = RuntimeSchema.createFrom(cls);
            CACHE_SCHEMA.put(cls, schema);
        }
        return schema;
    }

    /**
     * đối tượng được tuần tự hóa
     *
     * @param obj Các đối tượng cần được tuần tự hóa
     * @param <T> Loại đối tượng được tuần tự hóa
     * @return Mảng nhị phân nối tiếp
     */
    @SuppressWarnings("unchecked")
    public static <T> byte[] serializeCollect(T obj) {
        Class<T> clazz = (Class<T>) obj.getClass();
        LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
        try {
            Object serializeObject = obj;
            Schema schema = WRAPPER_SCHEMA;
            if (!WRAPPER_SET.contains(clazz)) {
                schema = getSchema(clazz);
            } else {
                serializeObject = WrapperUtil.builder(obj);
            }
            return ProtostuffIOUtil.toByteArray(serializeObject, schema, buffer);
        } catch (Exception e) {
           System.out.println("Exception");
            throw new IllegalStateException(e.getMessage(), e);
        } finally {
            buffer.clear();
        }
    }

    /**
     * Giải tuần tự hóa đối tượng
     *
     * @param data  Mảng nhị phân cần được giải tuần tự hóa
     * @param clazz Đối tượng được khử lưu huỳnhclass
     * @param <T>   Loại đối tượng được khử lưu huỳnh
     * @return Bộ sưu tập đối tượng được deserialized
     * SerializeDeserializeWrapper wrapper = SerializeDeserializeWrapper.builder(list);
     * byte[] serializeBytes = ProtostuffUtils.serialize(wrapper);
     * long end4 = System.currentTimeMillis();
     * SerializeDeserializeWrapper deserializeWrapper = ProtostuffUtils.deserialize(serializeBytes, SerializeDeserializeWrapper.class);
     */
    public static <T> T deserializeCollect(byte[] data, Class<T> clazz) {
        try {
            if (!WRAPPER_SET.contains(clazz)) {
                T message = clazz.newInstance();
                Schema<T> schema = getSchema(clazz);
                ProtostuffIOUtil.mergeFrom(data, message, schema);
                return message;
            } else {
                WrapperUtil<T> wrapper = new WrapperUtil<T>();
                ProtostuffIOUtil.mergeFrom(data, wrapper, WRAPPER_SCHEMA);
                return wrapper.getData();
            }
        } catch (Exception e) {
            System.out.println("deserialize exception");
            throw new IllegalStateException(e.getMessage(), e);
        }
    }

    public static byte[] subBytes(byte[] src, int begin, int count) {
        byte[] bs = new byte[count];
        for (int i = begin; i < begin + count; i++) bs[i - begin] = src[i];
        return bs;
    }

    public static byte[] intToByteArray(int i) {
        byte[] result = new byte[4];
        result[0] = (byte) ((i >> 24) & 0xFF);
        result[1] = (byte) ((i >> 16) & 0xFF);
        result[2] = (byte) ((i >> 8) & 0xFF);
        result[3] = (byte) (i & 0xFF);
        return result;
    }

    public static int byteArrayToInt(byte[] bytes) {
        int value = 0;
        for (int i = 0; i < 4; i++) {
            int shift = (3 - i) * 8;
            value += (bytes[i] & 0xFF) << shift;
        }
        return value;
    }
}

Lớp kiểm tra tùy chỉnh cấu trúc dữ liệu đơn giản để ghi vào tệp, đọc tệp, giải tuần tự hóa dữ liệu và đưa dữ liệu đó vào đối tượng JAVA. Protostuff hiện là tốc độ nhanh nhất cho chuỗi hoạt động này và rất nhanh đối với khối lượng dữ liệu lên tới hàng triệu và hàng chục triệu.

ProtoUsage.java 

package protoBuf;

import java.io.File;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class ProtoUsage {

    public static final String filePath = "E:\\testByte";

    //params: list for test
      public static void writeByte2File(List<Product> prodList){
          try{
              if(new File(filePath).exists()){
                  new File(filePath).delete();
              }
              FileOutputStream fos = new FileOutputStream(filePath, true);
              for (Product prod : prodList) {
                  byte data[] = ProtoBufUtil.serializer(prod);
                  byte dataLeng[] = ProtoBufUtil.intToByteArray(data.length);
                  FileUtil.writeByte2FileFlush2Stream(fos, dataLeng, filePath);
                  FileUtil.writeByte2FileFlush2Stream(fos, data, filePath);
              }
          }catch(Exception e){
              e.printStackTrace();
          }

      }

    public static void main(String[] args) {
          try{
              int testCount=5000000;
              List<Product> prodList = new ArrayList<Product>();
              for(int i=0;i<testCount;i++){
                  Product prod = new Product();
                  prod.setId("product="+i);
                  prod.setName("product has a test name: testNo("+i+")");
                  prodList.add(prod);
              }
              long start = System.currentTimeMillis();
              //将测试数据写入文件
              writeByte2File(prodList);
              System.out.println("Write data time cost:"+(System.currentTimeMillis()-start));

              //Bắt đầu đọc tập tin。
              // ProtoStuffƯu điểm lớn nhất là tốc độ tuần tự hóa và giải tuần tự hóa rất nhanh,
              // Điều này giúp tiết kiệm thời gian xử lý dữ liệu của chương trình。 .
              //Đầu tiên đọc tất cả dữ liệu cùng một lúc
              long treatStart = System.currentTimeMillis();
              byte res[] = FileUtil.readDataFromFile(filePath);
              List<Product> resultProd = new ArrayList<Product>();

              //Cấu trúc dữ liệu tệp nhị phân
              // 0016testtesttesttest0018testestestestteste
              //0016(Độ dài của dữ liệu này được lưu)testtesttesttest0018(Độ dài của dữ liệu này được lưu)testestestestteste

              int hasRead = 0;//Lượng dữ liệu được xử lý
              byte length[] = new byte[4];//Độ dài của một đối tượng dữ liệu

              while (res.length != hasRead) {
                  length[0] = res[0 + hasRead];
                  length[1] = res[1 + hasRead];
                  length[2] = res[2 + hasRead];
                  length[3] = res[3 + hasRead];

                  hasRead += 4;
                  int dataLength = ProtoBufUtil.byteArrayToInt(length);
                  byte finalByte[] = ProtoBufUtil.subBytes(res, hasRead, dataLength);

                  Product prod = ProtoBufUtil.deserializer(finalByte, Product.class);
                  resultProd.add(prod);
                  hasRead += dataLength;
              }
              System.out.println("Read and treat Cost time:"+(System.currentTimeMillis()-treatStart));
              System.out.println("list size:"+resultProd.size());
//              resultProd.forEach(System.out::println);
          }catch(Exception e){
              e.printStackTrace();
          }

    }
}

//Object for test
class Product{
    String id;
    String name;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "name: " + this.getName() +
                ", id: " + this.getId();
    }
}

Dưới đây là kết quả kiểm tra. Tôi đã thử nghiệm 5 triệu dữ liệu. Tập tin chỉ được viết một lần. Chúng tôi đã sử dụng tính năng ghi tệp truyền thống trong các thử nghiệm của mình vì trong tình huống thực tế, có rất ít thao tác để ghi tệp cùng một lúc. Sẽ nhanh hơn nếu sử dụng NIO hoặc thứ gì khác.

Như bạn có thể thấy, chỉ mất 3 giây để đọc 5 triệu dữ liệu và giải tuần tự hóa từng dữ liệu. Đây là lợi thế của Protostuff ở một số nơi chỉ cần lưu trữ tệp cục bộ nhỏ để đọc, ghi và xử lý. tốc độ sẽ rất nhanh và rất hữu ích.

Write data time cost:20914 毫秒
Read and treat Cost time:3142 毫秒
list size:5000000

发表回复

Thanks for your support to bet365fans!