SQL基础数据结构
伙伴系统
MEM_ROOT定义
高性能武器–缓存
CPU L1/L2 cache
页高速缓存
分布式内存缓存—memcached
IO_CACHE定义
IO_CACHE的使用
线程上下文—-THD
TABLE_SHARE
TABLE
内存池MEM_ROOT
- 目的: malloc函数申请的内存使用完要使用free函数释放, 但向MEM_ROOT申请的内存,只需要在设为空闲块或释放时需要使用free,这样管理简单不要跟踪每个已分配的内存。
具有相同生命周期的变量向同一个MEM_ROOT实例申请内存,生命周期结束后就释放内存。 - 内存池:mariadb使用MEM_ROOT动态管理内存分配,MEM_ROOT向内存申请大块连续内存,程序再向MEN_ROOT请求所需内存。
- 优点: 减少malloc的调用,减少内存碎片产生。
- 内存碎片问题:
原因:程序请求内存大小和内存释放的时间不同,导致大块连续内存空间被划分为小块不连续内存空间,分为内部碎片和外部碎片。
- 内部碎片
分配器分配的内存空间比请求大(因为对齐和用于free的调用),程序内部未使用的碎片内存–内存碎片。 - 外部碎片
指还没分配的,但太小无法满足程序的大块连续内存申请的空闲内存。
Linux内核使用伙伴系统和slab对象缓存来减少内存碎片的发生
伙伴系统
- 伙伴系统算法解决外部碎片问题。
内核将空闲连续页框分为11组,对应11个空闲链表,分别包含1、2、4、8、16、32、64、128、256、512、1024个连续页框, 第N个页框的起始物理地址是N2^12 (N页大小4K)。
算法:
*. 请求N个连续页框的空闲块时,在N对应的的链表中查找,有就直接分配;
. 没有就在N2对应链表中查找;
. 如果还没找到,就在N4表中查找,如果找到N4连续页框就被分为N+N+N2,剩下的空闲块加入对应的链表。
*. 内核会将大小一致,物理地址连续的空闲块合并,再加入空闲链表.
glibc的内存管理器ptmalloc也采用伙伴系统算法管理内存
MEM_ROOT定义
进程内通常是内部碎片的问题
缓解策略:总是向内存申请4、8、16整数倍的大块内存,减少非对齐内存的申请
-
USED_MEM结构:从内存分配器分配的一个内存块
next:下一个内存块,实现链表
left:剩余内存块
size:当前内存块大小 -
MEN_ROOT使用这种策略管理内存:MEN_ROOT向内存分配器申请大块对齐的内存,程序向MEN_ROOT申请小块的内存
MEM_ROOT结构:管理所有已分配的内存块
free链表:包含足够大的内存空间的内存块
used链表:很小剩余空间,或全部用满的内存块
pre_alloc:初始化时与分配的内存块
min_malloc:阀值,内存块低于它时,改内存块移到used链表
block_size:内存块大小是block_size的整数倍
block_num:用于计算新分配内存块的大小
first_block_usage:计数器,内存块多次不能满足请求时,出于效率考虑,将其加入used内存块链表
err_handler:错误处理函数
MEM_ROOT的使用
MEN_ROOT men_root;
init_alloc_root(&mem_root, 2096, 0); //初始化MEM_ROOT
char p1 = (char)alloc_root(&mem_root, 128); // 分配内存
free_root(&mem_root, 0); //释放内存,如果使用MY_MARK_BLOCKS_FREE仅仅标记为空闲(加入free链表),不返回给内存管理器
高性能武器–缓存
意义:减少慢速操作的执行次数,提高性能
CPU L1/L2 cache
cpu运行越来越快,内存的速度提升却很慢
CPU cache 是位于在CPU和主存之间的能提高读写的内存(存在CPU中)
二级缓存位于一级缓存和主存之间,容量比一级缓存大,速度比一级缓存慢比内存快
页高速缓存
访问内存一次20 -100ns,范围磁盘一次10ms左右。
Linux内核使用page cache页面(4K)缓存机制加快对磁盘文件的读写, 页面缓冲区是内存中用于缓存磁盘文件数据的一块区域。
对于磁盘文件的读请求,内核直接在页高速缓存区查找;如果没有,从磁盘读取相应的页,同时读取紧随其后的少数页,并放入页高速缓冲区中。
对于磁盘文件的写请求,内核直接修改页高速缓冲区中对应的页,修改为脏页,内核再定期回写到磁盘中。
分布式内存缓存—memcached
经常被用来缓存数据库查询结果,减少慢速的数据库访问操作和计算操作,提高性能。
IO_CACHE定义
枚举类型cache_type,指定IO_CACHE的类型,如READ_CACHE等
enum cache_type
{
TYPE_NOT_SET=0,
READ_CACHE,
WRITE_CACHE,
SEQ_READ_APPEND,
READ_FIFO,
READ_NET,
WRITE_NET
};
IO_CACHE是MariaDB中定义的I/O缓存结构,许多磁盘文件的操作都是通过IO_CACHE进行的,还作为网络IO 的缓冲区
IO_CACHE的使用
- 读缓存READ_CACHE
IO_CACHE可以作为磁盘文件的应用层面的读缓存,减少磁盘文件的读操作。
算法:先读取缓存中是否包含请求的数据,如果没有就从磁盘读取包含请求数据的一大块数据到缓存中;根据数据的局部性原理(两次相邻请求的数据位于前一次请求的数据附近)提高磁盘文件的读效率。 - 写缓存WRITE_CACHE
算法:减少写磁盘文件的次数 - IO_CACHE作为顺序读取追加缓存SEQ_READ_APPEND
顺序读:只能从前往后读取
追加写:只能从文件末尾追加数据。
与写缓存功能差不多,一方面将缓存中数据刷入文件中,一方面释放已分配的内存,
分配两倍cachesize大小的内存,前半部分作为顺序读缓冲区,后半部分作为追加写缓冲区,
从IO_CACHE中读取数据的顺序:1、seq_read_buffer—->2、文件—–>3、append_buffer(需要加锁)。
线程上下文—-THD
线程描述符,包含一个线程的上下文信息
一个THD实例对应一个线程,如果使用线程池技术,一个连接对应一个THD实例
THD实例还可以对应MariaDB中其它系统线程
TABLE_SHARE
定义了数据库表的基本信息
一个TABLE_SHARE实例对应于数据库中的一个表
查询语句时,打开表,这时创建一个TABLE实例,如果对应的TABLE_SHARE不存在(第一次使用这个表或FLUSH tables),
那么需要从.frm文件中读取表的信息,然后创建对应的TABLE_SHARE实例
一个数据库表对应一个TABLE_SHARE实例,一个实例可以对应(被共享)多个TABLE实例
TABLE
定义了数据库表的描述符
打开语句中的表就会创建一个TABLE实例