第二届云原生挑战赛 RocketMQ的冷热存储引擎赛题思路分析(一)类型空间分配

675 阅读3分钟

先说前言,因为平常工作比较忙,社区上的featrue也需要追踪源码,导致我实在没有精力像其他作者那样频繁的更新博客了。所以这一次,对已经落幕的天池中间件,以一介失败者的身份做一下总结

提交记录:

312323.png

github地址: github.com/complone/ro…

yuque: www.yuque.com/docs/share/… 《RocketMQ冷热存储-leveldb方案调研》

processon(第一版方案构想): www.processon.com/view/link/6…

leveldb的设计论文: www.igvita.com/2012/02/06/…

leveldb源码注释版:github.com/complone/le…

看到赛题的第一反应是要同时支持顺序写以及随机读

顺序写想到了LevelDb的写入流程(流程图的下面部分)

风轮-RocketMQ冷热存储方案.jpg

1.用条件变量加重入锁,控制消息每一次 写入缓冲区->刷写磁盘 保证顺序写
2.采用三级缓存 leveldb的slice (byteBuffer的深拷贝)+ ConrruentSkipListHashMap (并发的跳表保证随机读) + filechannel.force(当缓冲区写入的byteBuffer写满64kb的时候,刷写到磁盘)

Slices

public final class Slice {
    private final byte[] data;
    private final int offset;
    private final int length;

    private int hash;
    public Slice(){
        this.data = new byte[0];
        this.offset = 0;
        this.length = 0;
    }
    public Slice(String s){
        requireNonNull(s, "String is null");
        this.data = s.getBytes();
        this.offset = 0;
        this.length = s.length();
    }

    public Slice(byte[] data)
    {
        requireNonNull(data, "array is null");
        this.data = data;
        this.offset = 0;
        this.length = data.length;
    }

    public Slice(int length)
    {
        this.data = new byte[length];
        this.offset = 0;
        this.length = length;
    }

    public Slice(byte[] data, int offset, int length)
    {
        requireNonNull(data, "array is null");
        this.data = data;
        this.offset = offset;
        this.length = length;
    }

    /**
     * 返回数据长度,以byte计算
     * */
    public int length()
    {
        return this.length;
    }

    /**
     * 返回数据的头指针
     * */
public byte[] getData()
{
    return this.data;
}

/**
 * 判断数据是否为空
 * */
public boolean empty(){
    return this.length == 0;
}
/**
 * 返回数据的偏移量
 */
public int getOffset()
{
    return this.offset;
}

/**
 * 返回指定index的byte
 * @throws IndexOutOfBoundsException
 */
public byte getByte(int index)
{
    checkPositionIndexes(index, index + DataUnit.BYTE_UNIT, this.length);
    index += this.offset;
    return this.data[index];
}

/**
 *
 * Java中所有的byte类型都是signed类型。只能表达(-128,127),signed byte可以表达(0,255)。
 * 通过将byte声明为short或者int类型。然后与0xFF取&。即可将signed byte转为unsigned byte。
 * 0xff 表示为二进制就是 1111 1111。在signed byte类型中,代表-1;但在short或者int类型中则代表255.
 * 当把byte类型的-1赋值到short或者int类型时,虽然值仍然代表-1,但却由1111 1111变成1111 1111 1111 1111.
 * 再将其与0xff进行掩码:
 * -1: 11111111 1111111
 * 0xFF: 00000000 1111111
 * 255: 00000000 1111111
 * 所以这样,-1就转换成255.
 *
 * @throws IndexOutOfBoundsException
 */
public short getUnsignedByte(int index)
{
    return (short) (getByte(index) & 0xFF);
}

/**
 * 返回16位的short,@param index 索引位置
 *
 * 知识点:
 * Leveldb对于数字的存储是little-endian的,在把int32或者int64转换为char*的函数中,
 * 是按照先低位再高位的顺序存放的,也就是little-endian的。所以,这里先要把高位左移8位,
 * 再与低位做或运算。
 *
 * @throws IndexOutOfBoundsException
 */
public short getShort(int index)
{
    checkPositionIndexes(index, index + DataUnit.SHORT_UNIT, this.length);
    index += this.offset;
    return (short) (this.data[index] & 0xFF | this.data[index + 1] << 8);
}

/**
 * 返回32位的int,@param index 索引位置
 *
 * @throws IndexOutOfBoundsException
 */
public int getInt(int index)
{
    checkPositionIndexes(index, index + DataUnit.INT_UNIT, this.length);
    index += offset;
    return (this.data[index] & 0xff) |
            (this.data[index + 1] & 0xff) << 8 |
            (this.data[index + 2] & 0xff) << 16 |
            (this.data[index + 3] & 0xff) << 24;
}

/**
 * 返回64位的long,@param index 索引位置
 *
 * @throws IndexOutOfBoundsException
 */
public long getLong(int index)
{
    checkPositionIndexes(index, index + DataUnit.LONG_UNIT, this.length);
    index += offset;
    return ((long) this.data[index] & 0xff) |
            ((long) this.data[index + 1] & 0xff) << 8 |
            ((long) this.data[index + 2] & 0xff) << 16 |
            ((long) this.data[index + 3] & 0xff) << 24 |
            ((long) this.data[index + 4] & 0xff) << 32 |
            ((long) this.data[index + 5] & 0xff) << 40 |
            ((long) this.data[index + 6] & 0xff) << 48 |
            ((long) this.data[index + 7] & 0xff) << 56;
}

/**
 * 把当前数据复制到目标Slice中
 */
public void getBytes(int index, Slice dst, int dstIndex, int length)
{
    getBytes(index, dst.data, dstIndex, length);
}

Continue..QwQ