内存数据库容量极致优化赛题解析 | 学习笔记

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介: 快速学习内存数据库容量极致优化赛题解析

开发者学堂课程【第八届大学生创新创业大赛阿里命题数据库命题解析内存数据库容量极致优化赛题解析学习笔记,与课程紧密连接,让用户快速学习知识。

课程地址https://developer.aliyun.com/learning/course/1045/detail/15281


内存数据库容量极致优化赛题解析


内容介绍:

一、云原生内存数据库 Tair 介绍

二、命题解析和常见优化方式介绍

三、Redis 开发简介

由云原生内存数据库 Tair 带来的问题,提升内存数据引擎资源使用率。后续内容将分为以下三个部分,首先我会介绍云原生内存数据库 Tair,之后将解析命题并介绍常见的内存数据库能量优化方式,为了降低大家参与问题的难度,最后会对 redis 代码进行简要介绍。


一、云原生内存数据库 Tair 介绍

云原生内存数据库Tair是阿里云推出的支持高并发的云原生内存数据库,完全兼容right数据结构和api,支持主从与集群架构,采用多种存储器直应对不同数据温度场景,并提供全球多活、数据闪回和丰富的数字模型等特性,致力于帮助用户构建在线实时场景,可以有效支持游戏,电商、政务的多种多样的应用场景,同时还通过DTS等服务提供了与多种数据库和计算系统进行数据互通的能力。

image.png

Tair团队在内存数据库里面有多年的探索和积累,最早可追溯到2009年4月。Tair1.0诞生,并被应用于淘宝核心系统,在同年的11月正式开始支撑双11的超大流量场景。其后的几年里Tair不断扩充能力,推出了面向不同场景的LDB、RDB等引擎,Fastdump系统,2014年5月Tair推出云上缓存产品OCS,成为阿里云初始的基础产品之一,服务云上Memcache用户,2015年3月跳推出阿里云KVStore即云数据库Redis版,Tair由此开始进入云时代。2019年4月KVStore团队在Redis开源社区贡献排名前三,并在Redis Conf 2019上发表了公开演讲。2019年11月发布Tair3.0即云数据库Redis企业版,包括性能增强型,2020年2月在存储顶会FAST20上发表了内存引擎论文《HotRing:A Hotspot-Aware In-Memory Key-Value Store》。2020年9月数据持久化不依赖传统磁盘,保证每个操作持久化的同时提供近乎Redis社区版的吞吐和延时,极大提升业务数据可靠性。·持久内存型:基于Intel傲腾傲腾持久内存,为您提供大容量、兼容Redis的内存数据库产品。单实例成本对比Redis社区版最高可降低30%,且容量存储型:基于云盘ESSD研发,兼容Redis核心数据结构与接口,可提供大容量、低成本、强持久化的数据库服务。2022年5月论文《Tair-PMem:AFully Durable Non-Volatile Memory Database》被数据库顶会VLDB收录。

image.png


二、命题解析和常见优化方式介绍

本命题的命题背景内内存数据库是以内存为主要介质的数据库管理系统,其特点是吞吐高、延迟低。同时由于内存介质本身容量小和价格高的限制,相对来说存放的数据量比较小,以上限制导致大部分用户只能在内存数据库中存放热点和高频数据,而无法满足新型场景下,对于容量吞吐延迟延迟的高需求。新型场景主要是指将服务所需的数据全部放置在内存数据库中,提供稳定的高吞吐数据服务,满足用户的实时查询和数据分析需求,同时通过持久内存等新硬件以较低的技能开销保证用户数据的可靠性,使得用户可以将内存数据库作为主数据库,而不再需要为了数据可靠性引入其他系统,这将大幅简化用户系统架构,降低开发和运维成本。为了更好的满足以上需求,阿里云原生内存数据库Tair完全兼容Redis的基础上,一方面通过引入持久内存、超高数云盘等进一步提供更大容量的服务,同时自研引擎上从数据结构,运行时数据,备份开销,内存碎片等角度进一步优化MemoryFootprint以提高资源利用率。本命题的内题内容为以开源Redis为原型一同探索在性能稳定的同时如何进一步提升内存数据库的内存利用率,鼓励从各种维度探索和创新。

下面将介绍一些常见的内存容量优化方式,有以下三个方面,第一个方面是结构优化,通过对内存数据库中各类数据结构进行调用,节约内存数据库容量,其中主要分为两个部分,存储结构和索引结构。存储结构主要指存储数据的各类结构,对这类结构可以通过更加紧凑的数据排出方式来降低容量开销,但紧缩的排布往往也会同时增加查询和更新的性能开销,如ziplist在查询时需要扫描多条数据,修改时需要移动控制其他不相关的数据,如何在节约空间的同时控制紧凑排布对数据的影响?是一个可以探索的方向,索引结构主要指内存数据库中的各类索引,主要优化方式有减少不必要的指针等。

第二个方面是数据压缩,其中主要手段有压缩用户数据和数据去重。由于用户存储在内存数据库的数据往往是具有可压缩性的,所以压缩用户数据可以节约内存数据库的容量,这里需要考虑压缩算法、粒度以及参数的选择,更进一步,由于用户的数据特征是多种多样的,用户数据特征甚至是多变的,需要内存数据库能够自动识别特征,并对压缩算法和参数进行动态的调整。压缩词典的构建也是一个需要解决的问题,OLTP内存数据库的数据往往不以页面为单位进行组织,数据相对分散,这给构造词典带来额外的难度,另外,词典的构造频率和时机也需要精确的控制,以平衡词典构建的性能消耗和实时性。对于部分数据重复较多的场景,数据去重也是一个有效的优化方式,比如Redis中的常见字符串使用,如何高效的查找重复的数据是制定方案的难点。

第三个方面是减少额外开销,一是减少运行时开销,主要是各类数据和buffer,二是减少备份时快照的开销,比如Redis在fork后cow的开销,三是减少数据结构的额外开销,比如Redis在主索引在rehash时会同时存在两个hashtable,增加了索引的开销,四是减少内存碎片,在这个方向上,jemalloc等分配器已经做了一些工作,结合内存数据库的分配特征,可以对内存值概率进行进一步的优化,还有一些在OLTP内存数据库中运用较少的优化方式没有详细介绍,比如列式存储。以上只是主要的内存数据库容量优化方式,也期待大家能够找到更多更有效的优化方式。


三、Redis 开发简介

首先会对Redis执行流程进行简单介绍,Redis在Linux上默认基于epoll实现事件循环,请求处理辑作为回调注册进循环中。回调函数为readQueryFromClient,大部分情况为逐层调

用:readQueryFromClient:从网络读取数据到对应client的buffer中 processInputBuffer:对请求进行解析,构造出命令参数等对象。 processCommandAndResetClient:封装请求调用和后续流程 processCommand:该函数较为复杂,有鉴权、查找命令处理函数等,逻辑较为繁杂 call:调用命令处理函数和生成同步日志。

image.png

后来将对 Redis 数据结构进行简单介绍,这张 PPT 上的图是Redis各个结构的关联关系,从左至右是从 RedisDB 到最后的 key 和 value,可以参考这张图来理解Redis主要的索引数据结构。此处以String对象为例,Redis支持多DB所以会有多个redisDB结构,每个DB中有两个主要的索引,分布负责Key到Value和Key到过期信息的映射索引通过Hash表实现,通过链表法解决冲突。每个Hash桶含有一个指针指向冲突链,冲突链中每一个entry有三个指针(分别指向Key,Value 和下一个entry) Value 均为 robj 结构,用于支持多种类型的数据结构。到 robj 为止是通用的结构,之后的结构各个类型会有所不同。

image.png

下面将对比两个Redis中的经典数据结构,Redis使用Hashtable作为其主要的索引结构,对应结构体类型dict,dict中主要有以下变量:,dictType类似虚表,用于支持KV释放等行为的自定义,dictht是hash表的抽象,其中包含指向hashtable的指针,hashtable大小, hashtable中的key数量等统计信息,为了支持rehash,所以有两个dictht。在正常运行时只有一个dictht中包含 hashtable当通过rehash进行hashtable的扩缩容时,会在另外一个dictht中创建出新的hashtable,并将数据从原有hashtable移动到新的hashtable中(所以在rehash时会产生额外的空间消耗),hashtable就是由dictEntry指针构成的数组,每个指针代表一个hash桶,指向相应的冲突链。冲突链由dictEntry构成,其中存储指向key,value和下一个entry的指针,Dict结构在Redis中使用频率很高,主索引和Hash类型等均使用了Dict结构。

image.png

Dict结构:

typedef struct dict {

dictType *type;

void *privdata;

dictht ht[2];

long rehashidx; /* rehashing not in progress if rehashidx=-1*/unsigned long iterators; /* number of iterators currently running * dict;/* This is our hash table structure. Every dictionary has two of this as we* implement incremental rehashing, for the old to the new table.*/

itypedef struct dictht {

dictEntry **table;

unsigned long size;

unsigned long sizemask;

unsigned long used;}

dictht;

typedef struct dictentry {

void *key;

union {

void *val;

vint64_t u64;

int64_t s64;

double d;

} v;

struct dictentry *next;

dictentry;

Ziplist 是 Redis 中典型的紧凑数据结构,通过连续排布 KV 减少指针等带来的空间开销,还通过数值类型的变长编码来进一步压缩空间。查询时需要扫描多个KV以定位到所需数据。结构头部会存储ziplist的字节长度(zlbytes),尾部元素相对起始地址的偏移量(zltail),元素个数(zllen)。之后是存储的各个元素(entry)。最后在整个ziplist 末尾会有一个终止元素(zlend)。

ziplist中的entry实际包含三部分:前一个entry的长度(prevlen),编码(encoding),实际内容(data)。其中前一个entry的长度是为了加速反向遍历。Dict结构不论是hashtable和冲突链中都需通过指针来关联各个对象。而ziplist由于是连续排布数据并不需要额外的指针,这节约了内存空间。但ziplist的连续排布也导致对entry的更新操作往往需要移动其他entry。比如删除某个处于ziplist中间的entry时,需要将之后的entry都往前移动,而移动后又有可能导致占用空间变小,需要进行空间的重新分配。插入操作也存在类似情况。这也导致ziplist结构的插入删除性能往往低于Dict结构。Redis中采用类似优化方式的还有intset。通过上述两个结构的介绍,可以看出在性能和内存容量上是有一定的取舍空间的,主要难度在于如何在保证性能紧密的情况下尽可能的节约内存空间。

image.png

最后介绍一下对Redis代码进行修改编译和测试的方法。首先是代码结构的介绍,主要代码均在主要代码均在src下,其中几个主要文件为:server.h:Redis的主头文件,大部分函数和结构的声明,server.c:Redis进程的初始化逻辑和命令表redisCommandTable等,network.c:网络IO相关函数,db.c:通用命令处理逻辑和相关函数,t*.c:各数据类型命令处理逻辑。编译和测试均使用make即可,更多信息可参考

https://github.com/redis/redis/blob/unstable/README.md

为了方便大家对内存消耗进行分析,这里也介绍一下内存容量监控和限制:MemoryCgroup是Linux上常用的内存用量统计和限制方式,其中的memoryusage_in_bytes提供了进程使用内存总量的统计。利用其中的memorylimit_in_bytes可以限制进程使用的内存总量,具体使用方式可参考

https://www.kenel.org/doc/html/latest/admin guide/cgroup-v1/memory.html

这里也还有一些额外的文档和学习资料,供大家查阅,主要包括

1.Tair帮助文档:https://help.aliyun.com/document detail/145957.html

2.Tair产品入口:https://www.aliyun.com/database/redistair

3.Tair性能白皮书:https://help.aliyun.com/document detail/188009.html

相关文章
|
3天前
|
缓存 监控 NoSQL
【MongoDB 专栏】MongoDB 的内存管理与优化
【5月更文挑战第11天】MongoDB的内存管理优化对性能至关重要,涉及数据缓存、索引及执行操作的内存使用。动态内存管理根据访问模式和负载调整,可通过配置参数优化,如设置合适缓存大小,调整内存分配参数。索引管理也很重要,需定期评估优化,避免内存占用过高。监控内存使用、数据清理压缩、架构规划也是优化手段。面对挑战,如高并发下的内存不足,需灵活调整策略,平衡系统资源。不断学习新方法,提升内存管理能力,以优化MongoDB性能。
【MongoDB 专栏】MongoDB 的内存管理与优化
|
4天前
|
数据库
编程日记02:个人站优化数据库和日志
编程日记02:个人站优化数据库和日志
9 0
|
5天前
|
机器学习/深度学习 算法 Linux
xenomai内核解析--实时内存管理--xnheap
Xenomai是一个实时操作系统(RTOS)层,用于Linux,旨在提供确定性的任务调度和服务。其内存管理机制包括一个名为xnheap的内存池,确保内存分配和释放的时间确定性,以满足硬实时系统的严格需求。
12 0
xenomai内核解析--实时内存管理--xnheap
|
5天前
|
缓存 关系型数据库 数据库
【Docker 专栏】Docker 与容器化数据库的集成与优化
【5月更文挑战第9天】本文探讨了Docker与容器化数据库集成的优势,如快速部署、环境一致性、资源隔离和可扩展性,并列举了常见容器化数据库(如MySQL、PostgreSQL和MongoDB)。讨论了集成方法、注意事项、优化策略,包括资源调整、缓存优化和监控告警。此外,强调了数据备份、恢复测试及性能评估的重要性。未来,随着技术发展,二者的集成将更紧密,为数据管理带来更多可能性。掌握此技术将应对数字化时代的机遇与挑战。
【Docker 专栏】Docker 与容器化数据库的集成与优化
|
6天前
|
缓存 Java 编译器
JMM内存模型 volatile关键字解析
JMM内存模型 volatile关键字解析
11 0
|
6天前
|
存储 关系型数据库 分布式数据库
数据库索引回表困难?揭秘PolarDB存储引擎优化技术
PolarDB分布式版存储引擎采用CSM方案均衡资源开销与可用性。
数据库索引回表困难?揭秘PolarDB存储引擎优化技术
|
14天前
|
存储 SQL 缓存
构建高效的矢量数据库查询:查询语言与优化策略
【4月更文挑战第30天】本文探讨了构建高效矢量数据库查询的关键点,包括设计简洁、表达性强的查询语言,支持空间操作、函数及索引。查询优化策略涉及查询重写、索引优化、并行处理和缓存机制,以提升查询效率和准确性。这些方法对处理高维空间数据的应用至关重要,随着技术进步,矢量数据库查询系统将在更多领域得到应用。
|
14天前
|
存储 缓存 固态存储
优化矢量数据库性能:技巧与最佳实践
【4月更文挑战第30天】本文探讨了优化矢量数据库性能的技巧和最佳实践,包括硬件(如使用SSD、增加内存和利用多核处理器)、软件(索引优化、查询优化、数据分区和压缩)和架构(读写分离、分布式架构及缓存策略)方面的优化措施。通过这些方法,可以提升系统运行效率,应对大数据量和复杂查询的挑战。
|
14天前
|
算法 安全 Linux
深度解析:Linux内核内存管理机制
【4月更文挑战第30天】 在操作系统领域,内存管理是核心功能之一,尤其对于多任务操作系统来说更是如此。本文将深入探讨Linux操作系统的内核内存管理机制,包括物理内存的分配与回收、虚拟内存的映射以及页面替换算法等关键技术。通过对这些技术的详细剖析,我们不仅能够理解操作系统如何高效地利用有限的硬件资源,还能领会到系统设计中的性能与复杂度之间的权衡。
|
16天前
|
关系型数据库 大数据 数据库
关系型数据库索引优化
关系型数据库索引优化是一个综合的过程,需要综合考虑数据的特点、查询的需求以及系统的性能要求。通过合理的索引策略和技术,可以显著提高数据库的查询性能和整体效率。
22 4

推荐镜像

更多