列式存储
ClickHouse是一个用于联机分析(OLAP)的列式数据库管理系统(DBMS)。
在传统的行式数据库系统中,数据按如下顺序存储:
处于同一行中的数据总是被物理的存储在一起。
在列式数据库系统中,数据按如下的顺序存储:
列式数据库总是将同一列的数据存储在一起,不同列的数据也总是分开存储。
采用行式存储时,数据在磁盘上的组织结构为:
行存储的好处是想查某个人所有的属性时,可以通过一次磁盘查找加顺序读取就可以。但是当想查所有人的年龄时,需要不停的查找,或者全表扫描才行,遍历的很多数据都是不需要的。
采用列式存储时,数据在磁盘上的组织结构为:
这时想查所有人的年龄只需把年龄那一列拿出来就可以了,只读取查询所需的列,减少I/O消耗。
也就是说,在行存模式下,数据按行连续存储,所有列的数据都存储在一个block中,不参与计算的列在IO时也要全部读出,IO就会增加。而ClickHouse支持在建表时,指定将数据按照某些列进行sort by。排序后,保证了相同sort key的数据在磁盘上连续存储,且有序摆放。在进行等值、范围查询时,where条件命中的数据都紧密存储在一个或若干个连续的Block中,而不是分散的存储在任意多个Block, 大幅减少需要IO的block数量。
Clickhouse是以数据列的方式进行组织存储的,每个列字段都拥有独立的.bin数据文件,并以列字段的名称命名。
如果需要计算people全表的平均年龄的话,ClickHouse只需要读取age.bin文件中的数据进行计算即可。这样一来,假设people表中有20个字段,对于计算出表中所有人的平均年龄的需求,ClickHouse所需要读取的数据量只有MySQL InnoDB的大约1/20
列式存储的好处
- 对于列的聚合、计数、求和等统计操作原因优于行式存储;
- 由于某一列的数据类型都是相同的,针对于数据存储更容易进行数据压缩,每一列选择更优的数据压缩算法,大大提高了数据的压缩比重,更高的压缩比意味着更小的data size,从磁盘中读取相应数据耗时更短;
- 由于数据压缩比更好,一方面节省了磁盘空间,另一方面对于cache也有了更大的发挥空间
OLAP 场景
OLAP(联机分析处理)是一种用于多维数据分析的技术。
- 销售分析:OLAP可以帮助企业对销售数据进行多维分析,包括按时间、地区、产品等多个维度进行分析,帮助企业发现销售趋势、热门产品、优势地区等信息,从而优化销售策略。
- 财务分析:企业可以利用OLAP对财务数据进行多维分析,包括利润、成本、收入等指标,通过不同的维度进行分析,发现财务运营中的问题和机会,指导财务决策。
- 客户分析:OLAP可以帮助企业对客户数据进行多维分析,包括客户偏好、消费习惯、地域分布等,通过多维分析可以更好地了解客户群体,提高客户满意度和忠诚度。
- 库存分析:企业可以利用OLAP对库存数据进行多维分析,包括库存周转率、库存成本、库存结构等,通过多维分析可以优化库存管理,减少库存成本,提高资金利用效率。
- 人力资源分析:OLAP可以帮助企业对人力资源数据进行多维分析,包括员工绩效、福利待遇、培训需求等,通过多维分析可以优化人力资源配置,提高员工满意度和绩效。
OLAP场景的关键特征:
- 大多数是读请求
- 不修改已添加的数据
- 每次查询都从数据库中读取大量的行,但是同时又仅需要少量的列
- 宽表,即每个表包含着大量的列
- 较少的查询频率(通常每台服务器每秒数百个查询或更少)
- 处理单个查询时需要高吞吐量(每个服务器每秒高达数十亿行)
- 事务不是必须的
- 对数据一致性要求低
- 查询结果明显小于源数据,换句话说,数据被过滤或聚合后能够被盛放在单台服务器的内存中
列式数据库更适合OLAP场景的原因
- 针对分析类查询,通常只需要读取表的一小部分列。在列式数据库中你可以只读取你需要的数据。例如,如果只需要读取100列中的5列,这将帮助你最少减少20倍的I/O消耗。
ClickHouse主要的应用场景:
- 大规模数据聚合:ClickHouse的列式存储和向量化查询执行使其在处理大规模数据聚合时表现出色。例如,在网络流量分析和金融交易数据汇总中,ClickHouse能够快速生成实时分析报告。
- 实时分析和监控:由于ClickHouse支持实时数据更新和低延迟查询,它非常适合用于实时分析和监控系统。例如,它可以用于监控云服务的性能指标,快速发现和响应潜在问题。
- 商业智能(BI):ClickHouse的高压缩率和快速查询能力使其成为商业智能平台的理想选择。企业可以利用ClickHouse进行复杂的数据分析,以支持决策制定。
- 日志分析:ClickHouse常用于日志数据的分析,能够处理和分析大量的日志数据,帮助企业从中提取有价值的信息。
- 物联网(IoT):在IoT场景中,ClickHouse可以处理来自传感器和设备的大量时序数据,支持设备监控和性能分析。
- 数据仓库:ClickHouse可以作为数据仓库的存储和分析引擎,提供高效的数据查询和报表生成功能。
ck 中核心概念
表分区(Partition)
表中的数据可以按照指定的字段分区存储,每个分区在文件系统中都是都以目录的形式存在。常用时间字段作为分区字段,数据量大的表可以按照小时分区,数据量小的表可以在按照天分区或者月分区,查询时,使用分区字段作为Where条件,可以有效的过滤掉大量非结果集数据。
分片(Shard)
一个分片本身就是ClickHouse一个实例节点,分片的本质就是为了提高查询效率,将一份全量的数据分成多份(片),从而降低单节点的数据扫描数量,提高查询性能。
复制集(Replication)
简单理解就是相同的数据备份,在CK中通过复制集,实现保障了数据可靠性外,也通过多副本的方式,增加了CK查询的并发能力。
集群(Cluster)
可以使用多个ClickHouse实例组成一个集群,并统一对外提供服务。
CK 的架构
HDFS、Spark、HBase和Elasticsearch这类分布式系统,都采用了Master-Slave主从架构,由一个管控节点作为Leader统筹全局。而ClickHouse则采用Multi-Master多主架构,集群中的每个节点角色对等,客户端访问任意一个节点都能得到相同的效果。这种多主的架构有许多优势,例如对等的角色使系统架构变得更加简单,不用再区分主控节点、数据节点和计算节点,集群中的所有节点功能相同。所以它天然规避了单点故障的问题,非常适合用于多数据中心、异地多活的场景。
MPP 架构
ClickHouse将数据划分为多个partition,每个partition再进一步划分为多个index granularity(索引粒度),然后通过多个CPU核心分别处理其中的一部分来实现并行数据处理。在这种设计下,单条Query就能利用整机所有CPU。极致的并行处理能力,极大的降低了查询延时所以,ClickHouse即使对于大量数据的查询也能够化整为零平行处理。
但是有一个弊端就是对于单条查询使用多cpu,就不利于同时并发多条查询。所以对于高qps的查询业务,ClickHouse并不是强项。
无共享架构:ClickHouse的MPP架构是一种无共享(Share-Nothing)架构,每个节点独立处理自己的数据,节点之间通过高速网络互联。这种架构使得ClickHouse能够线性扩展,理论上可以无限扩展节点数量。
分布式查询处理:在MPP架构下,ClickHouse能够将查询任务分解为多个子任务,并在多个节点上并行执行。每个节点完成自己的子任务后,结果被汇总到发起查询的节点上。这种设计使得ClickHouse在处理大规模数据集时能够实现快速响应。
容错和高可用:ClickHouse的MPP架构提供了容错和高可用性。即使某个节点发生故障,其他节点仍然可以继续处理查询,从而保证服务的连续性。
ClickHouse的特性
ClickHouse不单单是一个列式数据库, 它是一个数据库管理系统。因为它允许在运行时创建表和数据库、加载数据和运行查询,而无需重新配置或重启服务。
存储结构
ClickHouse中的数据文件和索引文件是分开存储的。每个列的数据被存储在单独的文件中,而主键索引则用于快速定位数据。这种设计使得数据插入和查询更加高效,尤其是在大规模数据集上。
数据压缩
在一些列式数据库管理系统中(例如:InfiniDB CE 和 MonetDB) 并没有使用数据压缩。但是, 若想达到比较优异的性能,数据压缩确实起到了至关重要的作用。
数据的磁盘存储
许多的列式数据库(如 SAP HANA, Google PowerDrill)只能在内存中工作,这种方式会造成比实际更多的设备预算。ClickHouse被设计用于工作在传统磁盘上的系统,它提供每GB更低的存储成本,但如果有可以使用SSD和内存,它也会合理的利用这些资源。
多核心并行处理
ClickHouse会使用服务器上一切可用的资源,从而以最自然的方式并行处理大型查询。
数据分片
上面提到的列式数据库管理系统中,几乎没有一个支持分布式的查询处理。
在ClickHouse中,数据可以保存在不同的shard上,每一个shard都由一组用于容错的replica组成,查询可以并行地在所有shard上进行处理。这些对用户来说是透明的。
支持SQL
ClickHouse支持基于SQL的声明式查询语言,该语言大部分情况下是与SQL标准兼容的。
支持的查询包括 GROUP BY,ORDER BY,IN,JOIN以及非相关子查询。但不支持窗口函数和相关子查询,也不支持事务。
实时的数据更新
ClickHouse支持在表中定义主键。为了使查询能够快速在主键中进行范围查找,数据总是以增量的方式有序的存储在MergeTree中。因此,数据可以持续不断地高效的写入到表中,并且写入的过程中不会存在任何加锁的行为。
索引
按照主键对数据进行排序,这将帮助ClickHouse在几十毫秒以内完成对数据特定值或范围的查找。
适合在线查询
在线查询意味着在没有对数据做任何预处理的情况下以极低的延迟处理查询并将结果加载到用户的页面中。
支持数据复制和数据完整性
ClickHouse使用异步的多主复制技术。当数据被写入任何一个可用副本后,系统会在后台将数据分发给其他副本,以保证系统在不同副本上保持相同的数据。
多样化引擎
ClickHouse和MySQL类似,把表级的存储引擎插件化,根据表的不同需求可以设定不同的存储引擎。目前包括合并树、日志、接口和其他四大类20多种引擎
数据分区与线程级并行
ClickHouse将数据划分为多个partition,每个partition再进一步划分为多个index granularity(索引粒度),然后通过多个CPU核心分别处理其中的一部分来实现并行数据处理。在这种设计下,单条Query就能利用整机所有CPU。极致的并行处理能力,极大的降低了查询延时。所以,ClickHouse即使对于大量数据的查询也能够化整为零平行处理。但是有一个弊端就是对于单条查询使用多cpu,就不利于同时并发多条查询。所以对于高qps的查询业务,ClickHouse并不是强项。
向量化查询执行
ClickHouse的向量化查询执行是其高性能的关键因素之一。通过利用现代CPU的SIMD指令集,ClickHouse能够一次性处理数据列的多个值,而不是逐行处理。
简单理解就是消除程序循环的优化,堆机器加快速度,数据级并行。
如果是行存储,对每一行数据都要调用相应的函数,函数调用开销占比高。
而列存储,对内存中的列式数据,一个batch调用一次SIMD指令(而非每一行调用一次),即对一组数据执行相同的一个指令,减少了函数调用次数,而且可以充分发挥SIMD指令的并行能力,大幅缩短了计算耗时。向量执行引擎,通常能够带来数倍的性能提升。
为了实现向量化执行,需要利用CPU的SIMD指令。SIMD的全称是Single Instruction Multiple Data,即用单条指令操作多条数据。它的原理是在CPU寄存器层面实现数据的并行操作。
各硬件访问速度对比,如下图:
- SIMD指令集:ClickHouse利用CPU的SIMD指令集,通过向量化执行引擎,可以一次性对多个数据进行操作,大大提高了数据处理的并行度和效率。
- 查询性能提升:向量化查询执行通常能够带来数倍的性能提升。例如,在处理聚合查询时,ClickHouse可以一次性处理整个数据列,而不是对每一行单独执行聚合函数,这显著减少了函数调用次数和CPU Cache miss。
- 代码生成:ClickHouse还支持运行时代码生成,为特定的查询动态生成优化的代码。这种方法可以进一步优化查询性能,尤其是在复杂的查询场景下。
CK 索引
ClickHouse与MySQL InnoDB的索引设计方式截然不同,它的一级索引是采用稀疏索引的方式进行实现的。对比如下:
左侧的稠密索引中,索引标记和数据记录是一一对应的,而图右侧的稀疏索引则对应的是包含N行记录的一个数据块。稀疏索引与稠密索引的区别:
稀疏索引的优点是,仅需要少量索引标记就可以记录海量数据的区间位置信息。如果索引粒度是默认的8192,那一亿条数据仅需要对应12208个索引标记,这样就可以将索引标记放到内存中,可以起到提升查询性能的效果。
在二级索引方面,MySQL InnoDB只支持B+ Tree和Hash两种类型,而ClickHouse则支持minmax、set、bloom_filter、ngrambf_v1、tokenbf_v1和inverted等索引类型。
minmax索引,用来记录N(N = granularity)个数据块内的最大值和最小值,在对某列数据进行范围查询的时候,可以过滤掉不满足条件的数据区间,其适用于数据区分度比较高的场景。
index granularity :直接翻译的话就是索引粒度,指在稀疏索引中两个相邻索引对应数据的间隔 。ClickHouse中的MergeTree默认是8192。官方不建议修改这个值,除非该列存在大量重复值,比如在一个分区中几万行才有一个不同数据。稀疏索引的好处就是可以用很少的索引数据,定位更多的数据,代价就是只能定位到索引粒度的第一行,然后再进行进行一点扫描。
主键索引
ClickHouse支持主键索引,它将每列数据按照index granularity(默认8192行)进行划分,每个index granularity的开头第一行被称为一个mark行。主键索引存储该mark行对应的primary key的值。对于where条件中含有primary key的查询,通过对主键索引进行二分查找,能够直接定位到对应的index granularity,避免了全表扫描从而加速查询。
待主键定义之后,MergeTree会依据index_granularity间隔(默认8192行),为数据表生成一级索引并保存至primary.idx 文件内,索引数据按照PRIMARY KEY排序。相比使用PRIMARY KEY定义,更为常见的是通过ORDER BY指代主键。在此种情形下,PRIMARY KEY与ORDER BY定义相同,所以索引(primary.idx)和数据(.bin)会按照完全相同的规则排序。
ClickHouse中的主键,和其他数据库不太一样,它只提供了数据的一级索引,但是却不是唯一约束。这就意味着是可以存在相同primary key的数据。
索引的查询过程
(1)生成查询条件区间
首先,将查询条件转换为条件区间,例如下面的例子:
sql
WHERE ID = 'A003'
['A003', 'A003']
WHERE ID > 'A000'
('A000', +inf)
WHERE ID < 'A188'
(-inf, 'A188')
WHERE ID LIKE 'A006%'
['A006', 'A007')
(2)递归交集判断
以递归的形式,依次对MarkRange的数值区间与条件区间做交集判断。从最大区间[A000, +inf)开始:
- 如果不存在交集,则直接通过剪枝算法优化此整段MarkRange;
- 如果存在交集,且MarkRange步长大于8(end-start),则将此区间进一步拆分成8个子区间(merge_tree_coarse_index_granularity指定,默认值为8),并重复此规则,继续做递归交集判断;
- 如果存在交集,且MarkRange不可再分解(步长小于8),则记录MarkRange并返回。
(3)合并MarkRange区间
将最终匹配的MarkRange聚在一起,合并它们的范围:
MergeTree通过递归的形式持续向下拆分区间,最终将MarkRange定位到最细的粒度,以帮助在后续读取数据的时候,能够最小化扫描数据的范围。以上图为例,当查询条件WHERE ID='A003'的时候,最终只需要读取[A000, A003]和[A003, A006]两个区间的数据,它们对应MarkRange(start:0,end:2)范围,而其他无用的区间都被裁剪掉了。因为MarkRange转换的数值区间是闭区间,所以会额外匹配到临近的一个区间。
CK 引擎
ClickHouse 有很多表引擎,表引擎决定了数据以什么方式存储,以什么方式加载,以及数据表拥有什么样的特性。目前 ClickHouse 表引擎一共分为四个系列,分别是 Log、MergeTree、Integration、Special。
- Log 系列:适用于少量数据 (<100w行) 的场景,不支持索引,所以对于范围查询效率不高。
- Integration 系列:主要用于导入外部数据到 ClickHouse,或者在 ClickHouse 中直接操作外部数据,支持 Kafka、HDFS、JDBC、Mysql 等。
- Special 系列:比如 Memory 将数据存储在内存,重启后会丢失数据,查询性能极好,File 直接将本地文件作为数据存储等大多是为了特定场景而定制的。
- MergeTree 系列:MergeTree 家族自身拥有多种引擎的变种,其中 MergeTree 作为家族中最基础的引擎提供主键索引、数据分区、数据副本和数据采样等能力并且支持极大量的数据写入,家族中其他引擎在 MergeTree 引擎的基础上各有所长。
ClickHouse中最强大的表引擎MergeTree(合并树)和该系列(*MergeTree)中的其他引擎。
MergeTree
MergeTree 系列的表引擎是为插入大量数据而生,数据是以数据片段的形式一个接一个的快速写入,ClickHouse 为了避免数据片段过多会在后台按照一定的规则进行合并形成新的段,相比在插入时不断的修改已经存储在磁盘的数据,这种插入后合并再合并的策略效率要高很多。这种数据片段反复合并的特点,也正是 MergeTree 系列 (合并树家族) 名称的由来。为了避免形成过多的数据片段,需要进行批量写入。
sql
CREATE TABLE test_MergeTree(orderNo String, number Int16, createTime DateTime, updateTime DateTime)
ENGINE = MergeTree()
PARTITION BY createTime
ORDER BY (orderNo)
PRIMARY KEY (orderNo);
insert into test_MergeTree values('1', '20', '2021-01-01 00:00:00', '2021-01-01 00:00:00');
insert into test_MergeTree values('1', '30', '2021-01-01 00:00:00', '2021-01-01 01:00:00');
上面建表语句中,定义了订单号,商品数量,创建时间,更新时间。
按照创建时间进行数据分区,orderNo 作为主键 (primary key),orderNo 也作为排序键 (order by),默认情况下主键和排序键相同,大部分情况不需要再专门指定 primary key,这个例子中指定只是为了说明下主键和排序键的关系。当然排序键可以与的主键字段不同,但是主键必须为排序键的子集,如主键 (a,b), 排序键必须为 (a,b, , ),且组成主键的字段须在排序键字段中的最左侧。
注意这里写入的两条数据主键 orderNo 都是 1 的两条数据,这个场景是我们先创建订单,再更新了订单的商品数量为 30 和更新时间,此时业务实际订单量为 1,商品件量是 30。插入主键相同的数据不会产生冲突,并且查询数据两条相同主键的数据都存在。
由于每次插入都会形成一个 part,第一次 insert 生成了 1609430400_1_1_0 数据分区文件,第二次 insert 生成了 1609430400_2_2_0 数据分区文件,后台还没触发合并,所以在 clickhouse-client 上的展示结果是分开两个表格的。这在一些场景会有问题,比如去重统计订单数量,count (orderNo),统计下单件数 sum (number)。
综上,可以看出MergeTree 支持所有 ClickHouse SQL 语法。大部分功能点和我们熟悉的 MySQL 是类似的,但是有些功能差异比较大,比如MergeTree 虽然有主键,但并不是类似 MySQL 用来保持记录唯一的去重作用,MySQL 中一个表中不能存在两条相同主键的数据,但是 ClickHouse 中是可以的。要想实现去重效果,需要结合具体的表引擎ReplacingMergeTree、CollapsingMergeTree、VersionedCollapsingMergeTree实现。
ReplacingMergeTree
MergeTree 虽然有主键,但是不能对相同主键的数据进行去重,我们的业务场景不能有重复数据。ClickHouse 提供了 ReplacingMergeTree 引擎用来去重,能够在合并分区时删除重复的数据。一种是物理去重,就是重复的数据直接被删除掉,二是查询去重,即在查询结果已经将重复数据过滤掉的。
sql
CREATE TABLE test_ReplacingMergeTree(orderNo String, version Int16, number Int16, createTime DateTime, updateTime DateTime)
ENGINE = ReplacingMergeTree(version)
PARTITION BY createTime
ORDER BY (orderNo)
PRIMARY KEY (orderNo);1)
insert into test_ReplacingMergeTree values('1', 1, '20', '2021-01-01 00:00:00', '2021-01-01 00:00:00');2)
insert into test_ReplacingMergeTree values('1', 2, '30', '2021-01-01 00:00:00', '2021-01-01 01:00:00');3)
insert into test_ReplacingMergeTree values('1', 3, '30', '2021-01-02 00:00:00', '2021-01-01 01:00:00');
-- final方式去重
select * from test_ReplacingMergeTree final;
-- argMax方式去重
select argMax(orderNo,version) as orderNo, argMax(number,version) as number,argMax(createTime,version),argMax(updateTime,version) from test_ReplacingMergeTree;
ReplacingMergeTree 建表方法和 MergeTree 没有特别大的差异,只是 ENGINE 由 MergeTree 更改为 ReplacingMergeTree ([ver]),其中 ver 是版本列,是一个选填项。
在执行完前两条 insert 语句后进行三次查询的结果,三种方式查询均未对物理存储的数据产生影响,final、argMax 方式只是查询结果是去重的。
- 普通查询:查询结果未去重,物理数据未去重 (未合并分区文件)
- final 去重查询:查询结果已去重,物理数据未去重 (未合并分区文件)
- argMax 去重查询:查询结果已去重,物理数据未去重 (未合并分区文件)
其中 final 和 argMax 查询方式都过滤掉了重复数据。
但final 只能对本地表做去重查询,不能对跨分片的数据进行去重查询,如果两条数据落在了不同数据分片 (注意这里不是数据分区),那么 final不会有去重效果。但是 argMax 的结果是去重的。argMax 是通过比较第二参数 version 的大小,来取出我们要查询的最新数据来达到过滤掉重复数据的目的,其原理是将每个 Shard 的数据搂到同一个 Shard 的内存中进行比较计算,所以支持跨分片的去重。
ReplacingMergeTree 特点
- 使用主键作为判断重复数据的唯一键,支持插入相同主键数据。
- 在合并分区的时候会触发删除重复数据的逻辑。但是合并的时机不确定,所以在查询的时候可能会有重复数据,但是最终会去重。可以手动调用 optimize,但是会引发对数据大量的读写,不建议生产使用。
- 以数据分区为单位删除重复数据,当分区合并时,同一分区内的重复数据会被删除,不同分区的重复数据不会被删除。
- 可以通过 final,argMax 方式做查询去重,这种方式无论有没有做过数据合并,都可以得到正确的查询结果。
ReplacingMergeTree 最佳使用方案
- 普通 select 查询:对时效不高的离线查询可以采用 ClickHouse 自动合并配合,但是需要保证同一业务单据落在同一个数据分区,分布式表也需要保证在同一个分片 (Shard),这是一种最高效,最节省计算资源的查询方式。
- final 方式查询:对于实时查询可以使用 final,final 是本地去重,需要保证同一主键数据落在同一个分片(Shard),但是不需要落在同一个数据分区,这种方式效率次之,但是与普通 select 相比会消耗一些性能,如果 where 条件对主键索引,二级索引,分区字段命中的比较好的话效率也可以完全可以使用。
- argMax 方式查询:对于实时查询可以使用 argMax,argMax 的使用要求最低,咋查都能去重,但是由于它的实现方式,效率会低很多,也很消耗性能,不建议使用。
上述的三种使用方案中其中 ReplacingMergeTree 配合 final 方式比较普遍。
补充一、ClickHouse VS MySQL
- MySQL单条SQL是单线程的,只能跑满一个core,ClickHouse相反,有多少CPU,吃多少资源,所以效率高;
- ClickHouse不支持事务,不存在隔离级别。ClickHouse的定位是分析性数据库,而不是严格的关系型数据库。
- IO方面,MySQL是行存储,ClickHouse是列存储,后者在count()这类操作天然有优势,同时,在IO方面,MySQL需要大量随机IO,ClickHouse基本是顺序IO。
- CK大小是大小写敏感的,关键字非大小敏感
补充二、CK快的原因
写入:ClickHouse采用类LSM Tree的结构,数据写入后定期在后台Compaction,而且, ClickHouse在数据导入时全部是顺序写入,写入后数据段不可更改,在后台 Compaction 时也是多个段 合并排序后写回磁盘。
读取:ck是列式存储、索引机制、数据压缩、ck使用了向量化引擎(寄存器)、ck使用cpu加速、ck会在内存中进行group by
补充三、你们的项目Clickhouse 存储多少数据? 几张表?
10 几张宽表, 每天平均 10 来 G, 存储一年。
需要磁盘 10G * 365 天 * 2 副本/0.7 = 约 11T