大数据ClickHouse进阶(二十五):ClickHouse熔断机制

2022-11-15 00:13:41 浏览数 (2)

​ClickHouse熔断机制

在ClickHouse中熔断机制是限制资源被过度使用的一种保护机制,当使用的资源数量达到阈值时,正在进行的操作会被自动中断,按照使用资源统计方式不同,熔断机制分为两类。

一、根据时间周期的累计用量熔断

这种方式下,系统资源的用量是按照时间周期累计统计,当累计量达到阈值,则直到下个计算周期开始之前,该用户将无法继续进行操作。可以通过users.xml中<quotas>标签来定义资源配额,可配配置项如下:

代码语言:javascript复制
<quotas>
	<default><!-- 自定义名称 -->
		<interval>
			<duration>3600</duration><!-- 时间周期,单位:秒 -->
			<queries>0</queries>
			<erros>0</erros>
			<result_rows>0</result_rows>
			<read_rows>0</read_rows>
			<execution_time>0</execution_time>
		</interval>
	</default>
</quotas>

以上配置中各项解释如下:

  • default:表示自定义名称,全局唯一。
  • duration:表示累积的时间周期,单位是秒。
  • queries:表示在周期内允许执行的查询次数,0表示不限制。
  • errors:表示在周期内允许发生异常的次数,0表示不限制。
  • result_row:表示在周期内允许查询返回的结果行数,0表示不限制。
  • read_rows:表示在周期内在分布式查询中,允许远端节点读取的数据行数,0表示不限制。
  • execution_time:表示周期内允许执行的查询时间,单位是秒,0表示不限制。

现在测试资源配额,我们在users.xml中<quotas>标签下新建一组<limit_1>资源配额,配置如下:

代码语言:javascript复制
<limit_1><!-- 自定义名称 -->
	<interval>
		<duration>3600</duration><!-- 时间周期,单位:秒 -->
		<queries>5</queries>
		<errors>5</errors>
		<result_rows>0</result_rows>
		<read_rows>0</read_rows>
		<execution_time>3600</execution_time>
	</interval>
</limit_1>

以上配置在1小时的周期内只允许5次查询。同时将此组资源配置分配给用户zhangsan:

代码语言:javascript复制
<zhangsan>
	... ...
	<quota>limit_1</quota>
</zhangsan>

配置完成后,使用zhangsan登录clickhoue,执行如下查询测试:

代码语言:javascript复制
node1 :) select * from person_info;
注意:以上查询连续执行5次,会报错:
DB::Exception: Quota for user `zhangsan` for 3600s has been exceeded: queries = 6/5. Interval will end at 2021-10-28 17:00:00. Name of quota template: `limit_1`. (QUOTA_EXPIRED)

经过测试课件熔断机制已经生效。

至此,用户users.xml中的如下:

代码语言:javascript复制
<?xml version="1.0"?>
<yandex>
    <!-- See also the files in users.d directory where the settings can be overridden. -->

    <!-- Profiles of settings. -->
    <profiles>
        <!-- Default settings. -->
        <default>
            <!-- Maximum memory usage for processing single query, in bytes. -->
            <max_memory_usage>10000000000</max_memory_usage>
	    <distributed_product_mode>allow</distributed_product_mode>
            <!-- How to choose between replicas during distributed query processing.
                 random - choose random replica from set of replicas with minimum number of errors
                 nearest_hostname - from set of replicas with minimum number of errors, choose replica
                  with minimum number of different symbols between replica's hostname and local hostname
                  (Hamming distance).
                 in_order - first live replica is chosen in specified order.
                 first_or_random - if first replica one has higher number of errors, pick a random one from replicas with minimum number of errors.
            -->
            <load_balancing>random</load_balancing>
	    <constraints><!--配置约束-->
	        <max_memory_usage>
		    <min>5000000000</min>
		    <max>20000000000</max>
		</max_memory_usage>
		<distributed_product_mode>
		    <readonly/>
		</distributed_product_mode>
	    </constraints>
        </default>
        <normal>			
             <readonly>1</readonly>
      	     <allow_ddl>0</allow_ddl>
	</normal>

        <!-- Profile that allows only read queries. -->
        <readonly>
            <readonly>1</readonly>
        </readonly>
    </profiles>

    <!-- Users and ACL. -->
    <users>
        <!-- If user name was not specified, 'default' user is used. -->
        <default>
            <!-- See also the files in users.d directory where the password can be overridden.

                 Password could be specified in plaintext or in SHA256 (in hex format).

                 If you want to specify password in plaintext (not recommended), place it in 'password' element.
                 Example: <password>qwerty</password>.
                 Password could be empty.

                 If you want to specify SHA256, place it in 'password_sha256_hex' element.
                 Example: <password_sha256_hex>65e84be33532fb784c48129675f9eff3a682b27168c0ea744b2cf58ee02337c5</password_sha256_hex>
                 Restrictions of SHA256: impossibility to connect to ClickHouse using MySQL JS client (as of July 2019).

                 If you want to specify double SHA1, place it in 'password_double_sha1_hex' element.
                 Example: <password_double_sha1_hex>e395796d6546b1b65db9d665cd43f0e858dd4303</password_double_sha1_hex>

                 If you want to specify a previously defined LDAP server (see 'ldap_servers' in the main config) for authentication,
                  place its name in 'server' element inside 'ldap' element.
                 Example: <ldap><server>my_ldap_server</server></ldap>

                 If you want to authenticate the user via Kerberos (assuming Kerberos is enabled, see 'kerberos' in the main config),
                  place 'kerberos' element instead of 'password' (and similar) elements.
                 The name part of the canonical principal name of the initiator must match the user name for authentication to succeed.
                 You can also place 'realm' element inside 'kerberos' element to further restrict authentication to only those requests
                  whose initiator's realm matches it.
                 Example: <kerberos />
                 Example: <kerberos><realm>EXAMPLE.COM</realm></kerberos>

                 How to generate decent password:
                 Execute: PASSWORD=$(base64 < /dev/urandom | head -c8); echo "$PASSWORD"; echo -n "$PASSWORD" | sha256sum | tr -d '-'
                 In first line will be password and in second - corresponding SHA256.

                 How to generate double SHA1:
                 Execute: PASSWORD=$(base64 < /dev/urandom | head -c8); echo "$PASSWORD"; echo -n "$PASSWORD" | sha1sum | tr -d '-' | xxd -r -p | sha1sum | tr -d '-'
                 In first line will be password and in second - corresponding double SHA1.
            -->
            <password></password>

            <!-- List of networks with open access.

                 To open access from everywhere, specify:
                    <ip>::/0</ip>

                 To open access only from localhost, specify:
                    <ip>::1</ip>
                    <ip>127.0.0.1</ip>

                 Each element of list has one of the following forms:
                 <ip> IP-address or network mask. Examples: 213.180.204.3 or 10.0.0.1/8 or 10.0.0.1/255.255.255.0
                     2a02:6b8::3 or 2a02:6b8::3/64 or 2a02:6b8::3/ffff:ffff:ffff:ffff::.
                 <host> Hostname. Example: server01.yandex.ru.
                     To check access, DNS query is performed, and all received addresses compared to peer address.
                 <host_regexp> Regular expression for host names. Example, ^serverdd-dd-d.yandex.ru$
                     To check access, DNS PTR query is performed for peer address and then regexp is applied.
                     Then, for result of PTR query, another DNS query is performed and all received addresses compared to peer address.
                     Strongly recommended that regexp is ends with $
                 All results of DNS requests are cached till server restart.
            -->
            <networks>
                <ip>::/0</ip>
            </networks>

            <!-- Settings profile for user. -->
            <profile>default</profile>

            <!-- Quota for user. -->
            <quota>default</quota>

            <!-- User can create other users and grant rights to them. -->
            <!-- <access_management>1</access_management> -->
        </default>
        <zhangsan>
	    <!-- 设置密码 -->
            <password>123456</password>
			
	    <!-- 设置用户允许登录的网络地址 -->
	    <networks>
	    <!-- 表示任意节点登录 -->
	    <ip>::/0</ip>
	    </networks>
	    <!-- 用户使用的profile 角色 -->
	    <profile>normal</profile>
	    
	    <!-- 设置熔断机制 -->
	    <quota>limit_1</quota>	
            <!-- 设置用户访问数据库 -->
	    <allow_databases>
	        <database>default</database>
		<database>datasets</database>
	    </allow_databases>
	    <databases>
		<default><!-- 数据库名称 -->
		    <person_info><!-- 表名称 -->
			<filter>id&lt;=2 or id=4 </filter> <!-- 数据过滤条件 -->
                    </person_info>
		</default>
	    </databases>
	</zhangsan>
    </users>

    <!-- Quotas. -->
    <quotas>
        <!-- Name of quota. -->
        <default>
            <!-- Limits for time interval. You could specify many intervals with different limits. -->
            <interval>
                <!-- Length of interval. -->
                <duration>3600</duration>

                <!-- No limits. Just calculate resource usage for time interval. -->
                <queries>0</queries>
                <errors>0</errors>
                <result_rows>0</result_rows>
                <read_rows>0</read_rows>
                <execution_time>0</execution_time>
            </interval>
        </default>
	<limit_1><!-- 自定义名称 -->
	    <interval>
		<duration>3600</duration><!-- 时间周期,单位:秒 -->
		<queries>5</queries>
		<errors>5</errors>
		<result_rows>0</result_rows>
		<read_rows>0</read_rows>
		<execution_time>3600</execution_time>
	    </interval>
	</limit_1>
    </quotas>
</yandex>

二、根据单次查询的用量熔断

这种情况下,系统资源的用量是按照单词查询统计的,而具体的熔断规则是由许多不同配置项组成,这些配置项需要配置在用户对应的profile角色中,如果某次查询使用的资源用量达到了阈值,则会被中断。例如:

以上例子中配置项“max_memory_usage”限定了单词查询可以使用的内存量,默认情况下规定不能超过10GB,如果一次查询的内存用量超过10G则会报错。需要注意的是,在单词查询的内存用量统计中,ClickHouse是以分区为最小单元进行统计,不是以总的数据总量统计,这意味着单次查询的实际内存用量是有可能超过阈值的。

下面例举一些配置的角色中常用的配置项:

1、max_memory_usage

在单个ClickHouse服务进程中,运行一次查询限制使用的最大内存量,默认值为10GB。

2、max_memory_usage_for_user

在单个ClickHouse服务进程中,以用户为单位进行统计,单个用户在运行查询时限制使用的最大内存量,默认值为0,即不做限制。

3、max_memory_usage_for_all_queries

在单个ClickHouse服务进程中,所有运行的查询累计加在一起所限制使用的最大内存量,默认为0,即不做限制。

4、max_partitions_per_insert_block

在单次Insert写入的时候,限制创建的最大分区个数,默认值为100个,如果超出阈值,会出现异常。

5、max_rows_to_group_by

在执行Group By聚合查询时,限制去重后聚合Key的最大个数,默认值为0,即不做限制。当超过阈值时,其处理方式由group_by_overflow_mode参数决定。

6、group_by_overflow_mode

当max_rows_to_group_by熔断规则触发时,group_by_overflow_mode将会提供三种护理方式。

  • throw:抛出异常,默认值。
  • break:立即停止查询,并返回当前数据。
  • any:仅根据当前已存在的聚合key继续完成聚合查询。

7、max_bytes_before_external_group_by

在执行group by聚合查询的时候,限制使用的最大内存量,默认值为0,不做限制,当超过阈值时,聚合查询将会使用本地磁盘。

8、join_use_nulls

当两张表进行join操作时,如果左表中的记录在右表中不存在,那么右表相应字段会返回NULL,如果配置join_use_nulls为1,那么对应字段会返回该字段相应数据类型的默认值,此值默认为0,即在右表找不到对应数据时返回NULL。

0 人点赞