我的编程空间,编程开发者的网络收藏夹
学习永远不晚

怎么理解PostgreSQL中Clock Sweep算法

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

怎么理解PostgreSQL中Clock Sweep算法

本篇内容介绍了“怎么理解PostgreSQL中Clock Sweep算法”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

一、数据结构

BufferDesc
共享缓冲区的共享描述符(状态)数据


//buffer header锁定
#define BM_LOCKED               (1U << 22)  
//数据需要写入(标记为DIRTY)
#define BM_DIRTY                (1U << 23)  
//数据是有效的
#define BM_VALID                (1U << 24)  
//已分配buffer tag
#define BM_TAG_VALID            (1U << 25)  
//正在R/W
#define BM_IO_IN_PROGRESS       (1U << 26)  
//上一个I/O出现错误
#define BM_IO_ERROR             (1U << 27)  
//开始写则变DIRTY
#define BM_JUST_DIRTIED         (1U << 28)  
//存在等待sole pin的其他进程
#define BM_PIN_COUNT_WAITER     (1U << 29)  
//checkpoint发生,必须刷到磁盘上
#define BM_CHECKPOINT_NEEDED    (1U << 30)  
//持久化buffer(不是unlogged或者初始化fork)
#define BM_PERMANENT            (1U << 31)  

typedef struct BufferDesc
{
    //buffer tag
    BufferTag   tag;            
    //buffer索引编号(0开始),指向相应的buffer pool slot
    int         buf_id;         
    
    //tag状态,包括flags/refcount和usagecount
    pg_atomic_uint32 state;
    //pin-count等待进程ID
    int         wait_backend_pid;   
    //空闲链表链中下一个空闲的buffer
    int         freeNext;       
    //缓冲区内容锁
    LWLock      content_lock;   
} BufferDesc;

BufferTag
Buffer tag标记了buffer存储的是磁盘中哪个block


typedef struct buftag
{
    //物理relation标识符
    RelFileNode rnode;          
    ForkNumber  forkNum;
    //相对于relation起始的块号
    BlockNumber blockNum;       
} BufferTag;

二、源码解读

算法思想,在 Buffer Manager 中已有详细介绍,摘录如下:

WHILE true
(1)     Obtain the candidate buffer descriptor pointed by the nextVictimBuffer
(2)     IF the candidate descriptor is unpinned THEN
(3)        IF the candidate descriptor's usage_count == 0 THEN
                BREAK WHILE LOOP  
           ELSE
            Decrease the candidate descriptpor's usage_count by 1
               END IF
         END IF
(4)     Advance nextVictimBuffer to the next one
      END WHILE 
(5) RETURN buffer_id of the victim

算法思想朴素且简单,比起高大上的LRU算法要简单多了.
nextVictimBuffer可视为时钟的时针,把缓冲区视为环形缓冲区,时针循环周而往复的循环,如缓冲区满足unpinned(ref_count == 0) && usage_count == 0的条件,则选中该缓冲,否则,usage_count减一,继续循环,直至找到满足条件的buffer为止.选中的buffer一定是buffers中较少使用的那个.

StrategyGetBuffer中的代码片段:

...
    
    //空闲链表中找不到或者满足不了条件,则执行"clock sweep"算法
    //int NBuffers = 1000;
    trycounter = NBuffers;//尝试次数
    for (;;)
    {
        //------- 循环
        //获取buffer描述符
        buf = GetBufferDescriptor(ClockSweepTick());
        
        local_buf_state = LockBufHdr(buf);
        if (BUF_STATE_GET_REFCOUNT(local_buf_state) == 0)
        {
            //----- refcount == 0
            if (BUF_STATE_GET_USAGECOUNT(local_buf_state) != 0)
            {
                //usage_count <> 0
                //usage_count - 1
                local_buf_state -= BUF_USAGECOUNT_ONE;
                //重置尝试次数
                trycounter = NBuffers;
            }
            else
            {
                //usage_count = 0
                
                //发现一个可用的buffer
                if (strategy != NULL)
                    //添加到该策略的环形缓冲区中
                    AddBufferToRing(strategy, buf);
                //输出参数赋值
                *buf_state = local_buf_state;
                //返回buf
                return buf;
            }
        }
        else if (--trycounter == 0)
        {
            //----- refcount <> 0 && --trycounter == 0
            
            UnlockBufHdr(buf, local_buf_state);
            elog(ERROR, "no unpinned buffers available");
        }
        //解锁buffer header
        UnlockBufHdr(buf, local_buf_state);
    }

ClockSweepTick()函数是StrategyGetBuffer()的辅助过程,移动时针(当前位置往前一格),返回时针指向的buffer.
其实现逻辑如下:


static inline uint32
ClockSweepTick(void)
{
    uint32      victim;
    
    victim =
        pg_atomic_fetch_add_u32(&StrategyControl->nextVictimBuffer, 1);
    if (victim >= NBuffers)
    {
        //-------- 候选buffer大于NBuffers
        //记录元素的victim
        uint32      originalVictim = victim;
        
        //回卷BufferDescriptors中查找的内容
        victim = victim % NBuffers;
        
        if (victim == 0)
        {
            //出现回卷
            uint32      expected;//期望值(不考虑回卷)
            uint32      wrapped;//回卷后的值
            bool        success = false;//成功标记
            //期望值
            expected = originalVictim + 1;
            while (!success)
            {
                
                SpinLockAcquire(&StrategyControl->buffer_strategy_lock);
                //获取回卷数
                wrapped = expected % NBuffers;
                //原子比较并交换数字
                success = pg_atomic_compare_exchange_u32(&StrategyControl->nextVictimBuffer,
                                                         &expected, wrapped);
                if (success)
                    //如成功,则增加计数
                    StrategyControl->completePasses++;
                //释放自旋锁
                SpinLockRelease(&StrategyControl->buffer_strategy_lock);
            }
        }
    }
    //返回victim
    return victim;
}

static inline bool
pg_atomic_compare_exchange_u32(volatile pg_atomic_uint32 *ptr,
                               uint32 *expected, uint32 newval)
{
    AssertPointerAlignment(ptr, 4);
    AssertPointerAlignment(expected, 4);
    return pg_atomic_compare_exchange_u32_impl(ptr, expected, newval);
}
bool
pg_atomic_compare_exchange_u32_impl(volatile pg_atomic_uint32 *ptr,
                                    uint32 *expected, uint32 newval)
{
    bool        ret;
    
    SpinLockAcquire((slock_t *) &ptr->sema);
    
    //执行比较/交换逻辑
    ret = ptr->value == *expected;//ptr与*expected是否一致?
    *expected = ptr->value;/
    //释放自旋锁
    SpinLockRelease((slock_t *) &ptr->sema);
    //返回结果
    return ret;
}

“怎么理解PostgreSQL中Clock Sweep算法”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注亿速云网站,小编将为大家输出更多高质量的实用文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

怎么理解PostgreSQL中Clock Sweep算法

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

怎么理解PostgreSQL中session hang情况

这篇文章主要介绍“怎么理解PostgreSQL中session hang情况”,在日常操作中,相信很多人在怎么理解PostgreSQL中session hang情况问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答
2022-11-30

LRU算法怎么理解

本篇内容介绍了“LRU算法怎么理解”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!01、前言我们常用缓存提升数据查询速度,由于缓存容量有限,当
2023-06-16

web算法复杂度怎么理解

本篇内容介绍了“web算法复杂度怎么理解”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!算法学(Algorithmics)是设计和研究算法的科
2023-06-03

怎么理解Java算法复杂度

本篇内容主要讲解“怎么理解Java算法复杂度”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“怎么理解Java算法复杂度”吧!大O符号衡量时间复杂度通常使用”大O符号“。什么是大O符号?我们需要先看
2023-06-02

怎么理解散列算法在C# 加密中的应用

怎么理解散列算法在C# 加密中的应用,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。散列算法是C# 加密中经常会用到的方法,那么什么是散列算法呢?它的作用是如何实现的呢?那么
2023-06-17

JavaScript数据结构与算法怎么理解

本篇内容主要讲解“JavaScript数据结构与算法怎么理解”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“JavaScript数据结构与算法怎么理解”吧!前言数据结构与算法这个词相信大家都听过、
2023-07-02

C#中的小数运算怎么理解

这篇文章主要介绍“C#中的小数运算怎么理解”,在日常操作中,相信很多人在C#中的小数运算怎么理解问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”C#中的小数运算怎么理解”的疑惑有所帮助!接下来,请跟着小编一起来
2023-06-17

Lua中的三目运算怎么理解

这篇文章主要讲解了“Lua中的三目运算怎么理解”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Lua中的三目运算怎么理解”吧!Lua 是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开
2023-06-27

怎么深入解析Vue3中的diff 算法

今天给大家介绍一下怎么深入解析Vue3中的diff 算法。文章的内容小编觉得不错,现在给大家分享一下,觉得有需要的朋友可以了解一下,希望对大家有所帮助,下面跟着小编的思路一起来阅读吧。1.0 diff 无key子节点在处理被标记为UNKE
2023-06-26

Python中怎么理解yield from语法

本篇内容介绍了“Python中怎么理解yield from语法”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!. 为什么要使用协程在上一篇中,
2023-06-01

编程热搜

目录