第43问:锁用得太多, 为什么要调整 Buffer Pool

当我们使用一个事务操作很多数据时, MySQL 有时会报错: The total number of locks exceeds the lock table size

根据官方文档, 我们需要调大 buffer pool 的大小:

本期实验, 我们来探索一下锁用得多与 buffer pool 大小的关系

实验

我们用老方法建一个数据库, 并将 buffer pool 大小调整到了最小值5M, 方便我们复现问题

现在来模拟一个用锁特别多的事务:

我们还是用老方法让表翻倍, 来不停地占用锁.

看一下效果:

我们可以通过 information_schema.INNODB_TRX 来查看事务使用了多少锁, 解释一下上图中标记的这几个状态:

  • trx_tables_locked: 该事务锁了几张表
  • trx_rows_locked: 该事务锁了多少数据行
  • trx_lock_structs: 该事务一共用到了多少个锁结构.一个锁结构用于锁住多个表或多个行
  • trx_lock_memory_bytes: 该事务的锁结构一共用了多少内存

再来看看 buffer pool 的状态:

解释一下 Buffer pool 的这两个状态:

  • total 是 Buffer pool 的总页数
  • misc 是 Buffer pool 中非数据页的页数

我们继续造数据, 让该事务使用的锁越来越多, 再来看看状态:

与最初的状态相比, 该事务使用的锁的内存增长了 (1269968 - 24784 = ) 1245184 字节 = 1216 k, 而 buffer pool 非数据页多使用了 (84-8 = ) 76页, 每页16k, 总共 1216 k

也就是说, 该事务的锁内存均分自 buffer pool .

我们继续造数据, 造到报错为止, 不停查看状态:

可以看到, 发生报错时, 事务会回滚. 回滚前, 该事务的锁占用的内存大小是 3842256 字节, 也就是 ( 3842256 / 1024 / 16 = ) 234.5 页, 此时 buffer pool 使用率大概是 ( 234.5 / 320 = ) 73%

我们的实验到此结束, 通过此实验我们知道:

  1. 如果事务用到了很多锁, 那么锁结构使用的内存是从 Buffer pool 中分配的
  2. Buffer pool 使用率比较高时, 就会报错: The total number of locks exceeds the lock table size

关于 Buffer pool 使用到多少, 会进行报错, 我们参考如下 MySQL 源码:

我们可以看到这个理论阈值是75%, 当 Buffer pool 中 非数据页的大小超过了75%, 我们就会再无法分配更多的锁.
(实验中测定的73%与这个理论阈值相差不大)

小贴士

当 Buffer pool 的大小不足时, Buffer pool 可以回收数据页 (干净页可直接回收, 脏页可刷脏后回收), 而不能直接回收 非数据页.

MySQL 设定这个75%的阈值, 也是为了让 Buffer pool 中留一部分空间给数据页, 毕竟 Buffer pool 的最大作用是给数据页做缓存.



关于 MySQL 的技术内容,你们还有什么想知道的吗?赶紧留言告诉小编吧!

<think> 我们正在讨论GStreamer中动态调整Buffer Pool的方法和原理。根据之前的引用,我们知道Buffer Pool是GStreamer中用于高效管理缓冲区(GstBuffer)的机制。动态调整Buffer Pool指的是在运行时改变Buffer Pool的配置,例如缓冲区的大小、数量等,以适应不同的工作负载或媒体格式变化。 ### 动态调整Buffer Pool的原理 在GStreamer中,Buffer Pool通常由元素(如sink元素)创建和管理。当媒体格式(通过Caps协商确定)发生变化时,可能需要调整Buffer Pool的配置。例如,视频分辨率的变化可能需要改变缓冲区的大小。动态调整的原理如下: 1. **停用Buffer Pool(Deactivate)**:首先需要将当前的Buffer Pool设置为非活动状态(inactive)。在非活动状态下,新的`gst_buffer_pool_acquire_buffer()`调用将返回`GST_FLOW_FLUSHING`,表示无法获取缓冲区。同时,停用操作会等待所有已分配的缓冲区被归还到池中[^3]。 2. **重新配置Buffer Pool**:一旦Buffer Pool被停用,就可以修改其配置。这包括设置新的Caps(媒体格式)、缓冲区大小、最小/最大缓冲区数量等。重新配置通常通过`gst_buffer_pool_set_config()`函数完成。 3. **重新激活Buffer Pool(Activate)**:配置完成后,将Buffer Pool重新设置为活动状态。激活操作会根据新的配置预分配缓冲区(如果配置要求预分配)[^3]。 ### 动态调整的方法 动态调整Buffer Pool通常发生在Caps协商之后,即当元素的`set_caps`方法被调用时。以下是一个典型的流程: 1. **Caps协商**:当两个相连的元素完成Caps协商后,下游元素(如sink)会收到一个确定的Caps(媒体格式)。此时,元素需要检查当前的Buffer Pool是否支持新的Caps,或者是否需要重新配置。 2. **检查并重新配置Buffer Pool**: - 如果元素已经有一个Buffer Pool,它需要停用该池,然后设置新的配置(基于新的Caps),再激活。 - 如果元素还没有Buffer Pool,则创建一个新的Buffer Pool并配置它。 3. **处理缓冲区**:配置完成后,元素可以使用新的Buffer Pool来分配和释放缓冲区。 以下是一个简化的代码示例(基于GStreamer插件开发): ```c static gboolean gst_my_element_set_caps (GstMyElement *self, GstCaps *caps) { /* 1. 解析caps获取需要的参数(如视频宽度、高度等) */ GstVideoInfo info; if (!gst_video_info_from_caps(&info, caps)) { GST_ERROR_OBJECT(self, "Failed to parse caps"); return FALSE; } /* 2. 如果存在已有的Buffer Pool,停用它 */ if (self->pool) { gst_buffer_pool_set_active(self->pool, FALSE); gst_object_unref(self->pool); self->pool = NULL; } /* 3. 创建新的Buffer Pool */ self->pool = gst_video_buffer_pool_new(); /* 4. 配置Buffer Pool */ GstStructure *config = gst_buffer_pool_get_config(self->pool); gst_buffer_pool_config_set_params(config, caps, info.size, 0, 0); if (!gst_buffer_pool_set_config(self->pool, config)) { GST_ERROR_OBJECT(self, "Failed to set config"); return FALSE; } /* 5. 激活Buffer Pool */ if (!gst_buffer_pool_set_active(self->pool, TRUE)) { GST_ERROR_OBJECT(self, "Failed to activate pool"); return FALSE; } return TRUE; } ``` ### 注意事项 - **并发处理**:在停用Buffer Pool时,需要确保所有已分配的缓冲区都被归还。GStreamer内部会等待所有缓冲区归还,因此开发者无需手动等待[^3]。 - **配置参数**:配置Buffer Pool时,通常需要设置以下参数: - Caps:媒体格式。 - 缓冲区大小(size):根据媒体格式计算出的每个缓冲区的大小。 - 最小/最大缓冲区数量:池中缓冲区的最小和最大数量。 - **预分配**:激活Buffer Pool时,如果配置要求预分配(例如设置了最小缓冲区数量),则会预分配这些缓冲区[^3]。 ### 引用说明 - 当缓冲区引用计数归零时,如果它来自Buffer Pool,则会被自动归还到池中[^1]。 - 激活Buffer Pool时会根据配置预分配资源,停用时则在所有缓冲区归还后释放资源[^3]。 ### 总结 动态调整Buffer Pool是GStreamer中处理媒体格式变化的关键机制。通过停用、重新配置和重新激活Buffer Pool,元素能够适应新的媒体格式,并高效地管理缓冲区资源。这一过程通常在Caps协商后触发,并由元素(如sink或解码器)负责执行。 §§相关题§§ 1. GStreamer中如何自定义Buffer Pool? 2. Caps协商失败时如何影响Buffer Pool? 3. 如何监控Buffer Pool的使用情况(如缓冲区分配和归还)? 4. 动态Pad(如decodebin的输出Pad)如何管理Buffer Pool? </think> ### GStreamer动态调整Buffer Pool的方法与原理 Buffer Pool在GStreamer中用于高效管理媒体数据缓冲区(`GstBuffer`)。动态调整的核心原理是**根据运行时需求重新配置Pool参数**,主要涉及以下流程: #### 动态调整原理 1. **按需激活/停用** Buffer Pool仅在需要时激活(`gst_buffer_pool_set_active(TRUE)`),激活时根据配置预分配资源[^3]: ```c gboolean gst_buffer_pool_set_active(GstBufferPool *pool, gboolean active); ``` - 激活时:预分配缓冲区(根据`min_buffers/max_buffers`参数) - 停用时:释放资源(需等待所有缓冲区归还) 2. **引用计数驱动** 缓冲区使用完毕时(引用计数=0),自动触发归还机制[^1]: ```mermaid graph LR A[Buffer引用计数归零] --> B{来自Pool?} B -- 是 --> C[gst_buffer_pool_release_buffer] B -- 否 --> D[直接释放内存] ``` 3. **动态重配置** 当媒体格式变化时(如分辨率改变),需重新配置Pool: ```c GstStructure *config = gst_buffer_pool_get_config(pool); gst_buffer_pool_config_set_params(config, new_caps, new_size, min_buf, max_buf); gst_buffer_pool_set_config(pool, config); // 应用新配置 ``` #### 动态调整方法 1. **Caps变更触发调整** 在元素的`set_caps()`虚函数中响应格式变化: ```c static gboolean my_element_set_caps(GstBaseTransform *trans, GstCaps *incaps, GstCaps *outcaps) { MyElement *self = MY_ELEMENT(trans); // 停用旧Pool if (self->pool) gst_buffer_pool_set_active(self->pool, FALSE); // 创建新Pool并配置 self->pool = gst_video_buffer_pool_new(); GstVideoInfo info; gst_video_info_from_caps(&info, outcaps); GstStructure *config = gst_buffer_pool_get_config(self->pool); gst_buffer_pool_config_set_params(config, outcaps, info.size, 5, 10); // min=5, max=10 gst_buffer_pool_set_config(self->pool, config); // 激活新Pool gst_buffer_pool_set_active(self->pool, TRUE); return TRUE; } ``` 2. **运行时参数调整** 动态修改Pool参数(需先停用): ```c // 获取当前配置 GstStructure *config = gst_buffer_pool_get_config(pool); // 修改参数(如增加缓冲区数量) gint min_buf, max_buf; gst_buffer_pool_config_get_params(config, NULL, NULL, &min_buf, &max_buf); gst_buffer_pool_config_set_params(config, NULL, -1, min_buf+2, max_buf+5); // 应用新配置 gst_buffer_pool_set_active(pool, FALSE); // 先停用 gst_buffer_pool_set_config(pool, config); gst_buffer_pool_set_active(pool, TRUE); // 重新激活 ``` 3. **自适应分配策略** 通过`gst_buffer_pool_acquire_buffer()`动态获取缓冲区: ```c GstFlowReturn ret; GstBuffer *buffer; ret = gst_buffer_pool_acquire_buffer(pool, &buffer, NULL); if (ret == GST_FLOW_OK) { // 成功获取缓冲区 } else if (ret == GST_FLOW_EOS) { // 资源不足时的处理逻辑 // 可在此触发Pool扩容 } ``` #### 关键特性 1. **零拷贝优化** 通过`GstAllocator`与Pool配合,支持DMA-BUF等零拷贝机制[^3]: ```c GstAllocator *allocator = gst_dmabuf_allocator_new(); gst_buffer_pool_config_set_allocator(config, allocator, NULL); ``` 2. **自动回收机制** 当元素释放缓冲区时(`gst_buffer_unref()`),自动检测来源并归还Pool[^1] 3. **线程安全** 所有Pool操作内部实现机制,确保多线程安全 > **应用场景示例**:视频分辨率切换 > 当视频流从1080p切换到4K时: > 1. 触发`set_caps()`接收到新分辨率Caps > 2. 停用旧Pool(等待所有缓冲区归还) > 3. 创建新Pool配置更大缓冲区尺寸 > 4. 激活新Pool继续数据处理
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值