数据库4种索引类型_数据库索引类型

数据库4种索引类型_数据库索引类型本文介绍DCache中k-v和k-k-v这2种数据类型的基本存储结构,底层采用hash存储,key值的设置使用的是一致性哈希算法,hash冲突通过链表解决。希望通过本文可以帮助你快速理解DCache的底层实现。

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE稳定放心使用

本文介绍DCache中k-v和k-k-v这2种数据类型的基本存储结构,帮助你快速理解DCache的底层实现。

存储结构

DCache底层采用哈希表存储。以MKVCache为例,使用的哈希算法在如下文件中:

MKHash.h

MKHash.cpp

DCache在内存中将数据分为索引区和数据区:

  • 数据区用于存储真实的数据
  • 索引区只记录索引的值和对应数据区的地址

内存中的存储结构可以参考下面这个图:

数据库4种索引类型_数据库索引类型
DCache存储结构简图

 

说明:

这个简图是为了便于理解才画成这样,其实际结构要复杂的多,想深入了解的同学参考源码。

哈希算法

官方文档中介绍说,DCache采用的是一致性哈希算法,实现在MKHash.cpp:

unsigned int MKHash::HashRawString(const string &key)
{
    const char *ptr = key.c_str();
    size_t key_length = key.length();
    unsigned int value = 0;

    while (key_length--)
    {
        value += *ptr++;
        value += (value << 10);
        value ^= (value >> 6);
    }
    value += (value << 3);
    value ^= (value >> 11);
    value += (value << 15);


    return value == 0 ? 1 : value;
}


unsigned int MKHash::HashMK(const string &key)
{
    unsigned int uHash = HashRawString(key);


    return uHash;
}


unsigned int MKHash::HashMKUK(const string &key)
{
    unsigned int uHash = HashRawString(key);
    return uHash;
}

一致性哈希的原理不在这里阐述,大家可以自行搜索,这个算法可以解决数据迁移和数据库扩缩容过程中,数据的平滑分片的问题。

DCache采用了这个算法,在数据迁移或数据库横向扩缩容时,最多只会影响到相邻的2个数据节点,而不是需要所有节点都重新分布数据。这个原理跟Redis-Cluster的实现类似。

哈希区

这里定义了2种哈希索引结构:

  • 主key的索引
  • 联合key的索引

在 tc_multi_hashmap_malloc.h文件中,主key的哈希结构定义:

/**
* 主key HashItem
*/
struct tagMainKeyHashItem
{
    uint32_t    _iMainKeyAddr;      // 主key数据项的偏移地址
    uint32_t    _iListCount;        // 相同主key hash索引下主key个数
}__attribute__((packed));

_iMainKeyAddr, 主key索引到的数据偏移地址;

_iListCount, 相同hash值的主key个数。

可见,是采用链表方式处理哈希冲突的。

联合key的哈希结构定义,与主key哈希结构类似:

/**
 * HashItem
 */
struct tagHashItem
{
    uint32_t    _iBlockAddr;        //指向数据项的内存地址索引
    uint32_t    _iListCount;        //链表个数
}__attribute__((packed));

说明:

“联合key”就是二级索引,类似于我们写sql时 “where a=1 and b=2”中的第二个查询条件。

计算主key的哈希值( tc_multi_hashmap_malloc.cpp):

uint32_t TC_Multi_HashMap_Malloc::mhashIndex(const string &mk)
{
    if (_mhashf)
    {
        return _mhashf(mk) % _hashMainKey.size();
    }
    else
    {
        // 如果没有单独指定主key hash函数,则使用联合主键的hash函数
        return _hashf(mk) % _hashMainKey.size();
    }
}

其中,_mhashf 指向了MKHash::HashMK,即一致性哈希算法。

_hashMainKey.size(),主key哈希区元素(即 tagMainKeyHashItem)的个数,这个值是在创建内存结构时初始化好的,不会变。

计算联合key的哈希值:

uint32_t TC_Multi_HashMap_Malloc::hashIndex(const string &mk, const string &uk)
{
    // mk是主key,uk是除主key外的辅key,二者加起来作为联合主键
    return hashIndex(mk + uk);
}


uint32_t TC_Multi_HashMap_Malloc::hashIndex(const string& k)
{
    return _hashf(k) % _hash.size();
}

联合key的哈希计算方式与主key是一致的,只是key值为 主key与联合key的连接串。

现在可以把索引的图补全了:

数据库4种索引类型_数据库索引类型

 

哈希冲突

前面提到DCache采用链表方式处理哈希冲突,具体如何处理的呢?感兴趣的同学可以去研究一下源码(ps:源码比较难懂,需要下功夫)。

这里我仅根据Key和Value的数据结构,大胆猜测一下:

  • 写数据时,通过hash计算出key之后,会判断目标地址是否已有数据:如果已有数据,比对一下key值,key相同(说明是同一条数据)则更新;key不同(说明出现冲突),则扩展冲突链,_iListCount+1;
  • 读数据时,通过hash计算出key之后,到目标地址中取数据,然后判断目标地址中数据的key是否与本次查询的key匹配:如果匹配则返回;如果不匹配则顺着冲突链进行匹配,最多匹配_iListCount次

如果有大量冲突出现时,读写效率会下降到O(n)。所以在采用DCache时,要考虑系统要支撑的数据量大小。

目前DCache的key采用的是 unsigned int类型,最多可以支撑40+亿的数据存储。那么,如果你的系统量级在千万级时,基本可以忽略哈希冲突带来的效率下降。如果是上亿甚至十亿级别,就需要实际验证冲突率(可以在控制台上输入指令查询),视情况调整哈希算法。


总结

  • DCahce底层采用hash存储,读写时间复杂度是O(1);
  • Set、List、k-v、k-k-row都是采用的hash存储;
  • key值采用一致性哈希算法,可以平滑扩容和迁移;
  • 采用链表方式处理hash冲突;
  • DCache最多支持40+亿key的存储,支撑千万级用户量的系统无压力
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/180071.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • matlab 调用dos命令和文件操作

    第一、利用!直接调用,简单方便,可以带操作对象:!delA.bat第二、调用system函数或者dos函数,既可以实现功能,又返回参数,能检查执行情况,方便后面程序的开发,推荐这个[status

    2021年12月24日
    43
  • pycharm怎么缩小代码_pycharm快速缩进

    pycharm怎么缩小代码_pycharm快速缩进Pycharm编写代码的小技巧1、代码块缩进选中要缩进的代码块,按tab键,整个代码块缩进2、取消代码块的缩进选中要取消缩进的代码块,按shift+tab键,整个代码块取消缩进3、编写测试代码语句ifname==‘main’:输入main,然后按下Enter键4、在Pycharm中整块的代码进行注释选中要注释的代码,按下Ctrl+/5、取消整块代码的注释选中要取消注释的代…

    2022年8月27日
    0
  • 迅雷种子为什么php文件后缀,迅雷BT文件后缀是什么?[通俗易懂]

    迅雷种子为什么php文件后缀,迅雷BT文件后缀是什么?[通俗易懂]你是否正在寻找关于文件后缀的内容?让我把最实时的东西奉献给你:迅雷BT文件后缀是什么?BT是一个后缀名为.torrent的小文件,它里面保存了服务器地址、要下载的文件的大孝分成的块数以及各种下载参数设置,这个文件一般在20k-100k大小,可以把*.php直接改成*.torrent试试!要么就是文件制作出错!在去这个页面下载一次,当弹出迅雷下载的时候点取消.让Windows下载.会出现保存对话框…

    2022年10月23日
    0
  • 随机事件概率的公理化定义_概率推理

    随机事件概率的公理化定义_概率推理一、统计定义1.定义注:其中(3)运用的是概率的有限可加性(4)(5)2.优点:直观,适用于未知情况缺点:大量重复试验,不便于实际运用;不够严谨精确,不便于理论运用。

    2022年10月9日
    0
  • PAT乙级-坑点集合

    PAT乙级-坑点集合PAT乙级虽然挺简单但是坑不少。因为不给出测试数据,所以不知道怎么才能AC。以下几个题都是只做对了一部分,剩下的错误的点怎么想都不知道怎么才能过,所以整理一下,等回头再慢慢琢磨。1.1008数组元素循环右移问题1008数组元素循环左移2.1012数字分类1012数字分类3.1014福尔摩斯的约会1014福尔摩斯的约会4.1017A/B…

    2022年5月2日
    35
  • kafka时间轮源码_flume kafka

    kafka时间轮源码_flume kafkaKafka中存在大量的延迟操作,比如延迟生产、延迟拉取以及延迟删除等。Kafka并没有使用JDK自带的Timer或者DelayQueue来实现延迟的功能,而是基于时间轮自定义了一个用于实现延迟功能的定时器(SystemTimer)。JDK的Timer和DelayQueue插入和删除操作的平均时间复杂度为O(nlog(n)),并不能满足Kafka的高性能要求,而基于时间轮可以将插入和删除操作的时间复…

    2022年10月1日
    0

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号