ClickHouse源码分析:磁盘检测机制

举报
ZhjDayDayUp 发表于 2023/01/19 11:16:16 2023/01/19
【摘要】 记录下ClickHouse社区支持的磁盘检测机制

日志分析

磁盘配置如下:
image.png
如果有磁盘检测线程,如果某次检测时间超过200ms,则会打印如下日志:
image.png

如果在出现慢盘或者磁盘io很高的时候,可以看到上述日志。
如果磁盘出现损坏导致不能读写的情况,则会出现“Disk {} is broken”或者“Disk {} is readonly”的日志。

源码分析

ClickHouse版本为v22.3.2-lts

线程启动

入口:

DiskSelectorPtr Context::getDiskSelector(std::lock_guard<std::mutex> & /* lock */) const
{
    if (!shared->merge_tree_disk_selector)
    {
        constexpr auto config_name = "storage_configuration.disks";
        const auto & config = getConfigRef();

        shared->merge_tree_disk_selector = std::make_shared<DiskSelector>(config, config_name, shared_from_this());
    }
    return shared->merge_tree_disk_selector;
}

StoragePolicySelectorPtr Context::getStoragePolicySelector(std::lock_guard<std::mutex> & lock) const
{
    if (!shared->merge_tree_storage_policy_selector)
    {
        constexpr auto config_name = "storage_configuration.policies";
        const auto & config = getConfigRef();

        shared->merge_tree_storage_policy_selector = std::make_shared<StoragePolicySelector>(config, config_name, getDiskSelector(lock));
    }
    return shared->merge_tree_storage_policy_selector;
}

可以看到通过policies和disks的配置来初始化一个StoragePolicySelector和DiskSelector。
其中,DiskSelector的构造函数:

DiskSelector::DiskSelector(const Poco::Util::AbstractConfiguration & config, const String & config_prefix, ContextPtr context)
{
    Poco::Util::AbstractConfiguration::Keys keys;
    config.keys(config_prefix, keys);

    auto & factory = DiskFactory::instance();

    constexpr auto default_disk_name = "default";
    bool has_default_disk = false;
    for (const auto & disk_name : keys) // 遍历所有配置的disk,即:disk_name_1,disk_name_2,default
    {
        if (!std::all_of(disk_name.begin(), disk_name.end(), isWordCharASCII))
            throw Exception("Disk name can contain only alphanumeric and '_' (" + disk_name + ")", ErrorCodes::EXCESSIVE_ELEMENT_IN_CONFIG);

        if (disk_name == default_disk_name)
            has_default_disk = true;

        auto disk_config_prefix = config_prefix + "." + disk_name;

        disks.emplace(disk_name, factory.create(disk_name, config, disk_config_prefix, context, disks));
    }
    if (!has_default_disk)  // 如果在disk中没有配置default,则将default带进去
    {
        disks.emplace(
            default_disk_name,
            std::make_shared<DiskLocal>(
                default_disk_name, context->getPath(), 0, context, config.getUInt("local_disk_check_period_ms", 0)));
    }
}

代码逻辑很清晰,就是解析disks的配置,根据路径和名称创建DiskLocal。
如果在disks没有创建default的目录,则会使用config.xml中path指定的路径进行初始化。

这里注意下factory.create的调用。

factory.create --> DiskFactory::create --> registerDiskLocal中的creator(在启动阶段就注册了此接口) --> DiskLocal::startup --> DiskLocalCheckThread::startup(线程启动) --> DiskLocalCheckThread::run

其实也是初始化DiskLocal对象。但是,区别是,在creator中调用了disk->startup,直接启动了磁盘检测线程。
也就是说,如果走上面if (!has_default_disk) 分支的话,对应的磁盘是没有检测线程的

检测机制

在disk路径下,会有一个用于磁盘读写能力检测的文件:
image.png

void DiskLocalCheckThread::run()
{
    if (need_stop)
        return;

    bool can_read = disk->canRead();
    bool can_write = disk->canWrite();
    ...
}

启动时,如果该文件不存在,则会新建,然后写入魔鬼数字。
检测阶段,定期读此文件的内容,校验内容是否有变更。如果不变,则认为磁盘是可读的。

bool DiskLocal::canRead() const noexcept
{
    if (FS::canRead(fs::path(disk_path) / disk_checker_path))
    {
        auto magic_number = readDiskCheckerMagicNumber();
        if (magic_number && *magic_number == disk_checker_magic_number)
            return true;
    }
    return false;
}

检测阶段,通过写此目录下的临时文件,来校验磁盘是否可写。

bool DiskLocal::canWrite() const noexcept
{
    static DiskWriteCheckData data;
    String tmp_template = fs::path(disk_path) / "";
    {
        auto buf = WriteBufferFromTemporaryFile::create(tmp_template);
        buf->write(data.data, data.PAGE_SIZE_IN_BYTES);
        buf->sync();
    }
    return true;
}

临时文件在创建后会立马删除(通过Poco::TemporaryFile来实现),文件名类似下面:
image.png

调用堆栈

如下堆栈是在DiskLocal构造函数中打断点,以便查看磁盘检测相关的类是如何一步一步调用到的。
见附件

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。