最近有人面了我一个问题,openGauss的内存管理是什么样的?听到这个问题,我努力想了下,openGauss的内存管理是什么样的? 其它数据库的内存管理是什么样的,一时间千言万语在心头,但是就是不知道应该怎么深入浅出的表达。
事后,我做了一些数据库的内存管理的相关调研,并且做了一番思考和总结。
操作系统的内存管理
数据库虽然是一个基础软件,但是它毕竟是建立在操作系统的基础上,操作系统是比数据库还要底层的存在。自然操作系统的进程通信决定数据库的范围边界。
数据库进程就是操作系统内核创建的,而进程运行一定会使用CPU和内存等资源,对于内存的使用。操作系统提供虚拟地址管理方式,虚拟地址会完成至物理内存的映射,虚拟地址的内存是让你高效的利用内存,不会发生内存碎片。
虚拟地址与物理内存的交换单位以页单位为计算,绝大多数的内存页的默认大小都是 4KB,也有8KB或者16KB。这个时候数据库的设置最好要遵从操作系统的接口要求,利于数据传输。Oracle、MySQL、Postgresql很多方面不同,但是底层都在发生页面交换的事,都要配合操作系统的进程调度机制,这里都是相同的。
上面说的数据库与操作系统两者通过页式工作,另外内存管理还有段式,段式就是内核根据进程的要求,预先分配满足进程要求的内存空间。
页式的好处就是充分利用内存资源,没有造成碎片,但是假设数据库的磁盘频繁访问,且内存资源占用,数据页面换入换出会造成较大的压力。
段式的优势让你占用较多的资源,但是几番换入换出后,会造成较多的碎片。
最好的操作系统内存管理段页式,首先段式分派给数据库进程一定的资源,再通过页式让数据库充分利用好。
数据库的内存管理
数据库内存管理上,分为共享内存和私要内存,以Oracle举例,共享内存是SGA,私要内存是PGA。
我知道这样区分,逻辑不是很严谨, 但是你想象一下,数据库是犹如汽车一样复杂,从请求到处理经过多个精密组件做功,过程中不断产生化学的变化,都会反映到内存上。共享内存有利数据更快速的传输,私要内存完成隐私和隔离。
从openGauss的内存管理 想到的Oracle内存管理、MySQL内存管理以及Postgresql内存管理的回答。
Oracle是一个多进程的数据库,内存管理上由SGA和PGA两部分进行控制,SGA调值更大,可以让Oracle性能更优,通过缓存更多的数据,而不是下探磁盘。SGA开放给所有的服务器进程和后台,PGA是专享给用户相关的进程。
Postgresql同样是一个多进程的数据库,PG通过进程提供共享内存和私要内存,为其它重要进程提供重要服务。
MySQL却是单进程多线程的数据库,但是它也做到内存管理,主要是重做日志缓冲(redo log buffer)、额外内存池(innodb_additional_mem_pool_size)、缓冲池(innodb_buffer_pool)。
MySQL的页面是16k,当MySQL缓冲区的16 Kb的页需要写入文件系统时,需要分4次才行,那自然会出现类似这样的问题:写了3次以后掉电了,那最后4 Kb的数据就丢失了。MySQL的解决方法在硬盘上开辟一个2M的文件缓冲区,内存中缓冲区的数据在写入真正的数据文件之前,先写入这个文件缓冲区,写成功了,再往真正的数据文件去写。
Amazon Nitro自创的Torn Write Prevention技术,亚马逊云数据库实现了优化写入数据,只需一步即可安全写入 16Kb 数据页,完全不用复杂的Double Write了。
openGauss是基于posgresql9.2.4,多次修改,内存内核代码改动变化很大,通过线程实现的内存占用、释放,达到和Postgresql一样的目标。目前默认的opengauss建表方式我来看,还是页式的方式去控制的。openGauss早已经支持段页式的建表,这样更符合客观情况。
使用openGauss时,用户在用SQL语句 create table建表时可以通过指定参数segment=on,使得行存表可以使用段页式的方式存储数据。如果指定hashbucket=on,则默认强制使用segment=on。目前段页式存储不支持列存表。段页式表空间是自动创建的,不需要用户有额外的命令。
1. 指定参数segment=on,创建段页式普通表
create table t1(a int, b int, PRIMARY KEY(a,b)) with(segment=on);
总结
默认的openGauss的表创建在内存管理上是页式,通过设定,可以支持段页式,但是也要看硬件配置以及具体的业务状况去看,否则是不能达到预期目标的。页式内存管理 是针对TP的行式表,假设是一个恶劣的环境,磁盘数据多的,内存容量小小的,行式表选择页式的存储单元有助于大磁盘与小内存的交换。
倘若是列式表,列式希望分析计算的统计运作 从硬盘读取更多的数据,为了达到这一目标,clickhouse的块组织选择8192为数据粒度,达到这个基数就做一次合并压缩,而Dorisdb则是选择1024条数据做稀疏索引,这样后台线程能够持续追加、合并。
再想想TiDB和OceanBase,他们都是使用LSM机制的,对于内存的管理,定期把内存中的增量数据冻结刷新到硬盘里面。尤其是OceanBase的obkv需要的内存贼多,而TiDB可能是底层的rocksdb比较成熟 ,内存的需求量相对OceanBase会少一点。OceanBase内部的数据单元分为宏块和微块,宏块按照 2MB的划分, 每个宏块内部继续拆分多个变长的16KB微块,通过宏块进行库的管理【合并、行出】,微块按需也可以进入内存,提供更 快 的缓存访问。




