系统无法分配所需内存_Innodb内存管理解析

本文主要介绍innodb的内存管理,涉及基础的内存分配结构、算法以及buffer pool的实现细节,提及change buffer、自适应hash index和log buffer的基本概念和内存基本配比,侧重点在内存的分配和管理方式。本文所述内容基于mysql8.0版本。

基础内存分配

在5.6以前的版本中,innodb内部实现了除buffer pool外的额外内存池,那个时期lib库中的分配器在性能和扩展性上表现比较差,缺乏针对多核系统优化的内存分配器,像linux下最通用的ptmalloc的前身是Doug Lea Malloc,也是因为不支持多线程而被弃用了。所以innodb自己实现了内存分配器,使用额外的内存池来响应那些原本要发给系统的内存请求,用户可以通过设置参数innodb_use_sys_malloc 来选择使用innodb的分配器还是系统分配器,使用innodb_additional_mem_pool_size参数设定额外内存池的大小。随着多核系统的发展,一些分配器对内部实现进行了优化和扩展,已经可以很好的支持多线程,相较于innodb特定的内存分配器可以提供更好的性能和扩展性,所以这个实现在5.6版本已经弃用,5.7版本删除。本文所讨论的内容和涉及到的代码基于mysql8.0版本。

innodb内部封装了基础分配释放方式malloc,free,calloc,new,delete等,在开启pfs模式下,封装内部加入了内存追踪信息,使用者可传入对应的key值来记录某个event或者模块的内存分配信息,这部分信息通过pfs内部表来对外展示,以便分析内存泄漏、内存异常等问题。

innodb内部也提供了对系统基础分配释放函数封装的allocator,用于std::*容器内部的内存分配,可以让这些容器内部的隐式分配走innodb内部封装的接口,以便于内存信息的追踪。基础的malloc/calloc等封装后也会走allocator的接口。

基础封装:

非UNIV_PFS_MEMORY编译模式
#define UT_NEW(expr, key) ::new (std::nothrow) expr
#define UT_NEW_NOKEY(expr) ::new (std::nothrow) expr
#define UT_DELETE(ptr) ::delete ptr
#define UT_DELETE_ARRAY(ptr) ::delete[] ptr
#define ut_malloc(n_bytes, key) ::malloc(n_bytes)
#define ut_zalloc(n_bytes, key) ::calloc(1, n_bytes)
#define ut_malloc_nokey(n_bytes) ::malloc(n_bytes)
...打开UNIV_PFS_MEMORY
#define UT_NEW(expr, key)                                                ::new (ut_allocator<byte>(key).allocate(sizeof expr, NULL, key, false, false)) expr
#define ut_malloc(n_bytes, key)                         static_cast<void *>(ut_allocator<byte>(key).allocate( n_bytes, NULL, UT_NEW_THIS_FILE_PSI_KEY, false, false))#define ut_zalloc(n_bytes, key)                         static_cast<void *>(ut_allocator<byte>(key).allocate( n_bytes, NULL, UT_NEW_THIS_FILE_PSI_KEY, true, false))#define ut_malloc_nokey(n_bytes)               static_cast<void *>(                         ut_allocator<byte>(PSI_NOT_INSTRUMENTED) .allocate(n_bytes, NULL, UT_NEW_THIS_FILE_PSI_KEY, false, false))...

可以看到在非UNIV_PFS_MEMORY编译模式下,直接调用系统的分配函数,忽略传入的key,而UNIV_PFS_MEMORY编译模式下使用ut_allocator分配,下面有ut_allocator的介绍,比较简单的封装。

memory heap

12784e7d0fd4f442645b759332d820d5.png

主要管理结构为mem_heap_t,8.0中的实现比较简单,内部维护block块的链表,包含指向链表开始和尾部的指针可以快速找到链表头部和尾部的节点,每个节点都是mem_heap_t的结构。mem_heap在创建的时候会初始一块内存作为第一个block,大小可由使用者指定。mem_heap_alloc响应基本的内存分配请求,先尝试从block中切分出满足请求大小的内存,如果不能满足则创建一个新的block,新的block size至少为上一个block的两倍(last block),直到达到规定的上限值,新创建的block总是链到链表的尾部。mem_heap_t中记录了block的size和链表中block的总size以及分配模式(type)等信息,基本结构如下图:

ee9629fa5b9d1261d7eccb7901594ecc.png

innodb在内部定义了三种分配block模式供选择:

    1. MEM_HEAP_DYNAMIC 使用malloc动态分配,比较通用的分配模式
    2. MEM_HEAP_BUFFER size满足一定条件,使用buffer pool中的内存块
    3. MEM_HEAP_BTR_SEARCH 保留额外的内存,地址保存在free_block中

一般的使用模式为MEM_HEAP_DYNAMIC,也可以使用b|c的模式,在某些情况下使用free_block中的内存。mem heap链表中的block在最后统一free,按照分配模式走不同的free路径。

基本思想也是使用一次性分配大内存块,再从大内存块中切分来响应小内存分配请求,以避免多次调用malloc/free,减少overhead。实现上比较简单,内部只是将多个大小可能不一的内存块使用链表链起来,大多场景只有一个block。没有内存归还、复用和合并等机制,使用过程中不会将内存free,会有一定程度的内存浪费,但有效减少了内存碎片,比较适用于短周期多次分配小内存的场景。

基础allocator

ut_allocator

innodb内部提供ut_allocator用来为std::* 容器分配内存,内部封装了基础的内存分配释放函数,以便于内存追踪和统一管理。

ut_allocator提供基本的allocate/deallocate的分配释放函数,用于分配n个对象所需内存,内部使用malloc/free。另外也提供了大内存块的分配,开启LINUX_LARGE_PAGES时使用HugePage,在服务器内存比较大的情况可以减少页表条目提高检索效率,未开启时使用mmap/munmap内存映射的方式。

更多细节见如下代码:

/** Allocator class for allocating memory from inside std::* containers. */
template <class T>
class ut_allocator {// 分配n个elements所需内存大小,内部使用malloc/calloc分配// 不开启PFS_MEMORY分配n_elements * sizeof(T)大小的内存// 开启PFS_MEMORY多分配sizeof(ut_new_pfx_t)大小的内存用于信息统计pointer allocate(size_type n_elements, const_pointer hint = NULL,PSI_memory_key key = PSI_NOT_INSTRUMENTED,bool set_to_zero = false, bool throw_on_error = true);// 释放allocated()分配的内存,内部使用free释放                  void deallocate(pointer ptr, size_type n_elements = 0)// 分配一块大内存,如果开启了LINUX_LARGE_PAGES使用HugePage// 否则linux下使用mmappointer allocate_large(size_type n_elements, ut_new_pfx_t *pfx)// 对应allocate_large,linux下使用munmapvoid deallocate_large(pointer ptr, const ut_new_pfx_t *pfx)// 提供显示构造/析构funcvoid construct(pointer p, const T &val) { new (p) T(val); }void destroy(pointer p) { p->~T(); }开启PFS_MEMORY场景下提供更多可用func:pointer reallocate(void *ptr, size_type n_elements, PSI_memory_key key);// allocate+construct的封装, 分配n个单元的内存并构造相应对象实例pointer new_array(size_type n_elements, PSI_memory_key key);// 同上,deallocate+destroy的封装void delete_array(T *ptr);
}

mem_heap_allocator

mem_heap_t的封装,可以作为stl allocator来使用,内部使用mem_heap_t的内存管理方式。

/** A C++ wrapper class to the mem_heap_t routines, so that it can be used
as an STL allocator */
template <typename T>
class mem_heap_allocator {mem_heap_allocator(mem_heap_t *heap) : m_heap(heap) {}pointer allocate(size_type n, const_pointer hint = 0) {return (reinterpret_cast<pointer>(mem_heap_alloc(m_heap, n * sizeof(T))));}void deallocate(pointer p, size_type n) {}// 提供显示构造/析构funcvoid construct(pointer p, const T &val) { new (p) T(val); }void destroy(pointer p) { p->~T(); }private:mem_heap_t *m_heap;
}

buddy

innodb支持创建压缩页以减少数据占用的磁盘空间,支持1K, 2K, 4K 、8K和16k大小的page。buddy allocator用于管理压缩page的内存空间,提高内存使用率和性能。

buffer pool中:
UT_LIST_BASE_NODE_T(buf_buddy_free_t) zip_free[BUF_BUDDY_SIZES_MAX];/** Struct that is embedded in the free zip blocks */
struct buf_buddy_free_t {union {ulint size; /*!< size of the block */byte bytes[FIL_PAGE_DATA];} stamp;buf_page_t bpage; /*!< Embedded bpage descriptor */UT_LIST_NODE_T(buf_buddy_free_t) list;
};

f05133ad41a76f7e0bb326c356bd61f1.png

伙伴系统也是比较经典的内存分配算法,也是linux内核用于解决外部碎片的一种手段。innodb的实现在算法上与buddy的基本实现并无什么区别,所支持最小的内存块为1k(2^10),最大为16k,每种内存块维护一个链表,多种内存块链表组成了zip_free链表。

分配入口在buf_buddy_alloc_low,先尝试从zip_free[i]中获取所需大小的内存块,如果当前链表中没有,则尝试从更大的内存块链表中获取,获取成功则进行切分,一部分返回另一块放入对应free链表中,实际上是buf_buddy_alloc_zip的一个递归调用,只是传入的i不断增加。如果一直递归到16k的块都没法满足,则从buffer pool中新申请一块大内存块,并将其按照伙伴关系进行(比如现分配了16,需要2k,先切分8k,8k,再将其中一个8k切分为4k,4k,再将其中4k切分为2k,2k)切分直到满足分配请求。

释放入口在buf_buddy_free_low,为了避免碎片在释放的时候多做了一些事情。在释放一个内存块的时候没有直接放回对应链表中,而是先查看其伙伴是不是free的,如果是则进行合并,再尝试对合并后的内存块进行合并。如果其伙伴是在USED的状态,这里做了一次relocate操作,将其内容拷贝到其它free的block块上,再进行对它合并。这种做法有效减少了碎片的存在,但拷贝这种操作也降低了性能。

buffer pool

buffer pool是innodb主内存中一块区域,用于缓存主表和索引中的数据,读线程可以直接从buffer pool中读取相应数据从而避免io提升读取性能,当一个页面需要修改时,先在buffer pool中进行修改,另有后台线程来负责刷脏页。一般在专用服务器中,会将80%的内存分配给buffer pool使用。数据库启动时就会将内存分配给buffer pool,不过内存有延迟分配的优化,这部分内存在未真正使用前是没有进行物理映射的,所以只会影响虚存大小。buffer pool的内存在运行期间不会收缩还给系统,在数据库关闭时将这部分内存统一释放。可以设置多个buffer pool实例。

buffer pool中使用chunk内存块来管理内存,每个buffer pool实例包含一个或多个chunk,chunks在buffer pool初始化时使用mmap分配,并初始化为多个block。每个block地址相差UNIV_PAGE_SIZE,UNIV_PAGE_SIZE一般是16kb,这块内存包含了page相关控制信息和真正的数据page两部分,之后将这些page加入free list中供使用。这里直接使用了mmap而非malloc,是因为在glibc的ptmalloc内存分配器中,大于MMAP_THRESHOLD阀值的内存请求也是使用mmap,MMAP_THRESHOLD默认值是128k,buffer pool的配置大小一般会远大于128k。

数据结构

buffer pool进行内存管理的主要数据结构。

buf_pool_t

控制buffer pool的主结构,内部包含多种逻辑链表以及相关锁信息、统计信息、hash table、lru和flush算法相关等信息。

struct buf_pool_t {//锁相关 BufListMutex chunks_mutex;    /*!< protects (de)allocation of chunks*/BufListMutex LRU_list_mutex;  /*!< LRU list mutex */BufListMutex free_list_mutex; /*!< free and withdraw list mutex */BufListMutex zip_free_mutex;  /*!< buddy allocator mutex */BufListMutex zip_hash_mutex;  /*!< zip_hash mutex */ib_mutex_t flush_state_mutex; /*!< Flush state protection mutex */BufPoolZipMutex zip_mutex;    /*!< Zip mutex of this buffer */// index、各种size、数量统计ulint instance_no;            /*!< Array index of this buffer pool instance */ulint curr_pool_size;         /*!< Current pool size in bytes */ulint LRU_old_ratio;          /*!< Reserve this much of the buffer pool for "old" blocks */
#ifdef UNIV_DEBUGulint buddy_n_frames; 
#endifut_allocator<unsigned char> allocator; // 用于分配chunksvolatile ulint n_chunks;     /*!< number of buffer pool chunks */volatile ulint n_chunks_new; /*!< new number of buffer pool chunks */buf_chunk_t *chunks;         /*!< buffer pool chunks */buf_chunk_t *chunks_old;    ulint curr_size;             /*!< current pool size in pages */ulint old_size;              /*!< previous pool size in pages */// hash table, 用于索引相关数据页page_no_t read_ahead_area;   hash_table_t *page_hash;    hash_table_t *page_hash_old; hash_table_t *zip_hash;     // 统计信息相关ulint n_pend_reads;         ulint n_pend_unzip;         time_t last_printout_time;buf_buddy_stat_t buddy_stat[BUF_BUDDY_SIZES_MAX + 1];buf_pool_stat_t stat;     /*!< current statistics */buf_pool_stat_t old_stat; /*!< old statistics */// flush相关BufListMutex flush_list_mutex; FlushHp flush_hp;             UT_LIST_BASE_NODE_T(buf_page_t) flush_list;ibool init_flush[BUF_FLUSH_N_TYPES];ulint n_flush[BUF_FLUSH_N_TYPES];os_event_t no_flush[BUF_FLUSH_N_TYPES];ib_rbt_t *flush_rbt;   ulint freed_page_clock; ibool try_LRU_scan;     lsn_t track_page_lsn; /* Pagge Tracking start LSN. */lsn_t max_lsn_io;UT_LIST_BASE_NODE_T(buf_page_t) free;UT_LIST_BASE_NODE_T(buf_page_t) withdraw;ulint withdraw_target; // lru 相关LRUHp lru_hp;LRUItr lru_scan_itr;LRUItr single_scan_itr;UT_LIST_BASE_NODE_T(buf_page_t) LRU;buf_page_t *LRU_old; ulint LRU_old_len;   UT_LIST_BASE_NODE_T(buf_block_t) unzip_LRU;
#if defined UNIV_DEBUG || defined UNIV_BUF_DEBUGUT_LIST_BASE_NODE_T(buf_page_t) zip_clean;
#endif /* UNIV_DEBUG || UNIV_BUF_DEBUG */UT_LIST_BASE_NODE_T(buf_buddy_free_t) zip_free[BUF_BUDDY_SIZES_MAX];buf_page_t *watch;
};

buf_page_t

数据页的控制信息,包含数据页的大部分信息,page_id、size、引用计数(io,buf),access_time(用于lru调整),page_state以及压缩页的一些信息。

class buf_page_t {public:page_id_t id;page_size_t size;uint32_t buf_fix_count;buf_io_fix io_fix;buf_page_state state;the flush_type.  @see buf_flush_t */unsigned flush_type : 2;unsigned buf_pool_index : 6;page_zip_des_t zip; 
#ifndef UNIV_HOTBACKUPbuf_page_t *hash; 
#endif              /* !UNIV_HOTBACKUP */
#ifdef UNIV_DEBUGibool in_page_hash; /*!< TRUE if in buf_pool->page_hash */ibool in_zip_hash;  /*!< TRUE if in buf_pool->zip_hash */
#endif                /* UNIV_DEBUG */UT_LIST_NODE_T(buf_page_t) list;
#ifdef UNIV_DEBUGibool in_flush_list; ibool in_free_list;  
#endif                 /* UNIV_DEBUG */FlushObserver *flush_observer; /*!< flush observer */lsn_t newest_modification;lsn_t oldest_modification;UT_LIST_NODE_T(buf_page_t) LRU;
#ifdef UNIV_DEBUGibool in_LRU_list; 
#endif               /* UNIV_DEBUG */
#ifndef UNIV_HOTBACKUPunsigned old : 1;               unsigned freed_page_clock : 31; unsigned access_time; 
#ifdef UNIV_DEBUGibool file_page_was_freed;
#endif /* UNIV_DEBUG */
#endif /* !UNIV_HOTBACKUP */
};

逻辑链表

free list

包含空闲的pages,当需要从buffer pool中分配空闲块时从free list中摘取,当free list为空时需要从LRU、unzip_LRU或者flush list中进行淘汰或刷脏以填充free list。buffer pool初始化时创建多个chunks,划分的pages都加入free list中待使用。

LRU list

最重要的链表,也是缓存占比最高的链表。buffer pool使用lru算法来管理缓存的数据页,所有从磁盘读入的数据页都会先加入到lru list中,也包含压缩的page。新的page默认加入到链表的3/8处(old list),等待下次读取并满足一定条件后再从old list加入到young list中,以防止全表扫描污染lru list。需要淘汰时从链表的尾部进行evict。

unzip_LRU list

是LRU list的一个子集,每个节点包含一个压缩的page和指向对应解压后的page的指针。

flush list

已修改过还未写到磁盘的page list,按修改时间排序,带有最老的修改的page在链表的最尾部。当需要刷脏时,从flush list的尾部开始遍历。

zip_clean list

包含从磁盘读入还未解压的page,page一旦被解压就从zip_clean list中删除并加入到unzip_LRU list中。

zip_free

用于buddy allocator的空闲block list,buddy allcator是专门用于压缩的page(buf_page_t)和压缩的数据页的分配器。

主要操作

e06e0282e1decfe5413acb176543616b.png

初始化buffer pool

buffer pool在db启动时调用buffer_pool_create进行初始化,使用mmap创建配置大小的虚拟内存,并划分为多个chunks,其它字段基本使用calloc分配(memset)。

buf_page_get_gen

从buffer pool中读取page,比较重要的操作。通过page_id获取对应的page,如果buffer pool中有这个page则封装一些信息后返回,如果没有则需要从磁盘中读入。读取模式分为以下7种:

NORMAL

在page hash table中查找(hash_lock s模式),如果找到增加bufferfix cnt并释放hash_lock,如果该page不在buffer pool中则以sync模式从磁盘中读取,并加入对应的逻辑链表中,判读是否需要线性预读。如果第一次访问buffer pool中的该page,设置访问时间并判断是否需要线性预读。判断是否需要加入到young list中。

SCAN

如果page不在buffer pool中使用异步读取磁盘的模式,不做随机预读和线性预读,不设置访问时间不加入young list,其它与normal一样。

IF_IN_POOL

只在buffer pool中查找page,如果没有找到则返回NOT_FOUND。

PEEK_IF_IN_POOL

这种模式仅仅用来drop 自适应hash index,跟IF_IN_POOL类似,只是不加入young list,不做线性预读。

NO_LATCH

读取并设置buffefix,但是不加锁。

IF_IN_POOL_OR_WATCH

与IF_IN_POOL类似,只在buffer pool中查找此page,如果没有则设置watch。

POSSIBLY_FREED

与normal类似,只是允许执行过程中page被释放。

buf_LRU_get_free_block

从buffer pool的free list中摘取一个空闲页,如果free list为空,移除lru链表尾部的block到free list中。这个函数主要是用于用户线程需要一个空闲block来读取数据页。具体操作如下:

  1. 如果free list不为空,从中摘取并返回。否则转下面的操作。
  2. 如果buffer pool设置了try_LRU_scan,遍历lru链表尝试从尾部释放一个空闲块加入free list中。如果unzip_LRU链表不为空,则先尝试从unzip_LRU链表中释放。如果没有找到再从lru链表中淘汰。
  3. 如果没有找到则尝试从lru中flush dirty page并加入到free list中。
  4. 没有找到,设置scan_all重复上述过程,与第一遍不同的地方在于需要scan整个lru链表。
  5. 如果遍历了整个lru链表依然没有找到可以淘汰的block,则sleep 10s等待page cleaner线程做一批淘汰或者刷脏。
  6. 重复上述过程直到找到一个空闲block。超过20遍设置warn信息。

page cleaner

系统线程,负责定期清理空闲页放入free list中和flush dirty page到磁盘上,dirty page指那些已经被修改但是还未写到磁盘上的数据页。使用innodb_page_cleaners 参数设定page cleaner的线程数,默认是4。通过特定参数控制dirty page所占buffer pool的空间比维持在一定水位下,默认是10%。

9e63aa21866d72f1171297d61233eebb.png

flush list

上面章节提到过flush_list是包含dirty page并按修改时间有序的链表,在刷脏时选择从链表的尾部进行遍历淘汰,代码主体在buf_do_flush_list_batch中。这里不得不提的一个巧妙的操作,叫作Hazard Pointer,buf_pool_t中的flush_hp,将整体遍历复杂度由最差O(n*n)降到了O(n)。之所以复杂度最差会变为O(n*n)是由于flush list允许多个线程并发刷脏,每次从链表尾部进行遍历,使用异步io的方式刷盘,在io完成后将page从链表中摘除,每次提交异步io后从链表尾部再次扫描,在刷盘速度比较慢的情况下,可能每次都需要跳过之前已经flush过的page,最差会退化为O(n*n)。

flush_hp

用于刷脏遍历flush list过程,flush_list_mutex保护下修改,在处理当前page之前,将hazard pointer设置为下一个要遍历的buf_page_t的指针,为线程指定下一个需要处理的page。当前page刷盘时会释放flush_list_mutex,刷盘完成后重新获得锁,处理flush_hp指向的page,无论这中间发生过什么,链表如何变动,flush_hp总是被设置成为下一个有效的buf_page_t指针。所以复杂度总能保证为O(n)。

刷脏的过程中也做了一些优化,代码在buf_flush_page_and_try_neighbors可以将当前page相邻的dirty page页也一起刷盘,目的是将多个随机io转为顺序io减少overhead,这在传统HHD的设备上比较有用,在SSD上seek time已经不是显著的影响因素。可以使用参数innodb_flush_neighbors进行设置和关闭:

  1. 为0则关闭flush neighbors的优化
  2. 默认值为1,flush与当前page相同extent(1M)上的连续dirty page
  3. 为2则flush与当前page相同extent上的dirty page

flush LRU list

buf_flush_LRU_list主要完成两件事:

  1. 将lru list尾部的可以移除的pages放入到free list中
  2. 将lru list尾部的dirty page刷到磁盘。

同一时刻只会有一个page cleaner线程对同一个LRU list操作。lru list遍历的深度由动态参数innodb_lru_scan_depth决定,用于优化io敏感的场景,默认值1024,设置比默认值小的值可以适应大多数work load,设置过大会影响性能,尤其是在buffer pool足够大的情况。操作过程在LRU_list_mutex的保护下,代码主体在buf_do_LRU_batch其中涉及到unzip_LRU list和LRU list中的淘汰,unzip_LRU list不一定每次都会做淘汰操作,衡量内存大小和负载情况,只有在size超出buffer pool的1/10以及当前负载为io bound的情况才会做。代码主体在buf_free_from_unzip_LRU_list_batch中, 将uncompressed page移除到free list中,并不会将任何数据刷盘,只是将解压缩的frames与压缩page分离。

如果在上述操作后仍无法达到需要释放的page数量(遍历深度),则继续从lru list尾部进行遍历,操作在buf_flush_LRU_list_batchlru list的遍历同样使用了Hazard Pointer,buf_pool_t中的lru_hp。当前page如果是clear并且没有被io fixed和buffer fixed,则从lru list中移除并加入free list中,否则如果page是已经修改过的并且满足flush的条件则对其进行刷脏。

小结

innodb设定了buffer pool的总大小,空闲page不够用时会将lru链表中可替换的页面移到free list中,根据统计信息估计负载情况来决定淘汰的策略。所有的block在几种状态之间进行转换,unzip_LRU、flush list设置一定的上限,设置多个影响淘汰和刷脏策略的参数,以达到不同负载不同buffer pool size下的性能和内存之间的平衡。

change buffer

change buffer是用于缓存不在buffer pool中的二级索引页改动的数据结构,insert、update或者delete这些DML操作会引起buffer的改变,page被读入时与change buffer中的修改合并加入buffer pool中。引入buffer pool的目的主要减少随机io,对于二级索引的更新经常是比较随机的,当页面不在buffer pool中时将其对应的修改缓存在change buffer中可有效地减少磁盘的随机访问。可以通过参数 innodb_change_buffering 设置对应缓存的操作:all, none, inserts, deletes, changes(inserts+deletes), purges。change buffer的内存也是buffer pool中的一部分,可以通过参数innodb_change_buffer_max_size来设置内存占比,默认25%,最多50%。

b8cea55c2d412f34f6f865f89ebc2e13.png

Adaptive Hash Index

innodb的索引组织结构为btree,当查询的时候会根据条件一直索引到叶子节点,为了减少寻路的开销,AHI使用索引键的前缀建立了一个哈希索引表,在实现上就是多个个hash_tables(分片)。哈希索引是为那些频繁被访问的索引页而建立的,可以理解为btree上的索引。看代码初始创建的数组大小为buf_pool_get_curr_size() / sizeof(void *) / 64,其实是比较小的一块内存,使用malloc分配。

log buffer

log buffer是日志未写到磁盘的缓存,大小由参数innodb_log_buffer_size指定,一般来说这块内存都比较小,默认是16M。在有大事务的场景下,在事务未commited之前可以将redo日志数据一直缓存,避免多次写磁盘,可以将log buffer调大。

参考资料:

https://dev.mysql.com/doc/refman/5.6/en/innodb-performance-use_sys_malloc.html

https://dev.mysql.com/doc/refman/8.0/en/innodb-in-memory-structures.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/520982.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

飞天2.0面向万物智能的操作系统正式启幕

原文链接 本文为云栖社区原创内容&#xff0c;未经允许不得转载。

阿里数据总监分享《阿里数据中台建设实践案例》,PPT+语音讲解!

戳蓝字“CSDN云计算”关注我们哦&#xff01;作者 | 技术领导力责编 | 阿秃本文整理自&#xff0c;阿里巴巴集团数据部商业应用总监列文&#xff0c;在“2019年阿里云&#xff08;上海&#xff09;峰会”上的分享&#xff0c;以PPT图片文字语音的方式呈现给各位社区读者。如果…

(Docker实战) 第五篇:建立持续集成环境02

标签&#xff1a; gblfy技术文档 文章目录一、 jenkins实战&#xff0c;持续集成实际项目1.1. 安装maven插件1.1.1. 找到Maven Integration这个插件1.1.2. 确保jenkins的maven本地仓库有一定的权限1.1.3. 创建job1.1.4. 配置参数1.1.5. 配置git仓库信息1.1.7. 配置构建信息1.1.…

阿里云OCR证件识别商业化发布,减少人工审核误差节省成本

随着互联网的发展&#xff0c;利用现代信息技术开展高效快捷便民服务工作&#xff0c;已经势在必行。证件识别算法&#xff0c;极大地提高了办公效率&#xff0c;现已在互联网金融、银行保险、电信通讯运营商、智能交通、政府、航空、社保局等行业领域被广泛运用。 近日&#…

(Docker实战) 第六篇:建立持续集成环境03

标签&#xff1a; gblfy技术文档 文章目录六、 git核心概念6.1. git学习地址6.2. github和码云的介绍6.3. git常用命令和操作6.3.1. 命令行常用命令6.3. IDEA操作方法(具体看视频)6.3.1. 合并分支6.3.2. compare with6.3.3. rename6.3.4. 看历史记录&#xff08;所有的和单个文…

程序员:站在“自学”鄙视链顶端的王者

我在大学的时候&#xff0c;真的遇到一个神人&#xff0c;叫他小马吧。几乎没见过小马上课&#xff0c;第一节实验课就完成全学期所有实验&#xff0c;大一就自学大二课程&#xff0c;大四还没毕业就拿到了阿里offer&#xff0c;然后在我们苦兮兮找工作的时候&#xff0c;人家已…

用户需求源源不断,阿里云网络创新不止

2018杭州云栖大会&#xff0c;阿里云网络产品重点介绍了两个创新产品&#xff0c;智能接入网关和云企业网&#xff0c;以及全球领先的云网络系统&#xff0d;飞天洛神。智能接入网关是业内主要云服务商中第一家提供这样产品的&#xff0c;云企业网更是业内首创的多地域互联产品…

漫画:要跳槽?这道缓存设计题你有必要看看!

戳蓝字“CSDN云计算”关注我们哦&#xff01;作者 | 程序员吴小胖责编 | 阿秃金九银十招聘季&#xff0c;社畜跳槽&#xff0c;学生出笼&#xff0c;也是非常热闹。不过今年继续互联网寒冬&#xff0c;能苟着还是苟着吧&#xff0c;猥琐发育别浪。苟着除了写Bug&#xff0c;还…

阿里云高级技术专家赵伟:安全加速 SCDN 设计与案例

此前&#xff0c;阿里云发布了SCDN安全加速解决方案&#xff0c;在CDN加速的基础上&#xff0c;将专业的安全能力赋能 CDN&#xff0c;实现既有加速又有安全的服务。在本次杭州云栖-飞天技术汇CDN与边缘计算专场中&#xff0c;阿里云高级技术专家赵伟从业务背景、架构设计和客户…

新品发布、降价普惠、拥抱开源、出海全球化 | 杭州云栖企业数字化转型峰会上的那些关键词

9月19日&#xff0c;在杭州云栖大会 - 企业数字化转型峰会现场&#xff0c;阿里巴巴中间件产品总监赵林分享了2018 Aliware的最新产品动态。本文将为您梳理Aliware在出海全球化、开源支持、消息队列高级特性降价、链路追踪新品发布、应用高可用新品发布、CloudToolkit 新品发布…

华为获颁中国首个5G基站设备进网许可证:可支持中国规模部署;IBM推出新一代企业平台Z15;Testin最新AI产品发布……...

关注并标星星CSDN云计算极客头条&#xff1a;速递、最新、绝对有料。这里有企业新动、这里有业界要闻&#xff0c;打起十二分精神&#xff0c;紧跟fashion你可以的&#xff01;每周两次&#xff0c;打卡即read更快、更全了解泛云圈精彩newsgo go go 荣耀V30 5G手机概念图&#…

php数组 函数,PHP array_uintersect_uassoc() 函数

PHP array_uintersect_uassoc() 函数PHP Array 函数定义和用法array_uintersect_uassoc 函数带索引检查计算数组的交集&#xff0c;用回调函数来比较数据和索引。array_uintersect_uassoc() 返回一个数组&#xff0c;该数组包含了所有在 array1 中也同时出现在所有其它参数数组…

p沟道mos管导通条件_打开知识大门的捷径!MOS管基本认识

01三个极的判定G极(gate)—栅极&#xff0c;不用说比较好认 S极(source)—源极&#xff0c;不论是P沟道还是N沟道&#xff0c;两根线相交的就是 D极(drain)—漏极&#xff0c;不论是P沟道还是N沟道&#xff0c;是单独引线的那边02N沟道与P沟道判别箭头指向G极的是N沟道 箭头背向…

大繁至简,首度揭秘阿里云飞天洛神系统

“最好的网络就像神一样&#xff0c;无处不在&#xff0c;又感知不到她的存在 …… 大繁至简&#xff0c;这是我们努力的方向。” – 阿里云网络高级技术专家 孙成浩 2018杭州云栖大会的主会场上&#xff0c;阿里云产品总监何云飞介绍了阿里云自主研发的云操作系统飞天的全…

(Docker实战) 第2篇:Centos7 拉取和部署Gitlab

文章目录搭建gitlab1. 拉取并运行gitlab所需要的redis镜像2. 拉取并运行gitlab所需要的postgresql镜像3. 下载并运行gitlab搭建gitlab 参考&#xff1a;https://github.com/sameersbn/docker-gitlab 1. 拉取并运行gitlab所需要的redis镜像 docker run --name gitlab-redis -d…

redis系列:通过队列案例学习list命令

前言 这一篇文章将讲述Redis中的list类型命令&#xff0c;同样也是通过demo来讲述&#xff0c;其他部分这里就不在赘述了。 项目Github地址&#xff1a;https://github.com/rainbowda/learnWay/tree/master/learnRedis/case-list 案例 demo功能是队列&#xff0c;整个demo的…

2019年程序员薪酬报告:平均年薪超70万!40岁后,这类人不“保值”了

Hired 近日发布了《2019 年度薪酬状况报告》&#xff0c;重点结论如下&#xff1a;仅有 23% 的硕士 / 博士表示&#xff0c;高学历带来了高薪&#xff0c;大部分人表示学历不是全部76% 的技术工作者认为&#xff0c;参加编程培训机构对求职有帮助在美国&#xff0c;技术工作者在…

业务代码解构利器--SWAK

简介 业务的不断发展、商品类型的不断增多、不断添加的业务需求使得闲鱼的代码出现“bad smell”——平台代码和业务代码耦合严重难以分离&#xff1b;业务和业务之间代码交织缺少拆解。这也是行业中的通病。为解决此类问题&#xff0c;闲鱼自研了一套技术框架——SWAK。本文带…

(Docker实战) 第4篇:Centos7 拉取和部署Redis

文章目录搭建redis搭建redis docker run --name redis -di --publish 6379:6379 redis:4.0远程验证测试&#xff1a; 想学习更多微服务、分布式、中间件、数据库、项目快速构建等系列技术 请访问http://gblfy.com 让我们一起进步&#xff01;&#xff01;&#xff01;

阿里巴巴上线静态开源站点搭建工具 Docsite

近日&#xff0c;阿里巴巴在Github上线了静态开源站点搭建工具Docsite&#xff0c;这是一款集官网、文档、博客和社区为一体的静态开源站点的解决方案&#xff0c;具有简单易上手、上手不撒手的特质&#xff0c;同时支持react和静态渲染、PC端和移动端、支持中英文国际化、SEO、…