clickhouse 超底层原理& 高可用集群 实操(史上最全)


文章很长,建议收藏起来慢慢读! 总目录 博客园版 为大家准备了更多的好文章!!!!

推荐:尼恩Java面试宝典(持续更新 + 史上最全 + 面试必备)具体详情,请点击此链接

尼恩Java面试宝典,34个最新pdf,含2000多页不断更新、持续迭代 具体详情,请点击此链接

在这里插入图片描述

本文背景

这段时间给大家 做简历指导,发现大家都缺少优质实操项目,所以打算介绍一个《100W级别qps日志平台实操》,基于clickhouse+netty,于是,就写了此文

此文涉及到大量的底层原理,和高并发的实操知识,建议大家慢慢读

并且强烈建议大家,对着此文,实操一下

MPP数据库简介

什么是OLTP与OLAP?

OLTP(OnLine Transaction Processing ) 联机事务处理 系统

例如mysql。

擅长事务处理,在数据操作中保持着很强的一致性和原子性 ,能够很好的支持频繁的数据插入和修改 ,但是,一旦数据量过大,OLTP便力不从心了。

OLAP(On-Line Analytical Processing)联机分析处理 系统

例如clickhouse,greenplum,Doris。

不特别关心对数据进行输入、修改等事务性处理,而是关心对已有 的大量数据进行多维度的、复杂的分析的一类数据系统 。

ClickHouse 简介

ClickHouse的全称是Click Stream,Data WareHouse。

ClickHouse 是俄罗斯的 Yandex (俄罗斯第一大搜索引擎) 于 2016 年开源的用于在线分析处理查询(OLAP :Online Analytical Processing)MPP架构列式存储数据库(DBMS:Database Management System),能够使用 SQL 查询实时生成分析数据报告。

ClickHouse 使用 C++ 语言编写,主要用于在线分析处理查询(OLAP),能够使用 SQL 查询实时生成分析数据报
告。

clickhouse可以做用户行为分析,流批一体

clickhouse没有走hadoop生态,采用 Local attached storage 作为存储

令人惊喜的是,ClickHouse 的性能大幅超越了很多商业 MPP 数据库软件,比如 Vertica,InfiniDB.

相比传统的数据库软件,ClickHouse 要快 100-1000X:

100Million 数据集:

ClickHouse 比 Vertica 约快 5 倍,比 Hive 快 279 倍,比 My SQL 快 801 倍

1Billion 数据集:

ClickHouse 比 Vertica 约快 5 倍,MySQL 和 Hive 已经无法完成任务了

Greenplum简介

Greenplum是一家总部位于美国加利福尼亚州,为全球大型企业用户提供新型企业级数据仓库(EDW)、企业级数据云(EDC)和商务智能(BI)提供解决方案和咨询服务的公司,

在全球已有:[纳斯达克,[纽约证券交易所,Skype. FOX,T-Mobile;

中国已有:中信实业银行,东方航空公司,阿里巴巴,华泰保险,中国远洋,李宁公司等大型企业用户选择Greenplum的产品。

Greenplum的架构采用了MPP(大规模并行处理)。

Greenplum名字来源

Greenplum的大中华区总裁Stanley Chen告诉我们:“Greenplum这个名字是一个7岁小女孩无意中脱口而出的。”起初几个创始人在斟酌公司名字的时候都很没头绪,于是他们去问了朋友的孩子,一个年仅7岁的可爱小姑娘告诉他们叫“Apple”,但是爸爸告诉她,这个名字已经被别人用了,还有其他的么?很快孩子便随口说了“Greenplum”,于是“Greenplum”公司的名字就这样诞生了。**

Doris简介

Doris由百度大数据部研发 ,之前叫百度 Palo,2018年贡献到 Apache 社区后,更名为 doris

Doris是一个MPP的OLAP系统,以较低的成本提供在大数据集上的高性能分析和报表查询功能。

图片

StoneDB介绍

此处的介绍,目的是 推广国产软件, 国产软件,不容易呀

StoneDB是国内首款基于MySQL的实时HTAP数据库产品, 由杭州石原子科技有限公司自主设计、研发的 基于 MySQL 内核打造的开源 HTAP(Hybrid Transactional and Analytical Processing)融合型数据库产品,可实现与 MySQL 的无缝切换。StoneDB 具备高性能、实时分析等特点,为用户提供一站式HTAP解决方案。StoneDB是一款兼容 MySQL的 HTAP 数据库,可以实现从MySQL到StonDB的无缝切换。

StoneDB 是基于 MySQL 内核打造的开源 HTAP (Hybrid Transactional and Analytical Processing) 融合型数据库,可实现与 MySQL 的无缝切换。StoneDB 具备超高性能、实时分析等特点,为用户提供一站式 HTAP 解决方案。

StoneDB 包含 100% 兼容 MySQL 5.6、5.7 协议,以及 MySQL 生态等重要特性,支持 MySQL 常用的功能及语法,支持 MySQL 生态中的系统工具和客户端,如 Navicat、Workbench、mysqldump、mydumper。由于 100% 兼容 MySQL,因此 StoneDB 的所有工作负载都可以继续使用 MySQL 数据库体系运行。

StoneDB 专门针对 OLAP 应用程序进行了设计和优化,支持百亿数据场景下进行高性能、多维度字段组合的复杂查询。

StoneDB 采用基于知识网格技术和列式存储引擎,该存储引擎为海量数据背景下 OLAP 应用而设计,通过列式存储数据、知识网格过滤、高效数据压缩等技术,为应用系统提供低成本和高性能的数据查询支持。

什么是MPP 系统

什么是 大规模并行处理 MPP架构?(Massively Parallel Processing)

MPP架构是将任务并行的分散到多个服务器和节点上,在每个节点上计算完成后,将各自部分的结果汇总在一起得到最终的结果。

采用MPP架构的数据库称为MPP数据库。

img

在 MPP 系统中,每个 单节点也可以运行自己的操作系统、数据库等。

换言之,每个节点内的 CPU 不能访问另一个节点的内存。

节点之间的信息交互是通过节点互联网络实现的,这个过程一般称为数据重分配(Data Redistribution) 。

与传统的单体架构 明显不同,MPP系统因为 要在不同处理单元之间传送信息,

  • 当数据规模很小的时候,MPP的效率要比单体架构要差一点,
  • 但是当数据规模上来之后,MPP的效率要比 单体架构 好。

这就是看通信时间占用计算时间的比例而定,如果通信时间比较多,那MPP系统就不占优势了,相反,如果通信时间比较少,那MPP系统可以充分发挥资源的优势,达到高效率。

为什么需要MPP数据库?

海量数据的分析需求

传统数据库无法支持大规模集群与PB级别数据量

单台机器性能受限、成本高昂,扩展性受限

支持复杂的结构化查询(这里是重点)

复杂查询经常使用多表联结、全表扫描等,牵涉的数据量往往十分庞大;

支持复杂sql查询和支持大数据规模;

Hadoop技术的先天不足

Hive等sql-on-hadoop性能太慢,分析场景不一样,SQL兼容性与支持不足

MPP数据库应用领域

大数据分析

MPP数据库做大数据计算或分析平台非常适合,例如:数据仓库系统、历史数据管理系统、数据集市等。

MPP数据库有很强的并行数据计算能力和海量数据存储能力,、

所以,报表统计分析、运维统计数据,快速生成报表展示都可以使用mpp数据库。

符合几个条件:不需要更新数据,不需要频繁重复离线计算,不需要并发大。

有上百亿以上离线数据,不更新,结构化,需要各种复杂分析的sql语句,那就可以选择他。

几秒、几十秒立即返回你想要的分析结果。

例如sum,count,group by,order,好几层查询嵌套,在几百亿数据里分分钟出结果

这类的数据库:,clickhouse,greenplum,Doris

MPP数据库不擅长高频的小规模数据插入、修改、删除,每次事务处理的数据量不大。

这类数据衡量指标是TPS,适用的系统是OLTP数据库。

Clickhouse特性

Clickhouse是一个列式数据库管理系统,在OLAP领域像一匹黑马一样,以其超高的性能受到业界的青睐。

Clickhouse的优势特性:

数据分区与线程级并行

ClickHouse支持PARTITION BY子句,在建表时可以指定按照任意合法表达式进行数据分区操作,

比如通过toYYYYMM()将数据按月进行分区、toMonday()将数据按照周几进行分区。

分区条件查询,只能读取包含适当分区数据块,而不扫描过多的数据。

灵活的使用,可以大大提升查询的性能。

ClickHouse 将数据划分为多个 partition,每个 partition 再进一步划分为多个 index granularity(索引粒度),

然后通过多个 CPU核心分别处理其中的一部分来实现并行数据处理。

在这种设计下,单条 Query 就能利用整机所有 CPU。

极致的并行处理能力,极大的降低了查询延时。

所以,ClickHouse 即使对于大量数据的查询也能够化整为零平行处理。

但是有一个弊端就是对于单条查询使用多 cpu,就不利于同时并发多条查询。所以对于高 qps 的查询业务,ClickHouse 并不是强项。

采用列式存储,数据类型一致,压缩性能更高

在一些列式数据库管理系统中(例如InfiniDB CE和MonetDB) 并没有使用数据压缩。>

InfiniDB Community Edition(社区版)提供一个可伸缩的分析型数据库引擎,主要为数据仓库、商业智能、以及对实时性要求不严格的应用而开发。基于MySQL搭建。包括对查询、事务处理以及大数据量加载的支持。

MonetDB是一个开源的面向列的数据库管理系统。MonetDB被设计用来为较大规模数据(如几百万行和数百列的数据库表)提供高性能查询的支持。

但是, 若想达到比较优异的性能,数据压缩确实起到了至关重要的作用。

提供 LZ4、ZSTD 两种数据压缩格式

硬件利用率高,连续IO,提高了磁盘驱动器的效率

许多的列式数据库(如 SAP HANA, Google PowerDrill)只能在内存中工作,这种方式会造成比实际更多的设备预算。

ClickHouse被设计用于工作在传统磁盘上的系统,它提供每GB更低的存储成本,但如果有可以使用SSD和内存,它也会合理的利用这些资源

向量化引擎与SIMD提高了CPU利用率,多核多节点并行化大查询

为了高效的使用CPU,数据不仅仅按列存储,同时还按向量(列的一部分)进行处理,这样可以更加高效地使用CPU

ClickHouse会使用服务器上一切可用的资源,从而以最自然的方式并行处理大型查询

支持SQL

ClickHouse支持基于SQL的声明式查询语言,该语言大部分情况下是与SQL标准兼容的。几乎覆盖了标准SQL的大部分语法,包括DDL和DML,以及配套的各种函数,用户管理及权限管理,数据的备份与恢复
支持的查询包括 GROUP BY,ORDER BY,IN,JOIN以及非相关子查询。
绝大部分查询基本和常用的mysql一样,可以省去大部分同学的学习成本。不仅如此提供了强大的函数支查询能力,更丰富的存储格式,例如array多维数组、json、tuple、set等。

支持近似计算

ClickHouse提供各种各样在允许牺牲数据精度的情况下对查询进行加速的方法:

  1. 用于近似计算的各类聚合函数,如:distinct values, medians, quantiles

  2. 基于数据的部分样本进行近似查询。这时,仅会从磁盘检索少部分比例的数据。

  3. 不使用全部的聚合条件,通过随机选择有限个数据聚合条件进行聚合。这在数据聚合条件满足某些分布条件下,在提供相当准确的聚合结果的同时降低了计算资源的使用。

索引

  • 主键索引

ClickHouse支持主键索引,它将每列数据按照index granularity(默认8192行)进行划分,会为每个数据片段创建一个索引文件,索引文件包含每个索引行(『标记』)的主键值。

索引行号定义为 n * index_granularity 。当数据被插入到表中时,会分成数据片段并按主键的字典序排序。例如,主键是 (CounterID, Date) 时,片段中数据按 CounterID 排序,具有相同 CounterID 的部分按 Date 排序。

但是值得注意的是:ClickHouse 不要求主键惟一。

所以,你可以插入多条具有相同主键的行。

要想实现去重效果,需要结合具体的表引擎ReplacingMergeTree、CollapsingMergeTree、VersionedCollapsingMergeTree实现。

  • 稀疏索引

ClickHouse支持对任意列创建任意数量的稀疏索引。其中被索引的value可以是任意的合法SQL Expression,并不仅仅局限于对column value本身进行索引。之所以叫稀疏索引,是因为它本质上是对一个完整index granularity(默认8192行)的统计信息,并不会具体记录每一行在文件中的位置。

支持近似计算

ClickHouse提供各种各样在允许牺牲数据精度的情况下对查询进行加速的方法:

  1. 用于近似计算的各类聚合函数,如:distinct values, medians, quantiles
  2. 基于数据的部分样本进行近似查询。这时,仅会从磁盘检索少部分比例的数据。
  3. 不使用全部的聚合条件,通过随机选择有限个数据聚合条件进行聚合。这在数据聚合条件满足某些分布条件下,在提供相当准确的聚合结果的同时降低了计算资源的使用。

丰富的表引擎

ClickHouse和MySQL类似,把表级的存储引擎插件化,根据表的不同需求可以设定不同的存储引擎。

目前包括合并树,日志,接口和其他四大类20多种引擎。

clickhouse 不仅拥有自己强大的MergeTree家族的多种本地引擎外,还提供了丰富的外部引擎供我们选择,包括但不限于:kafka、HDFS、Mysql。
但是使用外部引擎的时,性能自然会不如本地存储。

MergeTree引擎家族中很有很多优秀的引擎
比如:
    适合人物画像的AggregatingMergeTree引擎
    可自定义去重的SummingMergeTree引擎
    折叠树CollapsingMergeTree
    用于Graphite监控的GraphiteMergeTree引擎
    。。。
这些都具有数据副本的能力  Replicated*
官方推荐的适合大多数场景的依然是MergeTree引擎,其家族中其他引擎大多也是在其基础上做的封装。

除此之外,还有分布式的表引擎Distributed。 Distributed是一种逻辑表引擎,并不存储数据。

创建该表引擎时,会指向已配置的分片集,要查询的时候,它会向每个分片发起查询并最终汇总集合然后返回。
这里配置分片集可超灵活的配置,不用的时候可以删除。以后会单独来介绍该引擎的使用。

基于shard+replica实现的线性扩展和高可靠

clickhouse高吞吐写入能力

ClickHouse 采用类 LSM Tree的结构,数据写入后定期在后台 Compaction。

通过类 LSM tree的结构,ClickHouse 在数据导入时全部是顺序 append 写,写入后数据段不可更改,

在后台compaction 时也是多个段 merge sort 后顺序写回磁盘。

顺序写的特性,充分利用了磁盘的吞吐能力,即便在 HDD 上也有着优异的写入性能。

官方公开 benchmark 测试显示能够达到 50MB-200MB/s 的写入吞吐能力,按照每行100Byte 估算,大约相当于 50W-200W 条/s 的写入速度。

LSM结构(Log Structured Merge Tree)解读

1996年,一篇名为 Thelog-structured merge-tree(LSM-tree)的论文创造性地提出了日志结构合并树( Log-Structured Merge-Tree)的概念,该方法既吸收了日志结构方法的优点,又通过将数据文件预排序克服了日志结构方法随机读性能较差的问题。

尽管当时 LSM-tree新颖且优势鲜明,但它真正声名鹊起却是在 10年之后的 2006年,

2006年,Google 发表了 BigTable 的论文。这篇论文提到 BigTable 单机上所使用的数据结构就是 LSM。

那年谷歌的一篇使用了 LSM-tree技术的论文 Bigtable: A Distributed Storage System for Structured Data横空出世,在分布式数据处理领域掀起了一阵旋风,

随后两个声名赫赫的大数据开源组件( 2007年的 HBase与 2008年的 Cassandra,目前两者同为 Apache顶级项目)直接在其思想基础上破茧而出,彻底改变了大数据基础组件的格局,同时也极大地推广了 LSM-tree技术。

目前,LSM 被很多存储产品作为存储结构,比如 Apache HBase, Apache Cassandra, MongoDB 的 Wired Tiger 存储引擎, LevelDB 存储引擎, RocksDB 存储引擎等。

简单地说,LSM 的设计目标是提供比传统的 B+ 树更好的写性能。

LSM 通过将磁盘的随机写转化为顺序写来提高写性能 ,而付出的代价就是牺牲部分读性能、写放大(B+树同样有写放大的问题)。

LSM-tree最大的特点是同时使用了两部分类树的数据结构来存储数据,并同时提供查询。

其中一部分数据结构( C0树)存在于内存缓存(通常叫作 memtable)中,负责接受新的数据插入更新以及读请求,并直接在内存中对数据进行排序;

另一部分数据结构( C1树)存在于硬盘上 (这部分通常叫作 sstable),它们是由存在于内存缓存中的 C0树冲写到磁盘而成的,主要负责提供读操作,特点是有序且不可被更改。

LSM 相比 B+ 树能提高写性能的本质原因是:

外存——无论磁盘还是 SSD,其随机读写都要慢于顺序读写。

hash表和B+树

在了解LSM树之前,我们需要对hash表和B+树有所了解。

hash表就不用说了,通过key值经过hash算法,直接定位到数据存储地址,然后取出value值。

时间复杂度O(1),找数据和存数据就需要那么一下子,就给找到了

hash存储方式支持增、删、改以及随机读取操作,但不支持顺序扫描,对应的存储系统为key-value存储系统。

对于key-value的插入以及查询,哈希表的复杂度都是O(1),明显比树的操作O(n)快,

如果不需要有序的遍历数据,哈希表就是最佳选择

B+树我们常用的数据库Mysql的底层数据结构,例如我们的索引就是B+树结构。

B+树是一种专门针对磁盘存储而优化的N叉排序树,以树节点为单位存储在磁盘中,从根开始查找所需数据所在的节点编号和磁盘位置,将起加载到内存中然后继续查找,直到找到所需的数据。

B+树既有排序树的优点,能够很快沿着树枝找打目标节点,又能防止树的高度过高,大大减少磁盘IO次数。还能进行快速全表扫描遍历。

B+ 树的三个特点:

  1. 节点的子树数和关键字数相同
  2. 非叶子节点仅用作索引,它的关键字和子节点有重复元素
  3. 叶子节点形成有序链表,包含了全部数据,同时符合左小右大的顺序
    在这里插入图片描述

B+树改进了B树, 让内结点只作索引使用, 去掉了其中指向data record的指针, 使得每个结点中能够存放更多的key, 因此能有更大的出度.

这有什么用? 这样就意味着存放同样多的key, 树的层高能进一步被压缩, 使得检索的时间更短。

B树和B+树的对比介绍

首先从二叉树说起,

因为 二叉树 会产生退化现象,提出了平衡二叉树,

在平衡二叉树基础上, 再提出怎样让每一层放的节点多一些,来减少遍历高度,引申出m叉树,

m叉搜索树同样会有退化现象,引出m叉平衡树,也就是B树,这时候每个节点既放了key也放了value,

怎样使每个节点放尽可能多的key值,以减少遍历高度呢(访问磁盘次数),

可以将每个节点只放key值,将value值放在叶子结点,在叶子结点的value值增加指向相邻节点指针,这就是优化后的B+树。所有叶子节点形成有序链表,便于范围查询,不用每次要检索树。

目前数据库多采用两级索引的B+树,树的层次最多三层,因此可能需要5次磁盘访问才能更新一条记录(三次磁盘访问获得数据索引以及行id,然后再进行一次数据文件读操作及一次数据文件写操作)

B~树(平衡多路二叉树)

B树,又叫平衡多路查找树。一棵m阶的B树 (m叉树)的特性如下:

  1. 树中每个结点至多有m个孩子;

  2. 除根结点和叶子结点外,其它每个结点至少有[m/2]个孩子;

  3. 若根结点不是叶子结点,则至少有2个孩子;

  4. 所有叶子结点都出现在同一层,叶子结点不包含任何关键字信息(可以看做是外部接点或查询失败的接点,实际上这些结点不存在,指向这些结点的指针都为null);

  5. 每个非终端结点中包含有n个关键字信息: (n,A0,K1,A1,K2,A2,......,Kn,An)。其中,

a) Ki (i=1...n)为关键字,且关键字按顺序排序Ki < K(i-1)。

b) Ai为指向子树根的接点,且指针A(i-1)指向子树种所有结点的关键字均小于Ki,但都大于K(i-1)。

c) 关键字的个数n必须满足: [m/2]-1 <= n <= m-1

在这里插入图片描述

B+树

B+树:是应文件系统所需而产生的一种B~树的变形树。

一棵m阶的B+树和m阶的B-树的差异在于:

  1. 有n棵子树的结点中含有n个关键字; (B~树是n棵子树有n+1个关键字)

  2. 所有的叶子结点中包含了全部关键字的信息,及指向含有这些关键字记录的指针,且叶子结点本身依关键字的大小自小而大的顺序链接。 (B~树的叶子节点并没有包括全部需要查找的信息)

  3. 所有的非终端结点可以看成是索引部分,结点中仅含有其子树根结点中最大(或最小)关键字。 (B~树的非终节点也包含需要查找的有效信息)

在这里插入图片描述

a、B+树的磁盘读写代价更低
我们都知道磁盘时可以块存储的,也就是同一个磁道上同一盘块中的所有数据都可以一次全部读取。

而B+树的内部结点并没有指向关键字具体信息的指针(比如文件内容的具体地址 , 比如说不包含B~树结点中的FileHardAddress[filenum]部分) 。

因此其内部结点相对B~树更小。如果把所有同一内部结点的关键字存放在同一盘块中,那么盘块所能容纳的关键字数量也越多。

这样,一次性读入内存中的需要查找的关键字也就越多。相对来说IO读写次数也就降低了。

举个例子,假设磁盘中的一个盘块容纳16bytes,而一个关键字2bytes,一个关键字具体信息指针2bytes。一棵9阶B树(一个结点最多8个关键字)的内部结点需要2个盘快。而B+树内部结点只需要1个盘快。当需要把内部结点读入内存中的时候,B树就比B+数多一次盘块查找时间(在磁盘中就是盘片旋转的时间)。

b、B+树的查询效率更加稳定。

由于非终结点并不是最终指向文件内容的结点,而只是叶子结点中关键字的索引。

所以任何关键字的查找必须走一条从根结点到叶子结点的路。

所有关键字查询的路径长度相同,导致每一个数据的查询效率相当。

什么是LSM树

LSM树,即日志结构合并树(Log-Structured Merge-Tree)。

其实它并不属于一个具体的数据结构,它更多是一种数据结构的设计思想。

大多NoSQL数据库核心思想都是基于LSM来做的,只是具体的实现不同。

LSM树诞生背景

传统关系型数据库使用btree或一些变体作为存储结构,能高效进行查找。

但保存在磁盘中时它也有一个明显的缺陷,那就是逻辑上相离很近但物理却可能相隔很远,这就可能造成大量的磁盘随机读写。

随机读写比顺序读写慢很多,为了提升IO性能,我们需要一种能将随机操作变为顺序操作的机制,于是便有了LSM树。

为啥 随机读写比顺序读写慢很多呢?

磁盘读写时涉及到磁盘上数据查找,地址一般由柱面号、盘面号和块号三者构成。

也就是说:

step1:移动臂先根据柱面号移动到指定柱面,

step2: 然后根据 盘面号 确定盘面

step3:最后 块号 确定磁道,最后将指定的磁道段移动到磁头下,便可开始读写。

整个过程主要有三部分时间消耗,查找时间(seek time) +等待时间(latency time)+传输时间(transmission time) 。分别表示定位柱面的耗时、将块号指定 磁道段 移到磁头的耗时、将数据传到内存的耗时。

整个磁盘IO最耗时的地方在查找时间,所以减少查找时间能大幅提升性能。

LSM树原理

LSM树能让我们进行顺序写磁盘,从而大幅提升写操作,作为代价的是牺牲了一些读性能。

LSM树由两个或以上的存储结构组成,

最简单的两个存储结构:

  • 一个存储结构常驻内存中,称为C0 tree,具体可以是任何方便健值查找的数据结构,比如红黑树、map之类,甚至可以是跳表。

  • 另外一个存储结构常驻在硬盘中,称为C1 tree,具体结构类似B树。

C1所有节点都是100%满的,节点的大小为磁盘块大小。

在这里插入图片描述

LSMTree和SSTable

什么是SSTable呢? SSTable (Sorted String Table) 是排序字符串表的简称,

正如名字本身所包含的意思一样,SSTable是一个简单的抽象,用来高效地存储大量的键-值对数据,同时做了优化来实现顺序读/写操作的高吞吐量。

  • SStable在功能上,只是对hash加入了一个按键值排序。
  • 由于是排序数据,我们不必像hash索引一样记录所有数据的位置,索引可以是稀疏的,例如对于段文件中的上千条数据,只保留一个索引

例如:

对于数据键1~100000,我们每隔1000个数据保留一个索引,当查找5500时,对应索引应该是5000,之后向后查找500条(或者使用二分)。

使用方法似乎很好理解,麻烦的是构建和维护SStable。

SStable基本工作流程是:

  • 写入时将内容插入内存中的排序数据结构(例如红黑树)
  • 当内存大于一定阈值时将其写入磁盘,作为磁盘的一部分
  • 如果接收到新的读取请求,要先尝试在内存中读取,其次是磁盘的最新排序部分,然后是次新的,依次类推
  • 后台进程周期性将各个排序部分归并,并且出现相同数据时只会保留最新值。

如果使用sstable,先进行内存写,所以我们也要考虑日志系统,类似innodb里redolog方式来保证持久性。

LSM-Tree 全名叫Log-Structured Merge Tree,最早建立在日志结构文件之上,现在基于合并和压缩排序文件原理的存储引擎都统称为LSM存储引擎。

我们通常把LSM看成一种思想:保存在后台合并的一系列SStable

这种思想简单且有效:

  • 即使数据集远大于内存,LSM-tree也能正常工作
  • 由于键值有序,范围查询相比于hash表有很大优势
  • 由于写入是顺序的(归并是后台线程在空闲时间做的)LSM-tree可以提供非常高的写入吞吐量

查找的性能优化:

在LSM系统中查找一个不存在的键时会导致查询时间长,因为要从最新的数据一直往前查找,所以lsm一般会使用布隆过滤器进行优化

接下来,详细说说 SSTable

SSTable的定义

SSTable (Sorted String Table) 是排序字符串表的简称,来源于大名鼎鼎的 Google Bigtable 论文。

它用于 Bigtable 内部数据文件的存储,它是一个种高效的 key-value 型文件存储格式。

要解释这个术语的真正含义,最好的方法就是从它的出处找答案,所以重新翻开BigTable的论文。

在这篇论文中,最初对SSTable是这么描述的(第三页末和第四页初):

SSTable

The Google SSTable file format is used internally to store Bigtable data.

An SSTable provides a persistent, ordered immutable map from keys to values, where both keys and values are arbitrary byte strings. Operations are provided to look up the value associated with a specified key, and to iterate over all key/value pairs in a specified key range. Internally, each SSTable contains a sequence of blocks (typically each block is 64KB in size, but this is configurable). A block index (stored at the end of the SSTable) is used to locate blocks; the index is loaded into memory when the SSTable is opened. A lookup can be performed with a single disk seek: we first find the appropriate block by performing a binary search in the in-memory index, and then reading the appropriate block from disk. Optionally, an SSTable can be completely mapped into memory, which allows us to perform lookups and scans without touching disk.

简单的非直译:

SSTable是Bigtable内部用于数据的文件格式,它的格式为文件本身就是一个排序的、不可变的、持久的Key/Value对Map,其中Key和value都可以是任意的byte字符串。

使用Key来查找Value,或通过给定Key范围遍历所有的Key/Value对。

每个SSTable包含一系列的Block(一般Block大小为64KB,但是它是可配置的),在SSTable的末尾是Block索引,用于定位Block,这些索引在SSTable打开时被加载到内存中,在查找时首先从内存中的索引二分查找找到Block,然后一次磁盘寻道即可读取到相应的Block。

还有一种方案是将这个SSTable加载到内存中,从而在查找和扫描中不需要读取磁盘。

HBase中的SSTable

这个貌似就是HFile第一个版本的格式么,贴张图感受一下:
img
在HBase使用过程中,对这个版本的HFile遇到以下一些问题(参考这里):

  1. 解析时内存使用量比较高。
  2. Bloom Filter和Block索引会变的很大,而影响启动性能。具体的,Bloom Filter可以增长到100MB每个HFile,而Block索引可以增长到300MB,如果一个HRegionServer中有20个HRegion,则他们分别能增长到2GB和6GB的大小。HRegion需要在打开时,需要加载所有的Block索引到内存中,因而影响启动性能;而在第一次Request时,需要将整个Bloom Filter加载到内存中,再开始查找,因而Bloom Filter太大会影响第一次请求的延迟。
    而HFile在版本2中对这些问题做了一些优化,具体会在HFile解析时详细说明。

LevelDB 中的SSTable

先看看SSTable文件的结构

img

整体上看 SSTable文件分为数据区与索引区,

尾部的footer指出了meta index block与data index block的偏移与大小,

index block指出了各data block的偏移与大小,metaindex block指出了filter block的偏移与大小。

1)data block:存储key-value记录,分为Data、type、CRC三部分
2)filter block:默认没有使用,用于快速从data block 判断key-value是否存在
3)metaindex block :记录filter block的相关信息
4)Index block:描述一个data block,存储着对应data block的最大Key值,以及data block在文件中的偏移量和大小
5)footer:索引的索引,记录metaindex block和Index block在SSTable中的偏移量了和大小

下面再具体看看各个部分物理结构

1、block
sstable中data block 、metaindex block、index block都用这种block这种结构。

对于data block,当block大小(record、restarts数组、以及num_restarts)超过4k时,就切换一个新的block继续往SSTable写数据,

而metaindex block、index block就只有一个block,所以上图看起来data block有多个。
这里写图片描述

block主要由数据区record和restarts组成。 为什么是这种结构?

data block主要是存储数据,block内给一定数量(默认16)key-value分组,

每组又用restarts数组记录起始位置,因此可以根据restarts读取每组起始位置key-value,

由于block内的数据是从小到大有序存储的,所以可以通过restarts数组,获取每组起始key-value,比较起始key key(n)与查找的key大小,如果key(n)>key,那么key一定在序号>=n组之后,否则在 < n组之前。

因此可以通过二分查找思想通过restarts获取起始key,来定位key的位置,避免线性查找低效。

因此,restarts的思想就是:提高block内key-value查找效率,直接定位key所在group。

下面再来看看record结构。

record相对有意思,不是简单的用key-length | key-data | value-length|value-data存储。

这里写图片描述

data block中的key是有序存储的,相邻的key之间可能有重复,因此存储时采用前缀压缩,后一个key只存储与前一个key不同的部分。

重启点指出的位置即每组起始位置的key不按前缀压缩,而是完整存储该key。

type是表示数据是否压缩,以怎样的方式压缩,crc32是该block校验码。

2、index block

index block 的结构也是block 结构,是data block的索引,记录每个data block 最大key 和 起始位置以及大小。

具体的存储方式是以每个data block最大key 为key,以data block 起始位置和大小为value。因此可以根据每个

block的最大key与查询key比较,直接定位查询key所在的位置。

这是理论上key的存储方式,但是在sstable二次压缩的过程对key做了一个优化,它并不保存最大key,而是保存一个能分隔两个data block的最短Key,如:假定data block1的最大一个key为“abcdefg”,data block2最大key为“abzxcv”,则index可以记录data block1的索引key为“abd”;这样的分割串可以有很多,只要保证data block1中的所有Key都小于等于此索引,data block2中的所有Key都大于此索引即可。

这种优化缩减了索引长度,查询时可以有效减小比较次数。
因此,index block的思想是提高SSTable内key-value查找效率,直接定位key所在block。

3、metaindex_block
也是block结构。就只有一条记录,其key是filter. + filter_policy的name,value是filter大小和起始位置。

4、filter block
filter block就是一个bloom filter,关于bloom filter原理概念可以百度。
每个bloom filter是对data block 的key 经过hash num 次形成的字节数组,多少个data block对应多少个bloom filter。
bloom filter实质就是一个bit 数组,对block 内key hash,将相应的位置设为1,这种设计关键在于能提高不存在的key判断效率,通过filter 计算,如果不存在,就不用通过data block内的restarts方式读取文件查找key是否存在,但是如果filter判断存在,还需通过restarts方式确定。

5、footer
footer位于SSTable文件尾部,占用空间固定为48个字节。其末尾8个字节是一个magic_number。metaindex_block_handle与index_block_handle物理上占用了40个字节,metaindex_block_handle和index_block_handle是BlockHandle数据类型, 这种结构用于记录metaindex block 和index block的起始位置和大小。
这里写图片描述

对于BlockHandle ,其实可以看作文件内容指针实现方式,BlockHandle记录数据位置及大小,与c/c++指针 思想类似,通过地址和大小可以读取数据。

BlockHandle格式

varint64 offset | varint64 size_

采用变长存储,所以实际上存储可能连32字节都不到,剩余填充0。

总结:

SSTable其实就是通过二次索引,先读取footer,

根据footer中index_block_handler记录的index_block起始位置和大小,读取index block,

通过index block 查询key所在data block,再在data block内部通过restarts 进一步确定key所在group。

下面是完整的SSTable结构图

这里写图片描述

LSM插入步骤

插入一条新纪录时,首先在日志文件中插入操作日志,以便后面恢复使用,日志是以append形式插入,所以速度非常快;

将新纪录的索引插入到C0中,这里在内存中完成,不涉及磁盘IO操作;

当C0大小达到某一阈值时或者每隔一段时间,将C0中记录滚动合并到磁盘C1中;

对于多个存储结构的情况,当C1体量越来越大就向C2合并,以此类推,一直往上合并Ck。

在这里插入图片描述

LSM合并步骤

合并过程中会使用两个块:emptying block 和 filling block。

  1. 从C1中读取未合并叶子节点,放置内存中的emptying block中。
  2. 从小到大找C0中的节点,与emptying block进行合并排序,合并结果保存到filling block中,并将C0对应的节点删除。
  3. 不断执行第2步操作,合并排序结果不断填入filling block中,当其满了则将其追加到磁盘的新位置上,注意是追加而不是改变原来的节点。合并期间如故宫emptying block使用完了则再从C1中读取未合并的叶子节点。
  4. C0和C1所有叶子节点都按以上合并完成后,即完成一次合并。

LSM插入案例

向LSM树中插入 A E L R U ,首先会插入到内存中的C0树上,

这里使用AVL树,插入“A”,

当然,得先WAL, 预先写入日志,向磁盘日志文件追加记录,然后再插入C0,

img

插入“E”,同样先追加日志再写内存,

img

继续插入“L”,旋转后如下,

img

插入“R”“U”,旋转后最终如下。

img

假设此时触发合并,

则因为C1还没有树,所以emptying block为空,直接从C0树中依次找最小的节点。

filling block长度为4,这里假设磁盘块大小为4。

开始找最小的节点,并放到filling block中,

img

继续找第二个节点,

img

以此类推,填满filling block,

img

开始写入磁盘,C1树,

img

继续插入 B F N T ,先分别写日志,然后插入到内存的C0树中,

img

假如此时进行合并,先加载C1的最左边叶子节点到emptying block,

img

接着对C0树的节点和emptying block进行合并排序,首先是“A”进入filling block,

img

然后是“B”,

img

合并排序最终结果为,

img

将filling block追加到磁盘的新位置,将原来的节点删除掉,

img

继续合并排序,再次填满filling block,

img

将filling block追加到磁盘的新位置,上一层的节点也要以磁盘块(或多个磁盘块)大小写入,尽量避开随机写。另外由于合并过程可能会导致上层节点的更新,可以暂时保存在内存,后面在适当时机写入。

img

LSM查找操作

查找总体思想是先找内存的C0树,找不到则找磁盘的C1树,然后是C2树,以此类推。

假如要找“B”,先找C0树,没找到。

img

接着找C1树,从根节点开始,

img

找到“B”。

img

LSM删除操作

删除操作为了能快速执行,主要是通过标记来实现,在内存中将要删除的记录标记一下,后面异步执行合并时将相应记录删除。

比如要删除“U”,假设标为#的表示删除,则C0树的“U”节点变为,

img

而如果C0树不存在的记录,

则在C0树中生成一个节点,并标为#,查找时就能再内存中得知该记录已被删除,无需去磁盘找了。

比如要删除“B”,那么没有必要去磁盘执行删除操作,直接在C0树中插入一个“B”节点,并标为#。

img

LSM树的特点:用牺牲读性能,来换取写性能

优化写性能

如果我们对写性能特别敏感,我们最好怎么做?

—— Append Only:所有写操作都是将数据添加到文件末尾。这样做的写性能是最好的,大约等于磁盘的理论速度(200 ~ 300 MB/s)。

但是 Append Only 的方式带来的问题是:

  • 读操作不方便。
  • 很难支持范围操作。
  • 需要垃圾回收(合并过期数据)。

所以, 纯粹的 Append Only 方式只能适用于一些简单的场景:

  • 数据库的 WAL(预写日志)。
  • 能知道明确的 offset,比如 Bitcask。

如果要优化读性能

如果我们对读性能特别敏感,一般我们有两种方式:

  • 有序存储,比如 B+ 树,SkipList 等。
  • Hash 存储 —— 不支持范围操作,适用范围有限。

读写性能的权衡

如何获得(接近) Append Only 的写性能,而又能拥有不错的读性能呢?

以 LevelDB 为代表的 LSM 存储引擎给出了一个参考答案。

注意,LevelDB 实现的是优化后的 LSM,原始的 LSM 可以参考论文。

以 LevelDB 例子, LevelDB 的写操作主要由两步组成:

  • 写日志并持久化(Append Only)。
  • Apply 到内存中的 memtable(SkipList)。

所以,LevelDB 的写速度非常快。

memtable 写“满”后,会转换为 immutable memtable,

然后被后台线程 compaction 成按 Key 有序存储的 sst 文件(顺序写)。

由于 sst 文件会有多个,所以 LevelDB 的读操作可能会有多次磁盘 IO(LevelDB 通过 table cache、block cache 和 bloom filter 等优化措施来减少读操作的磁盘 IO 次数)。

基于 LSM 数据结构的 NO SQL的适用场景:

  • 写请求多。
  • 写性能要求高:(高吞吐+低延迟)。

LSM-tree的另一大特点是除了使用两部分类树的数据结构外,还会使用日志文件(通常叫作 commit log)来为数据恢复做保障。

这三类数据结构的协作顺序一般是:所有的新插入与更新操作都首先被记录到 commit log中——该操作叫作 WAL(Write Ahead Log),然后再写到 memtable,最后当达到一定条件时数据会从 memtable冲写到 sstable,并抛弃相关的 log数据; memtable与 sstable可同时供查询;当 memtable出问题时,可从 commit log与 sstable中将 memtable的数据恢复。

理论上,可以是内存中树的一部分和磁盘中第一层树做合并,对于磁盘中的树直接做update操作有可能会破坏物理block的连续性,但是实际应用中,一般LSM树有多层,当磁盘中的小树合并成一个大树的时候,可以重新排好顺序,使得block连续,优化读性能。

在这里插入图片描述

LSM树的特点:用读性能来换取写性能,将对数据的修改增量保持在内存中,达到指定的大小限制后将这些修改操作批量写入磁盘

LSM树的核心思想:放弃部分读性能,提高写性能

代表数据库:nessDB、LevelDB、HBase等非关系型数据库

我们可以参考 HBase的架构来体会其架构中基于 LSM-tree的部分特点。

按照 WAL的原则,数据首先会写到 HBase的 HLog(相当于 commit log)里,然后再写到 MemStore(相当于 memtable)里,最后会冲写到磁盘 StoreFile(相当于 sstable)中。

这样 HBase的 HRegionServer就通过 LSM-tree实现了数据文件的生成。

HBase LSM-tree架构示意图如下图。

img

LSM-tree的这种结构非常有利于数据的快速写入(理论上可以接近磁盘顺序写速度),

但是,LSM-tree不利于读——因为理论上读的时候可能需要同时从 memtable和所有硬盘上的 sstable中查询数据,这样显然会对性能造成较大的影响。

为了解决这个问题, LSM-tree采取了以下主要的相关措施。

  • 定期将硬盘上小的 sstable合并(通常叫作 Merge或 Compaction操作)成大的 sstable,以减少 sstable的数量。而且,平时的数据更新删除操作并不会更新原有的数据文件,只会将更新删除操作加到当前的数据文件末端,只有在 sstable合并的时候才会真正将重复的操作或更新去重、合并。
  • 对每个 sstable使用布隆过滤器( Bloom Filter),以加速对数据在该 sstable的存在性进行判定,从而减少数据的总查询时间。

LSM树和B+树的差异主要在于读性能和写性能进行权衡,在牺牲的读性能的同时,寻找其余补救方案。

B+树存储引擎,不仅支持单条记录的增、删、读、改操作,还支持顺序扫描(B+树的叶子节点之间的指针),对应的存储系统就是关系数据库。但随着写入操作增多,为了维护B+树结构,节点分裂,读磁盘的随机读写概率会变大,读性能会逐渐减弱。

LSM树(Log-Structured MergeTree)存储引擎和B+树存储引擎一样,同样支持增、删、读、改、顺序扫描操作。而且通过批量存储技术规避磁盘随机写入问题。

当然凡事有利有弊,LSM树和B+树相比,LSM树牺牲了部分读性能,用来大幅提高写性能。

什么是vectorization?

向量化计算(vectorization),也叫vectorized operation,也叫array programming,

说的是一个事情:将多次for循环计算变成一次计算。

img

上图中,左侧为vectorization,右侧为寻常的For loop计算。

vectorization 将多次for循环计算变成一次计算,

vectorization 完全仰仗于CPU的SIMD指令集,

SIMD指令可以在一条cpu指令上处理2、4、8或者更多份的数据。

在Intel处理器上,这个称之为SSE, 以及后来的AVX,在Arm处理上,这个称之为NEON。

因此简单来说,

for循环计算是将一个loop——处理一个array(N个数据)的时候,每次处理1个数据,共处理N次,

向量化计算就 转化为vectorization——处理一个array的时候每次同时处理8个数据,共处理N/8次。

vectorization如何让速度更快?

介绍SSE 指令集 / AVX指令集之前,先要引入一个向量的概念。所谓向量,

就是多个标量的组合,通常意味着SIMD(单指令多数据),就是一个指令同时对多个数据进行处理,达到很大的吞吐量。

早在1996年,Intel就在X86架构上应用了MMX(多媒体扩展)指令集,那时候还仅仅是64位向量。

到了1999年,SSE(流式SIMD扩展)指令集出现了,这时候的向量提升到了128位。

SIMD

SIMD(Single Instruction Multiple Data,单指令多数据流),是一种实现空间上的并行性的技术。

这种技术使用一个控制器控制多个处理单元,同时对一组数据中的每一个数据执行相同的操作。

在 SIMD 指令执行期间,任意时刻都只有一个进程在运行,即 SIMD 没有并发性,仅仅只是同时进行计算。

在 Intel 的 x86 微架构处理器中,SIMD 指令集有 MMX、SSE、SSE2、SSE3、SSSE3、SSE4.1、SSE4.2、AVX、AVX2、AVX512。

我们以x86指令集为例,

1997年,x86扩展出了MMX指令集,伴随着80-bit的vector寄存器,首开向量化计算的先河。

之后,x86又扩展出了SSE指令集 (有好几个版本, 从SSE1到SEE4.2),伴随着128-bit寄存器。

而在2011年,Intel发布了Sandy Bridge架构——扩展出了AVX指令集(256-bit寄存器)。

在2016年,第一个带有AVX-512寄存器的CPU发布了(512-bit寄存器,可以同时处理16个32-bit的float数)。

SSE和AVX各有16个寄存器。

SSE的16个寄存器为XMM0-XMM15,AVX的16个寄存器为YMM0-YMM15。

XMM 寄存器 registers 每个为128 bits,

YMM寄存器 registers 每个为256bit

AVX-512 寄存器 registers 每个为512bit。

AVX

AVX 是 SSE 架构的延伸,将 SSE 的 XMM 128bit 寄存器升级成了 YMM 256bit 寄存器,同时浮点运算命令扩展至 256 位,运算效率提升了一倍。

另外,AVX 还添加了三操作数指令,以减少在编码时先复制再运算的动作。

AVX2 将大多数整数运算命令扩展至 256 位,同时支持 FMA(Fused Multiply-Accumulate,融合乘法累加)运算,可以在提高运算效率的同时减少运算时的精度损失。

AVX512 将 AVX 指令进一步扩展至 512 位。

AVX指令介绍, 参考该网站:Crunching Numbers with AVX and AVX2 - CodeProject

SSE有3个数据类型:__m128 , __m128d 和 m128i,分别代表Float、double (d) 和integer (i)。

AVX有3个数据类型: m256 , m256d 和 m256i,分别代表Float、double (d) 和 integer (i)。

img

SSE指令的数据类型

SSE指令有3种数据类型,分别为:

__m256、__m256i、__m256d。

每一种类型都以"__"+"m"+“vector的位长度”构成。

__m256

包含8个float类型数据的向量

__m256i

包含若干个整型数据的向量,如char、short、int、unsigned long long等。

例如256位的vector可以32个char、16个short、8个int,这些整型既可以是有符号的也可以是无符号的。

__m256d

包含4个double类型数据的向量。

指令

SSE指令命名约定

_mm256_<name>_<data_type>

<name>:描述了内联函数的算术操作。
<data_type>:标识函数主要参数的数据类型

从内存中加载数据

指令:_m256i _mm256_loadu_si256 (__m256i const * mem_addr)

从内存中读入一个256位的整型数据放到dst中(32字节地址无需对齐)。

dst[255:0] := MEM[mem_addr+255:mem_addr]
dst[MAX:256] := 0

指令:_m256i _mm256_load_si256 (__m256i const * mem_addr):

从内存中读入一个256位的整型数据放到dst中(32字节地址必需对齐)。

dst[255:0] := MEM[mem_addr+255:mem_addr]
dst[MAX:256] := 0
指令:_m256 _mm256_load_ps (float const * mem_addr):

从内存中读入8个float型数据放入dst(32字节地址必需对齐)。

dst[255:0] := MEM[mem_addr+255:mem_addr]
dst[MAX:256] := 0
指令:_m128 _mm_maskload_ps (float const * mem_addr, m128i mask) :

从内存中读入128位(4个float),根据mask的真假赋值。

FOR j := 0 to 3
    i := j*32
    IF mask[i+31]
        dst[i+31:i] := MEM[mem_addr+i+31:mem_addr+i]
    ELSE
        dst[i+31:i] := 0
    FI
ENDFOR
dst[MAX:128] := 0
指令:_m256 _mm256_maskload_ps (float const * mem_addr, m256i mask):

根据掩码载入8个float数据

FOR j := 0 to 7
	i := j*32
	IF mask[i+31]
		dst[i+31:i] := MEM[mem_addr+i+31:mem_addr+i]
	ELSE
		dst[i+31:i] := 0
	FI
ENDFOR
dst[MAX:256] := 0
指令:_m256 _mm256_add_ps (__m256 a, __m256 b):

将 a+b 操作按32位float进行处理,其中32位不能有溢出。

FOR j := 0 to 7
	i := j*32
	dst[i+31:i] := a[i+31:i] + b[i+31:i]
ENDFOR
dst[MAX:256] := 0
指令:_m256i _mm256_add_epi8 (__m256i a, __m256i b):

将 a+b 操作按8位整型进行处理,其中8位不能有溢出。

FOR j := 0 to 31
	i := j*8
	dst[i+7:i] := a[i+7:i] + b[i+7:i]
ENDFOR
dst[MAX:256] := 0
指令:_m256i _mm256_adds_epi8 (__m256i a, __m256i b):

将 a+b 操作按8位整型进行处理,考虑饱和问题。

FOR j := 0 to 31	i := j*8	dst[i+7:i] := Saturate8( a[i+7:i] + b[i+7:i] )ENDFORdst[MAX:256] := 0
指令:_m256i _mm256_adds_epu8 (__m256i a, __m256i b):

将 a+b 操作按8位无符号整型进行处理,考虑饱和问题。

FOR j := 0 to 31
	i := j*8
	dst[i+7:i] := SaturateU8( a[i+7:i] + b[i+7:i] )
ENDFOR
dst[MAX:256] := 0
指令:_mm256_fmadd_ps (__m256 a, __m256 b, __m256 c)

​ 将a*b+c操作按32位float型进行。不要溢出。

FOR j := 0 to 7
	i := j*32
	dst[i+31:i] := (a[i+31:i] * b[i+31:i]) + c[i+31:i]
ENDFOR
dst[MAX:256] := 0
指令:_m256 _mm256_fnmadd_ps (__m256 a, __m256 b, __m256 c)

将 -(a*b)+c 操作按32位float型进行。不要溢出。

FOR j := 0 to 7
	i := j*32
	dst[i+31:i] := -(a[i+31:i] * b[i+31:i]) + c[i+31:i]
ENDFOR	
dst[MAX:256] := 0
指令:_m256 _mm256_fmaddsub_ps (__m256 a, __m256 b, __m256 c)

将a*b+c操作按32位float型进行,偶数做减法,奇数做加法。

FOR j := 0 to 7
	i := j*32
	IF ((j & 1) == 0) 
		dst[i+31:i] := (a[i+31:i] * b[i+31:i]) - c[i+31:i]
	ELSE
		dst[i+31:i] := (a[i+31:i] * b[i+31:i]) + c[i+31:i]
	FI
ENDFOR
dst[MAX:256] := 0

AVX指令_mm256_fmadd_ps使用案例

下面一小段C++程序来展示一下AVX带来的计算速度:

#include <immintrin.h>
#include <iostream>
#include <chrono>
#include <ctime> 

const int N = 8;
const int loop_num = 100000000;
float gemfield_i[8] = {1.1,2.2,3.3,4.4,5.5,6.6,7.7,8.8};
float gemfield_m[8] = {2.2,3.3,4.4,5.5,6.6,7.7,8.8,9.9};
float gemfield_a[8] = {11.1,12.2,13.3,14.4,15.5,16.6,17.7,18.8};
float gemfield_o[8] = {0};

__m256 gemfield_v_i = _mm256_set_ps(8.8,7.7,6.6,5.5,4.4,3.3,2.2,1.1);
__m256 gemfield_v_m = _mm256_set_ps(9.9,8.8,7.7,6.6,5.5,4.4,3.3,2.2);
__m256 gemfield_v_a = _mm256_set_ps(18.8,17.7,16.6,15.5,14.4,13.3,12.2,11.1);
__m256 gemfield_v_o = _mm256_set_ps(0,0,0,0,0,0,0,0);


void syszuxMulAndAddV() {
    auto start = std::chrono::system_clock::now();
    for(int j=0; j<loop_num; j++){
        gemfield_v_o += _mm256_fmadd_ps(gemfield_v_i, gemfield_v_m, gemfield_v_a);
    }
    auto end = std::chrono::system_clock::now();
    std::chrono::duration<double> elapsed_seconds = end-start;
    std::cout << "resultV: ";
    // float* f = (float*)&gemfield_v_o;
    for(int i=0; i<N; i++){
        std::cout<<gemfield_v_o[i]<<" ";
    }
    std::cout<< "\nelapsed time: " << elapsed_seconds.count() << "s\n";
}

void syszuxMulAndAdd(){
    auto start = std::chrono::system_clock::now();
    for(int j=0; j<loop_num; j++){
        for(int i=0; i<N; i++){
            gemfield_o[i] += gemfield_i[i] * gemfield_m[i] + gemfield_a[i];
        }
    }
    auto end = std::chrono::system_clock::now();
    std::chrono::duration<double> elapsed_seconds = end-start;
    std::cout << "result: ";
    for(int i=0; i<8; i++){
        std::cout<<gemfield_o[i]<<" ";
    }
    std::cout<< "\nelapsed time: " << elapsed_seconds.count() << "s\n";
}

int main() {
    syszuxMulAndAdd();
    syszuxMulAndAddV();
    return 0;
}

编译并运行:

#compile civilnet.cpp
gemfield@ThinkPad-X1C:~$ g++ -march=skylake-avx512 civilnet.cpp -o civilnet

#run civilnet
gemfield@ThinkPad-X1C:~$ ./civilnet
result: 2.68435e+08 5.36871e+08 5.36871e+08 1.07374e+09 1.07374e+09 2.14748e+09 2.14748e+09 2.14748e+09 
elapsed time: 2.39723s
resultV: 2.68435e+08 5.36871e+08 5.36871e+08 1.07374e+09 1.07374e+09 2.14748e+09 2.14748e+09 2.14748e+09 
elapsed time: 0.325577s

速度比对

for loop计算消耗了2.39723秒,

而vectorization计算消耗了0.325577s,

可以看到AVX的计算速度远超for loop,因为AVX使用了下面这样的并行方式:

在这里插入图片描述

除了vectorization,还有什么可以让CPU计算速度更快?

如今的CPU并不是大多数程序员所想象的那个黑盒子——按照PC寄存器指向的地址load指令一条一条的执行,这样的CPU在486之后就灭绝了。

现代CPU(Intel Core2后,AMDBulldozer后)的管线宽度为4个uops,一个时钟周期内最多可以执行4条指令(如果同时有loads、stores和single-uop的ALU指令)。

因此,vectorization并不是CPU唯一一种并行计算的方式 。

指令层面同样有并行机制

在指令与指令层面同样有并行机制,可以让一个单独的CPU core在同一时间内执行多条CPU指令。

当排队中的多条CPU指令包含了loads、stores、ALU,多数现代的CPU可以在一个时钟周期内同时执行4条指令。平均下来,CPU在每个时钟周期内同时执行2条指令甚至更好——这仰仗于程序如何更好的优化。

多核的并行机制

比如,一个 cpu型号为“Core(TM) i9-9820X CPU”,cpu核为10个,使用超线程技术将CPU核扩展为20个逻辑核/线程数:

gemfield@AI3:~$ cat /proc/cpuinfo | grep -i "processor"
processor       : 0
processor       : 1
processor       : 2
processor       : 3
processor       : 4
processor       : 5
processor       : 6
processor       : 7
processor       : 8
processor       : 9
processor       : 10
processor       : 11
processor       : 12
processor       : 13
processor       : 14
processor       : 15
processor       : 16
processor       : 17
processor       : 18
processor       : 19

gemfield@AI3:~$ cat /proc/cpuinfo | grep -i "processor" | wc -l
20

在这台机器上,我们可以同时运行20个线程(因为20个核是由HT扩展出来的,真正能同时运行的线程数量位于10个到20个之间)。

只不过20个超线程对计算密集型的加速并非20倍(也即并非超线程数),而是10倍(也即cpu核数),

因此,假如一个CPU拥有20个逻辑核、10个CPU核,每个核的每个时钟周期平均执行2个vector计算,每个vector计算可以同时操作8个float数。

因此,至少在理论上,这个的机器可以在一个时钟周期内执行10 * 2 * 8 = 160个操作(当前,不同的指令有不同的吞吐量)。

系统维度的的并行机制

接下来,应用层的程序员还会熟悉这一点:多线程——在多个处理器核上同时运行多个指令序列。

这是 微观层面的cpu的时间片 调度方案。

ClickHouse中的列式存储

clickhouse就是列式储存

从数据存储讲起

我们最先接触的数据库系统,大部分都是行存储系统。

大学的时候学数据库,老师让我们将数据库想象成一张表格,每条数据记录就是一行数据,每行数据包含若干列。

所以我们对大部分数据存储的思维也就是一个复杂一点的表格管理系统。

我们在一行一行地写入数据,然后按查询条件查询过滤出我们想要的行记录。

大部分传统的关系型数据库,都是面向行来组织数据的。

如 Mysql,Postgresql。近几年,也越来越多传统数据库加入了列存储的能力。虽然列存储的技术在十几年前就已经出现,却从来没有像现在这样成为一种流行的存储组织方式。

行存储和列存储,是数据库底层组织数据的方式。(和文档型、K-V 型,时序型等概念不在一个层次)

列式存储与行式存储

首先先来看看,行式存储是怎么样的,下面那张表
在这里插入图片描述

当我们是行式存储的时候,数据是一行一行的存储的,如下图
在这里插入图片描述

但是当我们是列式存储的时候就不一样了,是一列一列存储的,如下图
在这里插入图片描述

Row-Store与 Column-Store对比

而clickhouse就是列式储存,但是行式存储跟列式存储有什么区别吗,或者说双方的优缺点是什么?

行式存储的优缺点:

  • 优点:
    • 数据都被保存到一起
    • 添加,修改,删除操作相对比较容易
  • 缺点:
    • 当你只是想要查询一条记录中的几列的时候,会把一条记录所有列的数据搜索出来,导致搜索太慢
  • 应用场景:
    • 适合随机的增删改查操作
    • 需要在行中选取所有属性的查询操作
    • 需要频繁插入或更新的操作,其操作与索引和行的大小更为相关

列式存储的优缺点:

  • 优点:
    • 查询时,只有涉及到的列会被读取,所以查询速度会相对较快
    • 投影很高效
    • 任何列都可以作为索引
  • 缺点:
    • 选择完成时,被选择的列要重新组装
    • 添加,修改,删除操作相对比较麻烦
  • 应用场景:
    • 查询需要大量行但是少数几个列
    • 用于存储海量数据,并且修改操作不多的场景
Row-StoreColumn-Store
因为按一行一行写和读取数据,因此读取数据时往往需要读取那些不必要的列可以只读取必要的列
易于按记录读写数据对一个一个记录的数据写入和读取都较慢
适合 OLTP 系统适合 OLAP 系统
不利于大数据集的聚合统计操作利于大数据集的数据聚合操作
不利于压缩数据利于压缩数据

列存储优势

基于列模式的存储,天然就会具备以下几个优点:

  • 自动索引

    因为基于列存储,所以每一列本身就相当于索引。所以在做一些需要索引的操作时,就不需要额外的数据结构来为此列创建合适的索引。

  • 利于数据压缩

    利于压缩有两个原因。一来你会发现大部分列数据基数其实是重复的,这就可以做数据压缩。列式存储具有数据压缩特性,数据压缩比率是由压缩算法、列的数据类型、数据重复度等决定的。如果列有唯一约束,那么列中每行的数据都是唯一的,数据压缩比率就低。在InnoDB和StoneDB下,分别向具有唯一值的列插入6000万条数据,InnoDB表大小16G多,StoneDB表大小5G多,压缩比率为3:1多,而一般情况下是可以达到10:1以上的。

Apache Druid 底层数据存储就是基于列模式,另外 HBase 是一个比较有代表性的列存储模式数据库。

ClickHouse的安装和使用

环境准备

从事服务器开发工作的都会遇到,linux下open_file的值默认是1024;max user processes(用户的线程数)的值默认是4096,在实际用于中,这两个值严重不足,常常需要调整这两个值。

在这里插入图片描述

具体的参数介绍,请参见视频

Clickhouse stack有一个单节点的Clickhouse服务容器和一个TabixUI作为Clickhouse的客户端。

Clickhouse官方暂时没有图形化界面操作,只支持命令行下操作很不方便,不过官网提到了几个第三方的图形化界面,包括Tabix。

官网:https://tabix.io/ Tabix是一个第三方的开源Clickhouse图形化界面,免费而且是基于浏览器访问。

注,ClickHouse需要使用的端口包括用于HTTP通信的8123端口和用于主机间通信的9000端口。

SSE4.2验证

验证是否支持SSE 4.2指令集,因为向量化执行需要用到这项特性

[root@cdh1 clickhouse-alone]# grep -q sse4_2 /proc/cpuinfo && echo "SSE 4.2 supported" || echo "SSE 4.2 not supported"
SSE 4.2 supported

如果不支持SSE指令集,则不能直接使用先前下载的预编译安装包,需要通过源
码编译特定的版本进行安装

Docker默认是不开启 IPv6 支持的,但是我们某些业务往往又需要 IPv6 的支持,特别是 IPv6 普及大势所趋,本文主要介绍的是如何开启 Docker 桥接网络 IPv6 支持

编辑 Docker 配置文件 /etc/docker/daemon.json,如果该文件不存在,请手动建立。配置文件内容如下,如果你已有的配置文件缺少相应的配置项,添加上即可,没有必要完全覆盖内容。

cat >/etc/docker/daemon.json <<EOF
{
"registry-mirrors":["https://almtd3fa.mirror.aliyuncs.com"]
}
EOF

vi /etc/docker/daemon.json

{
  "experimental": true,
  "ipv6": true,
  "ip6tables": true,
  "fixed-cidr-v6": "2607:f0d0:1002:51::/66"
}

ipv6设置为true,启用对ipv6的支持。
ip6tables,启用ip6tables,docker会在ip6tables中配置docker网络相关的规则链。
experimental,启用实验特性,ip6tables是docker的一个实验功能,所以需要设为true。

fixed-cidr-v6,配置ipv6子网。

添加之后

[root@cdh1 udemy-single-test]# cat /etc/docker/daemon.json

{

  "registry-mirrors":["https://almtd3fa.mirror.aliyuncs.com"],
  "experimental": true,
  "ipv6": true,
  "ip6tables": true,
  "fixed-cidr-v6": "2607:f0d0:1002:51::/66"
}

这里ip6tables是指由 Docker 自动配置 IPv6 的防火墙规则,如果你希望自己手动配置,请改为 false 或者移除此项,否则容器将无法连接 IPv6 网络;fixed-cidr-v6 则是我们划分的子网段的第一个,这里仅作示例请读者根据实际情况修改。

完成配置后请使用 systemctl restart docker 重启docker服务生效。完成此步后 Docker 算是完成对于 IPv6 的支持了

ClickHouse的安装

部署代码如下:

version: '3.5'

services:
  clickhouse-alone:
    container_name: clickhouse-alone
    image: yandex/clickhouse-server:20.4
    volumes:
      - ./data:/var/lib/clickhouse/
      - ./config.xml:/etc/clickhouse-server/config.xml
      - ./users.xml:/etc/clickhouse-server/users.xml
    ports:
      - "8123:8123"
      - "9000:9000"
      - "9009:9009"
      - "9004:9004"
    ulimits:
      nproc: 65535
      nofile:
        soft: 262144
        hard: 262144
    healthcheck:
      test: ["CMD", "wget", "--spider", "-q", "localhost:8123/ping"]
      interval: 30s
      timeout: 5s
      retries: 3
    deploy:
      resources:
        limits:
          cpus: '4'
          memory: 4096M
        reservations:
          memory: 4096M
    networks:
     ha-network-overlay:
       aliases:
        - clickhouse-alone
  web-client:
    container_name: web-client
    image: spoonest/clickhouse-tabix-web-client
    environment:
      - CH_NAME=dev
      - CH_HOST=127.0.0.1:8123
      - CH_LOGIN=default
    ports:
      - "18080:80"
    depends_on:
      - clickhouse-alone
    deploy:
      resources:
        limits:
          cpus: '0.1'
          memory: 128M
        reservations:
          memory: 128M
    networks:
     ha-network-overlay:
       aliases:
        - web-client
networks:
 ha-network-overlay:
  name: ha-network-overlay
  driver: bridge

具体的安装过程,请参见视频

[root@cdh1 clickhouse-alone]#  tail -f /home/docker-compose/clickhouse/clickhouse-alone/log/clickhouse-server.err.log
2022.09.21 19:51:31.083714 [ 1 ] {} <Warning> Access(disk): File /var/lib/clickhouse/access/users.list doesn't exist
2022.09.21 19:51:31.083739 [ 1 ] {} <Warning> Access(disk): Recovering lists in directory /var/lib/clickhouse/access/
^C
[root@cdh1 clickhouse-alone]#  tail -f /home/docker-compose/clickhouse/clickhouse-alone/log/clickhouse-server.log

连接ClickHouse

ClickHouse提供了两个种接口:

  • HTTP 易于直接使用。
  • 本机TCP 开销较小。

建议使用适当的工具或库来连接,Yandex官方支持以下方式:

  • 命令行客户端
  • JDBC驱动程序
  • ODBC驱动程序
  • C ++客户端库

非官方的第三方库工具:

这个就太多了,支持Java、Python、PHP、Go、C等各种语言的客户端库

0 三个默认的端口:

首先看下ClickHouse配置文件,默认对外开放以下端口:

  <http_port>8123</http_port>

  <tcp_port>9000</tcp_port>

  <mysql_port>9004</mysql_port>

clickhouse-client

docker exec -it clickhouse-alone clickhouse-client  --host 127.0.0.1 --port 9000 --database default --user clickhouse  --password='123456'

在这里插入图片描述

DBeaver

免费和开源的 DBeaver ,支持几乎所有的数据库,这当然也包括ch,而且是Yandex官方推荐哦。

https://dbeaver.io/download/

img

创建连接,可以在分析数据库中找到ch

img

配置好JCDB方式的连接

在这里插入图片描述

查看数据库对象和数据没有任何问题。

img

Tabix

Tabix 也是ch官方推荐的数据库管理工具,他的好处是单独部署一套web服务,用户通过浏览器就可以连接ch数据库,无需额外安装任何客户端,支持SQL语法。

img

安装很简单:

https://tabix.io/doc/Install/

连接CH,注意用http端口 8123

img

这种性冷淡风格,很好。

img

配置文件介绍

为了降低修改配置的带来的风险和便于维护管理,我们将默认的配置文件做了如下拆解。

img

users.xml

users.xml默认的users.xml,可分为三个部分用户设置

users:主要配置用户信息如账号、密码、访问ip等及对应的权限映射配额设置

quotas:用于追踪和限制用户一段时间内的资源使用参数权限

profiles:读写权限、内存、线程等大多数参数配置为了统一管理权限

我们在users.xml预定义了对应权限及资源的quotas及profiles,例如default_profile、readwrite_profile、readonly_profile等,新增用户无需单独配置quotas及profiles,直接关联预定义好的配置即可

users.d/xxx.xml

按不同的用户属性设置user配置,

每一个xml对应一组用户,每个用户关联users.xml中的不同权限quotas及profiles

users_copy/xxx.xml

每次有变更用户操作时备份指定属性的xml,方便回滚

metrika.xml

默认情况下包含集群的配置、zookeeper的配置、macros的配置,

当有集群节点变动时,通常需要将修改后的配置文件同步整个集群,而macros 是每个服务器独有的配置,

metrika.xml 一般建议进行拆解,如果不拆解很容易造成配置覆盖,引起macros混乱丢失数据,所以我们在metrika.xml 中只保留每台服务器通用的配置信息,而将独立的配置拆解出去

conf.d/xxx.xml

保存每台服务器独立的配置,如macros.xml

config_copy/xxx.xml

存放每次修改主配置时的备份文件,方便回滚

CH的数据类型

  1. 整形:固定长度的整形,包括有符号整型或无符号整型
    • 整型范围(-2n-1~2n-1-1):
      • Int8 - [-128 : 127]
      • Int16 - [-32768 : 32767]
      • Int32 - [-2147483648 : 2147483647]
      • Int64 - [-9223372036854775808 : 9223372036854775807]
    • 无符号整型范围(0~2n-1):
      • UInt8 - [0 : 255]
      • UInt16 - [0 : 65535]
      • UInt32 - [0 : 4294967295]
      • UInt64 - [0 : 18446744073709551615]
  2. 浮点型:一般数据值比较小,不涉及大量的统计计算,精度要求不高的时候。比如保存商品的重量
    • Float32 - float
    • Float64 – double
  3. 布尔型:没有单独的类型来存储布尔值。可以使用 UInt8 类型,取值限制为 0 或 1。
  4. Decimal 型:有符号的浮点数,可在加、减和乘法运算过程中保持精度。对于除法,最低有效数字会被丢弃(不舍入)。
    • 使用场景: 一般金额字段、汇率、利率等字段为了保证小数点精度,都使用 Decimal 进行存储。
    • Decimal32(s),相当于 Decimal(9-s,s),有效位数为 1~9
    • Decimal64(s),相当于 Decimal(18-s,s),有效位数为 1~18
    • Decimal128(s),相当于 Decimal(38-s,s),有效位数为 1~38
  5. 字符串
    • String:字符串可以任意长度的。它可以包含任意的字节集,包含空字节。
    • FixedString(N):固定长度 N 的字符串,N 必须是严格的正自然数。当服务端读取长度小于 N 的字符串时候,通过在字符串末尾添加空字节来达到 N 字节长度。 当服务端读取长度大于 N 的字符串时候,将返回错误消息。与 String 相比,极少会使用 FixedString,因为使用起来不是很方便。
    • 使用场景:名称、文字描述、字符型编码。 固定长度的可以保存一些定长的内容,比如一些编码,性别等但是考虑到一定的变化风险,带来收益不够明显,所以定长字符串使用意义有限。
  6. 枚举类型
    • 包括 Enum8 和 Enum16 类型。Enum 保存 ‘string’= integer 的对应关系
      • Enum8 用 ‘String’= Int8 对描述。
      • Enum16 用 ‘String’= Int16 对描述。
  7. 时间类型
    • Date 接受年-月-日的字符串比如 ‘2019-12-16’
    • Datetime 接受年-月-日 时:分:秒的字符串比如 ‘2019-12-16 20:50:10’
    • Datetime64 接受年-月-日 时:分:秒.亚秒的字符串比如‘2019-12-16 20:50:10.66’

CH表引擎

表引擎是Clickhouse 的一大特色。可以说,表引擎决定了如何存储表的数据。包括:

  • 数据的存储方式和位置,写到哪里以及从哪里读取数据。

  • 支持哪些查询以及如何支持。

  • 并发数据访问。

  • 索引的使用(如果存在)。

  • 是否可以执行多线程请求。

  • 数据复制参数。

  • 表引擎的使用方式就是必须显式在创建表时定义该表使用的引擎,以及引擎使用的相关参数。

TinyLog

以列文件的形式保存在磁盘上,不支持索引,没有并发控制。

一般保存少量数据的小表,生产环境上作用有限。可以用于平时练习测试使用。

Memory

内存引擎,数据以未压缩的原始形式直接保存在内存当中,服务器重启数据就会消失。

读写操作不会相互阻塞,不支持索引。简单查询下有非常非常高的性能表现。

一般用到它的地方不多,除了用来测试,就是在需要非常高的性能,同时数据量又不太大的场景

MergeTree

clickhouse中最强大的表引擎当属MergeTree引擎及该系列中的其他引擎,支持索引和分区,

MergeTree 地位可以相当于innodb之于Mysql。

而且基于MergeTree,还衍生除了很多小弟,也是非常有特色的引擎。

create table t_order_mt(
 id UInt32,
 sku_id String,
 total_amount Decimal(16,2),
 create_time Datetime
) engine =MergeTree
 partition by toYYYYMMDD(create_time)
 primary key (id)
 order by (id,sku_id);

ReplacingMergeTree

ReplacingMergeTree 是 MergeTree 的一个变种,

它存储特性完全继承 MergeTree,只是多了一个去重的功能。

尽管 MergeTree 可以设置主键,但是 primary key 其实没有唯一约束的功能。如果你想处理掉重复的数据,可以借助这个 ReplacingMergeTree。

去重的时机:

数据的去重只会在合并的过程中出现。

合并会在未知的时间在后台进行,所以你无法预测先作出计划。有一些数据可能仍未被处理。

去重的范围:

如果表经过了分区,去重只会在分区内部进行去重,不能执行跨分区的去重。所以ReplacingMergeTree只适用于在后台清楚重复的数据以节省空间,但是它不保证没有重复的数据出现

结论:

  • 实际上是使用 order by 字段作为唯一键
  • 去重不能跨分区
  • 只有同一批插入(新版本)或合并分区时才会进行去重
  • 认定重复的数据保留,版本字段值最大的
  • 如果版本字段相同则按插入顺序保留最后一笔

SummingMergeTree

对于不查询明细,只关心以维度进行汇总聚合结果的场景。

如果只使用普通的MergeTree的话,无论是存储空间的开销,还是查询时临时聚合的开销都比较大。

ClickHouse 为了这种场景,提供了一种能够“预聚合”的引擎 SummingMergeTree

案例演示:
在这里插入图片描述
在这里插入图片描述

结论:

  • 以 SummingMergeTree()中指定的列作为汇总数据列
  • 可以填写多列必须数字列,如果不填,以所有非维度列且为数字列的字段为汇总数据列
  • 以 order by 的列为准,作为维度列
  • 其他的列按插入顺序保留第一行
  • 不在一个分区的数据不会被聚合
  • 只有在同一批次插入(新版本)或分片合并时才会进行聚合

CH的基本操作

DDL建表

create table t_order_mt(
 id UInt32,
 sku_id String,
 total_amount Decimal(16,2),
 create_time Datetime
) engine =MergeTree
 partition by toYYYYMMDD(create_time)
 primary key (id)
 order by (id,sku_id);

  • partition by 表示的是分区,上述sql所用的就是说根据创建时间进行分区

  • primary key 代表的是主键,特点如下:

    • 并不会唯一
    • 索引
  • order by 代表的是根据那两个字段进行排序

在这里插入图片描述

尝试插入数据

insert into t_order_mt values
(101,'sku_001',1000.00,'2020-06-01 12:00:00') ,
(102,'sku_002',2000.00,'2020-06-01 11:00:00'),
(102,'sku_004',2500.00,'2020-06-01 12:00:00'),
(102,'sku_002',2000.00,'2020-06-01 13:00:00'),
(102,'sku_002',12000.00,'2020-06-01 13:00:00'),
(102,'sku_002',600.00,'2020-06-02 12:00:00');

在这里插入图片描述

在这里插入图片描述

通过命令执行 : SELECT * FROM t_order_mt tom

在这里插入图片描述

发现结果分为两部分, 是因为建表的时候,根据 create_time进行了分区

以上实操过程, 尼恩会在视频中,做详细解读

partition by 分区(可选)

作用:降低扫描的范围,优化查询速度

分区目录:

MergeTree是以列文件 + 索引文件 + 表定义文件组成的,但是如果设定了分区,那么这些文件就会保存到不同的分区目录中。

并行:

分区后,面对涉及跨分区的查询统计,Clickhouse会以分区为单位并行处理。

数据写入与分区合并:

任何一个批次的数据写入都会产生一个临时分区,不会纳入任何一个已有的分区。

写入后的某个时刻,clickhouse会自动执行合并操作,把临时分区的数据,合并到已有分区中。

分区文件目录:

  • bin文件:数据文件
  • mrk文件:标记文件
    • 标记文件在idx索引文件和bin数据文件之间起了桥梁作用
    • 以mrk2结尾的文件,表示该表启用了自适应索引间隔
  • primary.idx文件:主键索引文件,用于加快查询效率
  • minmax_create_time.idx:分区键的最大最小值
  • checksums.txt:校验文件,用于校验各个文件的正确性。存放各个文件的size以及hash值

分区id生成规则:

  • 未定义分区键:没有定义partition by, 默认生成一个目录名为all的数据分区,所有数据均存放在all目录下。

  • 整型分区键:分区键为整型,那么直接用该整型值的字符串形式做为分区ID

  • 日期类分区键:分区键为日期类型,或者可以转化为日期类型。

  • 其他类型分区键:String,Float类型等,通过128位的Hash算法取其Hash值作为分区ID

  • MinBlockNum:最小分区块编号,自增类型,从1开始向上递增。

  • MaxBlockNum:最大分区块编号,新创建的分区MinBlockNum等于MaxBlockNum的编号

primary key 主键(可选)

clickhouse中的主键只提供了数据的一级索引,但是却不是唯一约束。

这就意味着是可以存在相同primary key的数据的。

主键的设定主要依据: 是查询语句中的where条件

根据条件通过对主键进行某种形式的二分查找,能够定位到对应的索引粒度(index granularity),避免了全表扫描

index granularity:索引粒度,指在稀疏索引中两个相邻索引对应数据的间隔。

Clickhouse中的MergeTree默认是8192.

官方不建议修改这个值,除非该列存在大量重复值。

稀疏索引:

是可以用很少的索引数据,定位更多的数据,代价就是只能定位到索引粒度的第一行,然后再进行进行一点扫描。
在这里插入图片描述

order by(必选)

order by 设定了分区内的数据按照哪些字段顺序进行有序保存。

order by 是 MergeTree 中唯一一个必填项,甚至比 primary key 还重要,因为当用户不设置主键的情况,很多处理会依照 order by 的字段进行处理(比如后面会讲的去重和汇总)。

要求:

主键必须是 order by 字段的前缀字段。

比如 order by 字段是 (id,sku_id) , 那么主键必须是 id 或者(id,sku_id)

数据TTL

TTL 即 Time To Live,MergeTree 提供了可以管理数据表或者列的生命周期的功能。
在这里插入图片描述
在这里插入图片描述

SQL操作

Insert

在表内插入一条数据:

insert into [table_name] values(…),(….)

在表内插入一个表的数据:

insert into [table_name] select a,b,c from [table_name_2]

Update 和 Delete

ClickHouse 提供了 Delete 和 Update 的能力,这类操作被称为 Mutation 查询,它可以看做 Alter 的一种。

虽然可以实现修改和删除,但是和一般的 OLTP 数据库不一样,Mutation 语句是一种很“重”的操作,而且不支持事务。

“重”的原因主要是:

每次修改或者删除都会导致放弃目标数据的原有分区,重建新分区。

所以尽量做批量的变更,不要进行频繁小数据的操作。

删除操作:

alter table t_order_smt delete where sku_id =‘sku_001’;

修改操作:

alter table t_order_smt update total_amount=toDecimal32(2000.00,2) where id =102;

由于操作比较“重”,所以 Mutation 语句分两步执行,

  • 同步执行的部分其实只是进行新增数据新增分区和并把旧分区打上逻辑上的失效标记。
  • 直到触发分区合并的时候,才会删除旧数据释放磁盘空间,一般不会开放这样的功能给用户,由管理员完成。

查询操作

ClickHouse 基本上与标准 SQL 差别不大:

  • 支持子查询

  • 支持 CTE(Common Table Expression 公用表表达式 with 子句)

  • 支持各种 JOIN,但是 JOIN 操作无法使用缓存,所以即使是两次相同的 JOIN 语句,ClickHouse 也会视为两条新 SQL

  • GROUP BY

在这里插入图片描述

  • GROUP BY 操作增加了 with rollup\with cube\with total 用来计算小计和总计。

  • GROUP BY with rollup:从右至左去掉维度进行小计

    在这里插入图片描述

  • GROUP BY with cube : 从右至左去掉维度进行小计,再从左至右去掉维度进行小计

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gmJvnMuB-1663815310200)(C:\Users\nien\AppData\Roaming\Typora\typora-user-images\1663815123052.png)]

  • GROUP BY with totals: 只计算合计

    在这里插入图片描述

alter 操作

  • 新增字段:alter table tableName add column newcolname String after col1;
  • 修改字段类型:alter table tableName modify column newcolname String;
  • 删除字段:alter table tableName drop column newcolname;

以上实操过程, 尼恩会在视频中,做详细解读

CH高可用分片集群的多种架构

高可用集群的目标

高可用的目标是 4个9,甚至5个9

系统可用性(Availability)是信息工业界用来衡量一个信息系统提供持续服务的能力,它表示的是在给定时间区间内系统或者系统某一能力在特定环境中能够正常工作的概率。

简单地说, 可用性是平均故障间隔时间(MTBF)除以平均故障间隔时间(MTBF)和平均故障修复时间(MTTR)之和所得的结果, 即:

bd1fd60467d9841ccb86f3b4b6bd046e.png

通常业界习惯用N个9来表征系统可用性,表示系统可以正常使用时间与总时间(1年)之比,比如:

  • 99.9%代表3个9的可用性,意味着全年不可用时间在8.76小时以内,表示该系统在连续运行1年时间里最多可能的业务中断时间是8.76小时;
  • 99.99%代表4个9的可用性,意味着全年不可用时间在52.6分钟以内,表示该系统在连续运行1年时间里最多可能的业务中断时间是52.6分钟;
  • 99.999%代表5个9的可用性,意味着全年不可用时间必须保证在5.26分钟以内,缺少故障自动恢复机制的系统将很难达到5个9的高可用性。

那么X个9里的X只代表数字35,为什么没有12,也没有大于6的呢?

我们接着往下计算:

1个9:(1-90%)*365=36.5天 

*2个9:(1-99%)*365=3.65天 

6个9:(1-99.9999%)*365*24*60*60=31秒

可以看到1个9和、2个9分别表示一年时间内业务可能中断的时间是36.5天、3.65天,这种级别的可靠性或许还不配使用“可靠性”这个词;

而6个9则表示一年内业务中断时间最多是31秒,那么这个级别的可靠性并非实现不了,而是要做到从“5个9” 到“6个9”的可靠性提升的话,后者需要付出比前者几倍的成本。

高可用表引擎1: Distributed 分布式表

分布式引擎本身不存储数据, 但可以在多个服务器上进行分布式查询。

Distributed 分布式引擎可以理解为ES集群中的 ClientNode

读是自动并行的。读取时,远程服务器表的索引(如果有的话)会被使用。

我们可以引申理解它就相当于关系型数据库中的视图概念。

示例:ENGINE = Distributed(<集群名称>, <库名>, <表名>[, sharding_key])

与分布式表对应的是本地表,

也就是上面的<表名>参数,查询分布式表的时候,ClickHouse会自动查询所有分片,然后将结果汇总后返回

向分布式表插入数据

ClickHouse会根据分片权重将数据分散插入到各个分片中

默认情况下,每个分片中所有副本都会写入数据

或者通过参数internal_replication配置每个分片只写入其中一个副本,使用复制表(ReplicateMergeTree)管理数据的副本

高可用相关的表引擎2: ReplicatedMergeTree 复制表

ReplicatedMergeTree
ReplicatedSummingMergeTree
ReplicatedReplacingMergeTree
ReplicatedAggregatingMergeTree
ReplicatedCollapsingMergeTree
ReplicatedVersionedCollapsingMergetree
ReplicatedGraphiteMergeTree

注意:只有MergeTree系列引擎支持Replicated前缀

  • 副本是表级别的,不是整个服务器级的。所以,服务器里可以同时有复制表和非复制表

  • 副本不依赖分片。每个分片有它自己的独立副本

  • 数据副本使用到Zookeeper,需要在metrika.xml中配置zk的集群信息

  • SELECT 查询并不需要借助 ZooKeeper ,副本并不影响 SELECT 的性能,查询复制表与非复制表速度是一样的

  • 默认情况下,INSERT 语句仅等待一个副本写入成功后返回。

    如果数据只成功写入一个副本后该副本所在的服务器不再存在,则存储的数据会丢失。

    要启用数据写入多个副本才确认返回,使用 insert_quorum选项

  • 数据块会去重。

    对于被多次写的相同数据块(大小相同且具有相同顺序的相同行的数据块),该块仅会写入一次

示例:

ENGINE = ReplicatedMergeTree('/clickhouse/tables/{layer}-{shard}/table_name', '{replica}')

大括号中的参数是metrika.xml中macros配置的,

每个节点读取自己的配置信息,统一了建表语句

第一个参数用于zk中的目录结构,用了layer-shard名称分层

第二个参数是副本名,用于标识同一个表分片的不同副本,同个分片中不同副本的副本名称要唯一

CH高可用方案1:MergeTree 本地表 + Distributed分布式表

每个分片中只有一个副本,数据存储在 本地表(MergeTree),

查询分布式表,引擎自动向所有分片查询数据并计算后返回

img

优势

架构简单,单机和分布式都可以用

劣势

单点问题,数据丢失风险大

CH高可用方案2:MergeTree 本地表 + Distributed 分布式 + 多副本

在方案一的基础上为每个节点增加副本

img

优势

在1.0的基础上,数据安全有了保障,任何一个实例或者服务器挂掉了,不影响集群查询服务

劣势

如果某个节点挂了,恢复以后可以将丢失的增量数据补全,

但是如果硬盘彻底损坏,存量数据基本无法恢复,

且这种方案不能用两个节点互为主备,会造成数据错乱

CH高可用方案3:ReplicatedMergeTree + Distributed + 多副本

把2.0方案中的数据表引擎替换成 ReplicatedMergeTree,并设置分布式写入时, 只写入分片的一个节点:

internal_replication 设置为true

实现同一个分片中,写入一个节点的数据后,自动同步到其他的副本中

下图实现的是一个节点启动多个ClickHouse实例

img

优势

由 ReplicatedMergeTree 表引擎管理数据副本(依赖Zookeeper),无须担心节点挂掉后数据的同步和丢失问题

劣势

集群配置比较复杂, macros配置分片和副本需要仔细

metrika.xml配置

img

节点扩展

img

在ClickHouse集群中使用复制表引擎 ReplicatedMergeTree 建立本地表,

插入的数据会在ClickHouse的副本间进行自动复制,实现数据的高可用效果

实操:CH分布式集群方案

高可用集群的架构方案

首先来看下本节内容大致的架构:

img

如上图,整个集群一共 4 个节点,分为两个分片,每个分片一个副本。

除了在每个节点创建 ReplicatedMergeTree 表,还会创建 Distributed 引擎的总表,

Distributed 引擎的总表是 各个节点上的本地表的总代理,写入、查询、分发等操作都经过分布式总表路由。

ClickHouse的集群层级

ClickHouse的集群层级,对应metrika.xml配置中的macros节点:

集群《layer》 => 分片《shard》 => 副本《replica》 (每个ClickHouse实例都可以看做一个副本)

Cluster一个集群可以包括若干个Cluster
Shard一个Cluster可以包括若干个Shard
一个Shard又可以包含若干个Replicate
一个Replicate就是一个特定的节点实例

ClickHouse集群信息基于手工编写配置文件metrika.xml,默认加载/etc/metrika.xml,

为了方便管理我们在主配置文件中引用/etc/clickhouse-server/metrika.xml

集群搭建完毕后可查询系统表system.clusters,查看集群配置信息

metrika.xml 配置如下:

增加集群配置文件:metrika.xml文件

创建metrika.xml文件
在/etc/clickhouse-server/config.d/目录下创建metrika.xml

加入如下内容:

<yandex>
	<clickhouse_remote_servers>
	 <!-- 自定义的集群名称 -->
	 <!-- 2分片1副本 -->
	 <!-- 数据分片1 -->
		<cluster_1>
		<!-- 分片信息 -->
			<shard>
				<weight>1</weight> 
				<!-- 分布式表写入数据是否只写入到一个副本,配合复制表引擎使用,默认false -->
				<internal_replication>true</internal_replication>
				 <!-- 分片副本信息,这里指定的用户名密码只能是明文,如果需要密文密码需要将配置指向users.xml中的profile中 -->
				<replica>
					<host>clickhouse-01</host>
					<port>9000</port>
					<!--  <user>xxx</user>
                     <password>xxx</password>  -->
				</replica>
				<replica>
					<host>clickhouse-03</host>
					<port>9000</port>
				</replica>
			</shard>
			<shard>
				<weight>1</weight>
				<internal_replication>true</internal_replication>
				<replica>
					<host>clickhouse-02</host>
					<port>9000</port>
				</replica>
				<replica>
					<host>clickhouse-04</host>
					<port>9000</port>
				</replica>
			</shard>
		</cluster_1>
	</clickhouse_remote_servers>
        <zookeeper-servers>
            <node index="1">
                <host>clickhouse-zookeeper</host>
                <port>2181</port>
            </node>
        </zookeeper-servers>
        <networks>
            <ip>::/0</ip>
        </networks>
        <clickhouse_compression>
            <case>
                <min_part_size>10000000000</min_part_size>
                <min_part_size_ratio>0.01</min_part_size_ratio>
                <method>lz4</method>
            </case>
        </clickhouse_compression>
</yandex>


注意修改 zk 以及各个节点的信息。

分发到其他节点,同时修改macros标签里面的值为对应的服务器

以上配置实操过程, 尼恩会在视频中,做详细解读

引入metrika.xml

配置完metrika.xml后,我们需要将metrika.xml引入配置中。在config.xml引入metrika.xml, config.xml就是clickhouse的全局配置。

路径默认是:/etc/clickhouse-server/config.xml

在该配置文件中添加以下配置:

<!--引入metrika.xml-->
<include_from>/etc/clickhouse-server/config.d/metrika.xml</include_from>

macros.xml宏设置

每个实例都有自己的宏设置,如服务器1:

<yandex>
    <macros>
        <replica>clickhouse-01</replica>
        <shard>01</shard>
        <layer>01</layer>
    </macros>
</yandex>

确保宏设置等于metrika.xml中的远程服务器设置

启动集群

docker-compose up -d

关于集群的启动/设置/架构,请参加尼恩的视频

启动检查

1、查询当前的集群信息

select * from system.clusters;

在这里插入图片描述

2、查询zookeeper信息

在ClickHouse系统表中,提供了一张Zookeeper代理表,

我们可以使用SQL轻松访问Zookeeper内的数据,不用再像以前一样使用客户端登录进去查看。

#查询Zookeeper根目录
select * from system.zookeeper where path = '/'

在这里插入图片描述



#查询ClickHouse目录
select * from system.zookeeper where path = '/clickhouse'

在这里插入图片描述

生产环境建议配置上Kerberos安全认证。

以上具体的实操过程, 尼恩会在视频中,做详细解读

集群数据写入

创建本地表

现在我们有了群集和副本设置。接下来,需要在每个服务器中创建ReplicatedMergeTree表作为本地表。


CREATE TABLE ttt (id Int32) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{layer}-{shard}/ttt', '{replica}') PARTITION BY id ORDER BY id

先解释一下 ReplicatedMergeTree 引擎用法:

ENGINE = ReplicatedMergeTree('zk_path', 'replica_name')

zk_path :

用于指定在 zk 中创建数据表的路径,一般 zk_path 建议配置成如下形式:

/clickhouse/{cluster}/{shard}/{table_name}
  • {cluster} 表示集群名;
  • {shard} 表示分片编号;
  • {table_name} 表示数据表的名称。

需要注意的是: zk_path 的定义,同一分片不同副本,需要定义相同的路径。

replica_name

replica_name 用于设置副本名称

需要注意的是: 同一分片不同副本,需要定义不同的名称;

上面两句话如果感觉有点绕,可以对比下面这 4 个节点的本地表建表语句,应该就可以理解啦。

在 clickhouse-01上:

CREATE DATABASE likecolumn; 

CREATE TABLE likecolumn.table_test (label_id UInt32, label_name String, insert_time Date) ENGINE = ReplicatedMergeTree('/clickhouse/cluster_1/01/table_test','replica01',insert_time, (label_id, insert_time), 8192)

在这里插入图片描述

在 clickhouse-03上:

CREATE DATABASE likecolumn; 

CREATE TABLE likecolumn.table_test (label_id UInt32, label_name String, insert_time Date) ENGINE = 
ReplicatedMergeTree('/clickhouse/cluster_1/01/table_test','replica02',insert_time, (label_id, insert_time), 8192)

在这里插入图片描述

在clickhouse-02上:

CREATE DATABASE likecolumn;

CREATE TABLE likecolumn.table_test ( label_id UInt32, label_name String, insert_time Date) ENGINE = ReplicatedMergeTree('/clickhouse/cluster_1/02/table_test','replica01',insert_time, (label_id, insert_time), 8192)

在这里插入图片描述

在clickhouse-04上:

CREATE DATABASE likecolumn;

CREATE TABLE likecolumn.table_test ( label_id UInt32, label_name String, insert_time Date) ENGINE = ReplicatedMergeTree('/clickhouse/cluster_1/02/table_test','replica02',insert_time, (label_id, insert_time), 8192)

创建分布式总表

任选一个集群内的节点创建分布式总表:

CREATE TABLE likecolumn.table_test_all AS likecolumn.table_test ENGINE = Distributed(cluster_1, likecolumn, table_test, rand())

在这里插入图片描述

这里解释一下 Distributed 引擎用法: ENGINE = Distributed(cluster, database, table, sharding_key)

  • cluster:集群名
  • database 和 table:库表名
  • sharding_key:分片键,选填。

高可靠写入数据

在创建了分布式总表的节点写入数据:

insert into likecolumn.table_test_all values (1,'111','2020-11-17');
insert into likecolumn.table_test_all values (2,'111','2019-11-18');
insert into likecolumn.table_test_all values (3,'111','2020-11-19');
insert into likecolumn.table_test_all values (4,'111','2020-11-20');

在这里插入图片描述

在这里插入图片描述

查询总表数据

select * from likecolumn.table_test_all ;

在这里插入图片描述

查询各个分片本地表的数据。

select * from likecolumn.table_test ;

04节点

在这里插入图片描述

02节点

在这里插入图片描述

01节点

在这里插入图片描述

高可用测试

停掉 192.168.150.123 的 ClickHouse 服务:

docker-compose stop clickhouse-01
docker-compose start clickhouse-01

在这里插入图片描述

insert into likecolumn.table_test_all values (5,'111','2020-11-20');
select * from likecolumn.table_test_all ;

发现整个集群仍然可以正常写入和查询:

在这里插入图片描述

以上具体的实操过程, 尼恩会在视频中,做详细解读

Zookeeper在ClickHouse中的作用

Zookeeper作为一个分布式一致性存储服务,提供了丰富的读写接口和watch机制,

分布式应用基于Zookeeper可以解决很多常见问题,例如心跳管理、主备切换、分布式锁等

ClickHouse中依赖Zookeeper解决哪些问题?

(1)分布式DDL执行。

如:ClickHouse中DDL执行默认不是分布式的,

用户需要在DDL语句中加上on Cluster XXX的申明才能触发这个功能

(2)ReplicatedMergeTree表主备节点之间的状态同步

ClickHouse分布式DDL和其他完全分布式化的数据库有什么不同?

ClickHouse分布式DDL:

(1)ClickHouse对库、表的管理都是在存储节点级别独立的,集群中各节点之间的库、表元数据信息没有一致性约束

原因是:由ClickHouse的架构特色决定的

  • 彻底Share Nothing,各节点之间完全没有相互依赖

  • 节点完全对等,集群中的节点角色统一

    ClickHouse没有传统MPP数据库中的前端节点、Worker节点、元数据节点等概念

  • ClickHouse的这种架构特色决定它可以敏捷化、小规模部署,集群可以任意进行分裂、合并。前提要求是感知数据在集群节点上的分布

(2)用户可以直接连接任意一个节点进行请求,当用户发送DDL命令时,默认只会在当前连接的节点执行命令

思考:现实中如果用户有一个100台机器的集群,为了创建一个分布式存储的表难道用户需要依次连接每台机器发送DDL命令吗?

这个问题会导致:多个DDL之间的冲突问题无法解决

举例:用户A和用户B同时创建同名表但是表字段又不一致,这肯定会让系统陷入一个诡异的不一致状态

Cluster一个集群可以包括若干个Cluster
Shard一个Cluster可以包括若干个Shard
一个Shard又可以包含若干个Replicate
一个Replicate就是一个特定的节点实例

实现过程

思路:用户可以通过ClickHouse启动的config.xml来配置这套节点规划逻辑

如何配置:用户可以把一个集群规划成若干个Cluster,每个Cluster可自定义Shard数量,每个Shard又可以自定义副本数量

说明:单个存储节点内部不同Cluster之间的表都是相互可见的

传统的MPP数据库与Clickhouse的MPP数据库的区别?

传统的MPP数据库没有表级别的自定义副本数能力,只能做全库的副本数配置
Clickhouse的MPP数据库(1)ClickHouse能做到表的Replicate数量自定义技术核心
(2)是它把主备同步逻辑放到了具体的表引擎中实现,而不是在节点级别做数据复制

注意:当前只有ReplicatedMergeTree表引擎可以自动做主备状态同步,其他表引擎没有状态同步机制

如果用户需要在多副本Cluster下创建其他表引擎,如何做?

需要在写入链路上配置多写逻辑

ReplicatedMergeTree表引擎的同步包括哪些?

  • 写入同步、

  • 异步Merge同步、

  • 异步Mutation同步等;

注意:它所有的同步逻辑都是强依赖Zookeeper

分布式DDL执行链路

哪些操作是可以走分布式DDL执行链路?

ASTCreateQuery包括常见的建库、建表、建视图,还有ClickHouse独有的Attach Table(可以从存储文件中直接加载一个之前卸载的数据表)
ASTAlterQuery:包括ATTACH_PARTITION、FETCH_PARTITION、FREEZE_PARTITION、FREEZE_ALL等操作(对表的数据分区粒度进行操作)
ASTDropQuery:其中包含了三种不同的删除操作(Drop / Truncate / Detach),Detach Table和Attach Table对应,它是表的卸载动作,把表的存储目录整个移到专门的detach文件夹下
ASTOptimizeQuery:这是MergeTree表引擎特有的操作命令,它可以手动触发MergeTree表的合并动作,并可以强制数据分区下的所有Data Part合并成一个
ASTRenameQuery修改表名,可更改到不同库下
ASTKillQueryQuery:可以Kill正在运行的Query,也可以Kill之前发送的Mutation命令

ES和CK的查询对比

Elasticsearch 是一个实时的分布式搜索分析引擎,它的底层是构建在 Lucene 之上的。简单来说是通过扩展 Lucene 的搜索能力,使其具有分布式的功能。

ES 通常会和其它两个开源组件 Logstash(日志采集)和 Kibana(仪表盘)一起提供端到端的日志/搜索分析的功能,常常被简称为 ELK。

Clickhouse 是俄罗斯搜索巨头 Yandex 开发的面向列式存储的关系型数据库。ClickHouse 是过去两年中 OLAP 领域中最热门的,并于 2016 年开源。

ES 是最为流行的大数据日志和搜索解决方案,但是近几年来,它的江湖地位受到了一些挑战,许多公司已经开始把自己的日志解决方案从 ES 迁移到了 Clickhouse,这里就包括:携程,快手等公司。

架构和设计的对比

ES 的底层是 Lucence,主要是要解决搜索的问题。搜索是大数据领域要解决的一个常见的问题,就是在海量的数据量要如何按照条件找到需要的数据。搜索的核心技术是倒排索引和布隆过滤器。

ES 通过分布式技术,利用分片与副本机制,直接解决了集群下搜索性能与高可用的问题。

在这里插入图片描述

ElasticSearch 是为分布式设计的,有很好的扩展性,在一个典型的分布式配置中,每一个节点(node)可以配制成不同的角色。

在这里插入图片描述

如上图所示:

Client Node,负责 API 和数据的访问的节点,不存储/处理数据。Data Node,负责数据的存储和索引。Master Node,管理节点,负责 Cluster 中的节点的协调,不存储数据。ClickHouse 是基于 MPP 架构的分布式 ROLAP(关系 OLAP)分析引擎。每个节点都有同等的责任,并负责部分数据处理(不共享任何内容)。

ClickHouse 是一个真正的列式数据库管理系统(DBMS)。

在 ClickHouse 中,数据始终是按列存储的,包括矢量(向量或列块)执行的过程。

让查询变得更快,最简单且有效的方法是减少数据扫描范围,和减少数据传输时的大小,而列式存储和数据压缩就可以帮助实现上述两点。

Clickhouse 同时使用了日志合并树,稀疏索引和 CPU 功能(如 SIMD 单指令多数据)充分发挥了硬件优势,可实现高效的计算。

Clickhouse 使用 Zookeeper 进行分布式节点之间的协调。

img

为了支持搜索,Clickhouse 同样支持布隆过滤器。

对比的环境

ES stack
ES stack有一个单节点的Elastic的容器和一个Kibana容器组成,Elastic是被测目标之一,Kibana作为验证和辅助工具。部署代码如下:

version: '3.7'

services:
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:7.4.0
    container_name: elasticsearch
    environment:
      - xpack.security.enabled=false
      - discovery.type=single-node
    ulimits:
      memlock:
        soft: -1
        hard: -1
      nofile:
        soft: 65536
        hard: 65536
    cap_add:
      - IPC_LOCK
    volumes:
      - elasticsearch-data:/usr/share/elasticsearch/data
    ports:
      - 9200:9200
      - 9300:9300
    deploy:
      resources:
        limits:
          cpus: '4'
          memory: 4096M
        reservations:
          memory: 4096M

  kibana:
    container_name: kibana
    image: docker.elastic.co/kibana/kibana:7.4.0
    environment:
      - ELASTICSEARCH_HOSTS=http://elasticsearch:9200
    ports:
      - 5601:5601
    depends_on:
      - elasticsearch

volumes:
  elasticsearch-data:
    driver: local

Clickhouse stack
Clickhouse stack有一个单节点的Clickhouse服务容器和一个TabixUI作为Clickhouse的客户端。部署代码如下:

version: "3.7"
services:
  clickhouse:
    container_name: clickhouse
    image: yandex/clickhouse-server
    volumes:
      - ./data/config:/var/lib/clickhouse
    ports:
      - "8123:8123"
      - "9000:9000"
      - "9009:9009"
      - "9004:9004"
    ulimits:
      nproc: 65535
      nofile:
        soft: 262144
        hard: 262144
    healthcheck:
      test: ["CMD", "wget", "--spider", "-q", "localhost:8123/ping"]
      interval: 30s
      timeout: 5s
      retries: 3
    deploy:
      resources:
        limits:
          cpus: '4'
          memory: 4096M
        reservations:
          memory: 4096M
  
  tabixui:
    container_name: tabixui
    image: spoonest/clickhouse-tabix-web-client
    environment:
      - CH_NAME=dev
      - CH_HOST=127.0.0.1:8123
      - CH_LOGIN=default
    ports:
      - "18080:80"
    depends_on:
      - clickhouse
    deploy:
      resources:
        limits:
          cpus: '0.1'
          memory: 128M
        reservations:
          memory: 128M
  • 数据导入 stack
    数据导入部分使用了Vector.dev开发的vector,该工具和fluentd类似,都可以实现数据管道式的灵活的数据导入。
  • 测试控制 stack
    测试控制我使用了Jupyter,使用了ES和Clickhouse的Python SDK来进行查询的测试。

用Docker compose启动ES和Clickhouse的stack后,我们需要导入数据,我们利用Vector的generator功能,生成syslog,并同时导入ES和Clickhouse,在这之前,我们需要在Clickhouse上创建表。ES的索引没有固定模式,所以不需要事先创建索引。

创建表的代码如下:

CREATE TABLE default.syslog(
    application String,
    hostname String,
    message String,
    mid String,
    pid String,
    priority Int16,
    raw String,
    timestamp DateTime('UTC'),
    version Int16
) ENGINE = MergeTree()
    PARTITION BY toYYYYMMDD(timestamp)
    ORDER BY timestamp
    TTL timestamp + toIntervalMonth(1);

查询来做一个对比

ES使用自己的查询语言来进行查询,Clickhouse支持SQL,我简单测试了一些常见的查询,并对它们的功能做一些比较。

  • 返回所有的记录
# ES
{
  "query":{
    "match_all":{}
  }
}

# Clickhouse 
"SELECT * FROM syslog"
  • 匹配单个字段
# ES
{
  "query":{
    "match":{
      "hostname":"for.org"
    }
  }
}

# Clickhouse 
"SELECT * FROM syslog WHERE hostname='for.org'"
  • 匹配多个字段
# ES
{
  "query":{
    "multi_match":{
      "query":"up.com ahmadajmi",
        "fields":[
          "hostname",
          "application"
        ]
    }
  }
}

# Clickhouse、
"SELECT * FROM syslog WHERE hostname='for.org' OR application='ahmadajmi'"
  • 单词查找,查找包含特定单词的字段
# ES
{
  "query":{
    "term":{
      "message":"pretty"
    }
  }
}

# Clickhouse
"SELECT * FROM syslog WHERE lowerUTF8(raw) LIKE '%pretty%'"
  • 范围查询, 查找版本大于2的记录
# ES
{
  "query":{
    "range":{
      "version":{
        "gte":2
      }
    }
  }
}

# Clickhouse
"SELECT * FROM syslog WHERE version >= 2"

查找到存在某字段的记录

ES是文档类型的数据库,每一个文档的模式不固定,所以会存在某字段不存在的情况;

而Clickhouse对应为字段为空值

# ES
{
  "query":{
    "exists":{
      "field":"application"
    }
  }
}

# Clickhouse
"SELECT * FROM syslog WHERE application is not NULL"

正则表达式查询

正则表达式查询,查询匹配某个正则表达式的数据

# ES
{
  "query":{
    "regexp":{
      "hostname":{
        "value":"up.*",
          "flags":"ALL",
            "max_determinized_states":10000,
              "rewrite":"constant_score"
      }
    }
  }
}

# Clickhouse
"SELECT * FROM syslog WHERE match(hostname, 'up.*')"

聚合计数

统计某个字段出现的次数

# ES
{
  "aggs":{
    "version_count":{
      "value_count":{
        "field":"version"
      }
    }
  }
}

# Clickhouse
"SELECT count(version) FROM syslog"

聚合不重复的值,查找所有不重复的字段的个数

# ES
{
  "aggs":{
    "my-agg-name":{
      "cardinality":{
        "field":"priority"
      }
    }
  }
}

# Clickhouse
"SELECT count(distinct(priority)) FROM syslog "

ReplicatedMergeTree引擎

ReplicatedMergeTree是MergeTree的派生引擎,它在MergeTree的基础上加入了分布式协同的能力,只有使用了ReplicatedMergeTree 复制表系列引擎,才能应用副本的能力。

或者用一种更为直接的方式理解,即使用ReplicatedMergeTree的数据表就是副本

ReplicatedMergeTree与MergeTree的逻辑关系

ReplicatedMergeTree与MergeTree的逻辑关系, 如下图所示:

img

在MergeTree中,一个数据分区由开始创建到全部完成,会历经两类存储区域。

  • 内存

    数据首先会被写入内存缓冲区。

  • 本地磁盘

    数据接着会被写入tmp临时目录分区,待全部完成后,再将临时目录重命名为正式分区。

ReplicatedMergeTree如何做到数据复制的呢?

ReplicatedMergeTree在上述MergeTree基础之上增加了ZooKeeper的部分,

它会进一步在ZooKeeper内创建一系列的监听节点,并以此实现多个实例之间的通信。

在整个通信过程中,ZooKeeper并不会涉及表数据的传输。

ReplicatedMergeTree特点

作为数据副本的主要实现载体,ReplicatedMergeTree在设计上有一些显著特点。

  • 依赖ZooKeeper:在执行INSERTALTER查询的时候,ReplicatedMergeTree需要借助ZooKeeper的分布式协同能力,以实现多个副本之间的同步。

    但是在查询副本的时候,并不需要使用ZooKeeper。

  • 表级别的副本

    副本是在表级别定义的,

    所以每张表的副本配置都可以按照它的实际需求进行个性化定义,包括副本的数量,以及副本在集群内的分布位置等。

  • 多主架构(Multi Master)

    可以在任意一个副本上执行INSERTALTER查询,它们的效果是相同的。

    这些操作会借助ZooKeeper的协同能力被分发至每个副本以本地形式执行。

  • Block数据块

    在执行INSERT命令写入数据时,会依据 max_insert_block_size的大小(默认1048576行)将数据切分成若干个Block数据块。

    所以Block数据块是数据写入的基本单元,并且具有写入的原子性和唯一性。

  • 原子性

    在数据写入时,一个Block块内的数据要么全部写入成功,要么全部失败。

  • 唯一性

    在写入一个Block数据块的时候,会按照当前Block数据块的数据顺序、数据行和数据大小等指标,计算Hash信息摘要并记录在案。在此之后,如果某个待写入的Block数据块与先前已被写入的Block数据块拥有相同的Hash摘要(Block数据块内数据顺序、数据大小和数据行均相同),则该Block数据块会被忽略。

ZooKeeper内的节点结构

ReplicatedMergeTree需要依靠ZooKeeper的事件监听机制以实现各个副本之间的协同。

所以,在每张ReplicatedMergeTree表的创建过程中,它会以zk_path为根路径,在ZooKeeper中为这张表创建一组监听节点。

按照作用的不同,监听节点可以大致分成如下几类:

  • 元数据

    • /metadata:保存元数据信息,包括主键、分区键、采样表达式等。
    • /columns:保存列字段信息,包括列名称和数据类型。
    • /replicas:保存副本名称,对应设置参数中的replica_name
  • 判断标识

    • /leader_election:用于主副本的选举工作,主副本会主导MERGEMUTATION操作(ALTER DELETEALTER UPDATE)。这些任务在主副本完成之后再借助ZooKeeper将消息事件分发至其他副本。
    • /blocks:记录Block数据块的Hash信息摘要,以及对应的partition_id。通过Hash摘要能够判断Block数据块是否重复;通过partition_id,则能够找到需要同步的数据分区。
    • /block_numbers:按照分区的写入顺序,以相同的顺序记录partition_id。各个副本在本地进行MERGE时,都会依照相同的block_numbers顺序进行。
    • /quorum:记录quorum的数量,当至少有quorum数量的副本写入成功后,整个写操作才算成功。quorum的数量由insert_quorum参数控制,默认值为0。
  • 操作日志

    • /log:常规操作日志节点(INSERTMERGEDROP PARTITION),它是整个工作机制中最为重要的一环,保存了副本需要执行的任务指令。log使用了ZooKeeper的持久顺序型节点,每条指令的名称以log-为前缀递增,例如log-0000000000、log-0000000001等。每一个副本实例都会监听/log节点,当有新的指令加入时,它们会把指令加入副本各自的任务队列,并执行任务。

    • /mutations:MUTATION操作日志节点,作用与log日志类似,当执行ALERT DELETEALERT UPDATE查询时,操作指令会被添加到这个节点。mutations同样使用了ZooKeeper的持久顺序型节点,但是它的命名没有前缀,每条指令直接以递增数字的形式保存,例如0000000000、0000000001等。

    • /replicas/{replica_name}/

      *:每个副本各自的节点下的一组监听节点,用于指导副本在本地执行具体的任务指令,其中较为重要的节点有如下几个:

      • /queue:任务队列节点,用于执行具体的操作任务。当副本从/log或/mutations节点监听到操作指令时,会将执行任务添加至该节点下,并基于队列执行。
      • /log_pointer:log日志指针节点,记录了最后一次执行的log日志下标信息。
      • /mutation_pointer:mutations日志指针节点,记录了最后一次执行的mutations日志名称。

Entry日志对象的数据结构

ReplicatedMergeTree在ZooKeeper中有两组非常重要的父节点,那就/log/mutations。它们的作用犹如一座通信塔,是分发操作指令的信息通道,而发送指令的方式,则是为这些父节点添加子节点。

所有的副本实例,都会监听父节点的变化,当有子节点被添加时,它们能实时感知。这些被添加的子节点在ClickHouse中被统一抽象为Entry对象,而具体实现则由LogEntry和MutationEntry对象承载,分别对应/log/mutations节点

  • LogEntry
    • source replica:发送这条Log指令的副本来源,对应replica_name。
    • type:操作指令类型,主要有getmergemutate三种,分别对应从远程副本下载分区、合并分区和MUTATION操作。
    • block_id:当前分区的BlockID,对应/blocks路径下子节点的名称。
    • partition_name:当前分区目录的名称。
  • MutationEntry
    • source replica:发送这条MUTATION指令的副本来源,对应replica_name。
    • commands:操作指令,主要有ALTER DELETEALTER UPDATE
    • mutation_id:MUTATION操作的版本号。
    • partition_id:当前分区目录的ID。

副本协同的核心流程

副本协同的核心流程主要有INSERTMERGEMUTATIONALTER四种,分别对应了数据写入、分区合并、数据修改和元数据修改。INSERTALTER是分布式执行的,借助ZooKeeper的事件通知机制,多个副本之间会自动进行有效协同,但是它们不会使用ZooKeeper存储任何分区数据。而其他操作并不支持分布式执行,包括SELECTCREATEDROPRENAMEATTACH

在下列例子中,使用ReplicatedMergeTree实现一张拥有1分片、1副本的数据表来分别执行INSERTMERGEMUTATIONALTER操作,演示执行流程。

INSERT协同的核心流程

当需要在ReplicatedMergeTree中执行INSERT查询以写入数据时,即会进入INSERT核心流程,它的核心流程如下图所示

img

INSERT的核心执行流程

  1. 向副本A写入数据
  2. 由副本A推送Log日志
  3. 各个副本拉取Log日志
  4. 各个副本向远端副本发起下载请求
    • 选择一个远端的其他副本作为数据的下载来源。远端副本的选择算法大致是这样的:
      • /replicas节点拿到所有的副本节点。
      • 遍历这些副本,选取其中一个。选取的副本需要拥有最大的log_pointer下标,并且/queue子节点数量最少。log_pointer下标最大,意味着该副本执行的日志最多,数据应该更加完整;而/queue最小,则意味着该副本目前的任务执行负担较小。
  5. 远端副本响应其它副本的数据下载
  6. 各个副本下载数据并完成本地写入

INSERT的写入过程中,ZooKeeper不会进行任何实质性的数据传输。本着谁执行谁负责的原则,在这个案例中由CH5首先在本地写入了分区数据。之后,也由这个副本负责发送Log日志,通知其他副本下载数据。如果设置了insert_quorum并且insert_quorum>=2,则还会由该副本监控完成写入的副本数量。其他副本在接收到Log日志之后,会选择一个最合适的远端副本,点对点地下载分区数据。

MERGE协同的核心流程

当ReplicatedMergeTree触发分区合并动作时,即会进入这个部分的流程,它的核心流程如下图所示

img

MERGE的核心执行流程

无论MERGE操作从哪个副本发起,其合并计划都会交由主副本来制定。

  1. 创建远程连接,尝试与主副本通信
  2. 主副本接收通信
  3. 由主副本制定MERGE计划并推送Log日志
  4. 各个副本分别拉取Log日志
  5. 各个副本分别在本地执行MERGE

可以看到,在MERGE的合并过程中,ZooKeeper也不会进行任何实质性的数据传输,所有的合并操作,最终都是由各个副本在本地完成的。而无论合并动作在哪个副本被触发,都会首先被转交至主副本,再由主副本负责合并计划的制定、消息日志的推送以及对日志接收情况的监控。

MUTATION协同的核心流程

当对ReplicatedMergeTree执行ALTER DELETE或者ALTER UPDATE操作的时候(ClickHouse把DELETEUPDATE操作也加入到了ALTER TABLE的范畴中,它并不支持裸的DELETE或者UPDATE操作),即会进入MUTATION部分的逻辑

img

MUTATION的核心执行流程

MERGE类似,无论MUTATION操作从哪个副本发起,首先都会由主副本进行响应。

  1. 推送MUTATION日志
  2. 所有副本实例各自监听MUTATION日志
  3. 由主副本实例响应MUTATION日志并推送Log日志
  4. 各个副本实例分别拉取Log日志
  5. 各个副本实例分别在本地执行MUTATION

MUTATION的整个执行过程中,ZooKeeper同样不会进行任何实质性的数据传输。所有的MUTATION操作,最终都是由各个副本在本地完成的。而MUTATION操作是经过/mutations节点实现分发的。CH6负责了消息的推送。但是无论MUTATION动作从哪个副本被触发,之后都会被转交至主副本,再由主副本负责推送Log日志,以通知各个副本执行最终的MUTATION逻辑。同时也由主副本对日志接收的情况实行监控。

ALTER协同的核心流程

当对ReplicatedMergeTree执行ALTER操作进行元数据修改的时候,即会进入ALTER部分的逻辑,例如增加、删除表字段等,核心流程如下图

img

ALTER的核心执行流程

ALTER的流程与前几个相比简单很多,其执行过程中并不会涉及/log日志的分发,整个流程大致分成3个步骤

  1. 修改共享元数据
  2. 监听共享元数据变更并各自执行本地修改
  3. 确认所有副本完成修改

ALTER整个的执行过程中,ZooKeeper不会进行任何实质性的数据传输。所有的ALTER操作,最终都是由各个副本在本地完成的。本着谁执行谁负责的原则,在这个案例中由CH6负责对共享元数据的修改以及对各个副本修改进度的监控。

clickhouse的优越的性能

  • 与一些同类型产品对比

    img

    官网截图

  • 与其他分析型数据库对比

    img

    易观数据

    可以看出clickhouse在性能上有非常卓越的表现。但是这并不意味着它可以代替其他的查询数据库。

就像这样,时不时的,在使用numpy库或者各种Tensor张量库进行计算的时候,我们都会感叹这些库计算的速度之快,以至于远远超越自己写的for循环。然后,我们就会逐渐并且越来越多的听说到一个词——vectorization(向量化计算)——其带来了巨大的计算性能。

Clickhouse的不足:

没有完美的设计,只有适合的设计。
clickhouse也有自己的限制。

  • 不支持事务
  • 缺少高频率,低延迟的修改或删除已存在数据的能力。仅能用于批量删除或修改数据(性能不太好)
  • 稀疏索引使得ClickHouse不适合通过其键检索单行的点查询
  • 不支持大量的并发请求。每秒上百甚至更少

Clickhouse推荐使用场景

  1. 数据被添加到数据库,基本不怎么修改。
  2. 查询并发相对不高。
  3. 大宽表存储,少部分字段使用。
  4. 批量操作,更新或者删除。
  5. 列值相对小,数字或者短字符串。
  6. 无事务处理。

比如:用于存储数据和统计数据使用/ 用户行为数据记录和分析工作 / 日志分析

参考文献

京东 ClickHouse 高可用实践 https://blog.csdn.net/wypblog/article/details/120426701

https://www.jianshu.com/p/dfbe8eb3b26c

https://zhuanlan.zhihu.com/p/366421463

https://blog.csdn.net/sinat_41207450/article/details/126777357

http://events.jianshu.io/p/5fc49abc3119

https://zhuanlan.zhihu.com/p/72953129

https://blog.csdn.net/sinat_22510827/article/details/125939191

https://blog.csdn.net/fyire/article/details/120826881

https://www.jianshu.com/p/42d9dcd4f8cd

https://www.igvita.com/2012/02/06/sstable-and-log-structured-storage-leveldb/
http://www.benstopford.com/2015/02/14/log-structured-merge-trees/

https://www.jianshu.com/p/a30b814ee1fc

https://seaboat.blog.csdn.net/article/details/82976862

https://blog.csdn.net/jyxmust/article/details/89803733

https://article.itxueyuan.com/AWwJJE

https://blog.csdn.net/shangsongwww/article/details/103420171

https://blog.csdn.net/ws1296931325/article/details/86635751/

https://www.cnblogs.com/yjt1993/p/14522536.html

https://oreki.blog.csdn.net/article/details/117258004

https://cloud.tencent.com/developer/article/1986902

https://blog.csdn.net/qq_41308860/article/details/120401911


SAP-Garson
原文链接:https://www.cnblogs.com/crazymakercircle/p/16718469.html

文章来自于网络,如果侵犯了您的权益,请联系站长删除!

上一篇:智慧工厂数字孪生建设方案
下一篇:abap 报表开发 一些功能汇总
评论列表

发表评论

评论内容
昵称:
关联文章

clickhouse 底层原理&amp;amp; 可用
【转】“大数据”学习资源整理
的大数据学习资源(Awesome Big Data)
【转】的“大数据”学习资源整理
的“大数据”学习资源()(山东数漫江湖)
并发可用的 架构实践
构建并发可用的架构
【15W字长文】主从复制可用Redis,完整包含Redis所有知识点
构建并发可用的电商平台架构实践(
最新| ClickHouse入门、调优、实战一条龙解秘籍
并发可用的架构实践-剖析架构(三)
构建并发可用的电商平台架构实践
S4HC指南 (2) - 后台配置
在华为云 OSC 快速部署 EMQX MQTT
SAP 讲解牛smartforms
s云服务器 网站服,云服务器
|NO.Z.00001|——————————|BigDataEnd|——|Hadoop&amp;amp;OLAP_ClickHouse.V01|——|ClickHouse.v01|概述|
的“大数据”学习资源
构建并发可用的电商平台架构实践(转)
构建并发可用的电商平台架构实践

热门标签
CBP 问题处理 # ALV # 【SAP | 前世今生】 # 1.moonsec-2020-[持续更新] # ABAP # ABAP-接口 # abap学习路线 # ALV # AVRCP协议 # bdc # BMS项目实战记录 # BW # ClickHouse # crud 框架 (mybatis-plus/ jpa等) # dynpro # ERP # JCo3.0 # PyRFC # Python数据分析与机器学习 # SAP ABAP # SAP FICO # SAP FTP # SAP HANA # SAP MM # SAP-Restful # SAP消息号A类 # sap应用技巧 # 工具使用 # 数据库 # 网安神器篇 # 优化篇 # 语法 # 筑基08:渗透测试综合实验 (path.Combinee(rootDir, "nwrfcsdk", "icuuc50")) ,ides .NET .NET 6 .NET Core .NET Remoting和WebServices .net(C#) .NET/C# .netcore .NET技术 .NET连接SAP .UD选择集 /h /ui2/cl_json @click.prevent _E8_AE_BA_E6_96_87 ~ { ABAP} ~ ~{一起学ABAP}~ “SAP.Middleware.Connector.RfcConfigParameters”的类型初 《ABAP专栏》 《SAP ABAP基础通关百宝书》【从入门到精通】 《测绘程序设计精品案例合集》 《计算机网络自顶向下方法》学习笔记 【Azure 应用服务】 【SAP】ABAP-CDSVIEW 【速成之路】SQLserver 0.0-SAP BW学习 001-计算机基础 01检验类型 1 10.Abap 10.ABAP-CTS 102 1024程序员节 103 1155服务器装系统 12.SAP-SKILL 122 13台根服务器位置 15行 1809 1909 1核1g1m服务器相当于什么性能 2003服务器修改ftp密码 2010 2012服务器系统安装数据库 2012服务器系统安装数据库吗 2018年终总结 2019 2019java专科 2019年终总结之SAP项目实践篇 2022跨年烟花代码 2022年 2023云数据库技术沙龙 2023云数据库技术沙龙 “MySQL x ClickHouse” 专场 2-step picking 2-step拣配 2月一次的flyback 321 32位服务器系统安装教程 3D 40 408 408——计算机网络 408学习笔记 40位 478g+ 虚拟服务器 4hana 545移动类型 5G 6.824 60.技术开发 6------SAP 701 711 740新语法 7------SAP A a2dp AA AB01 ABAP ABAP 语法 ABAP AES加密解密 ABAP ALV abap alv 更改数据 abap alv新增行数据 ABAP AMDP abap bapi ABAP BAPI分享 ABAP BASE64加解密 ABAP BC400 ABAP CDS ABAP checkbox ABAP Dialog开发 ABAP DOI ABAP EXCEL ABAP Expression ABAP GUID ABAP Handy program abap hr ABAP IDOC abap java ABAP JSON ABAP JSON大小写 ABAP JSON驼峰 abap me21n增强 abap mm后台表 ABAP Modify 的用法 ABAP New ABAP REST API ABAP REST JSON ABAP RSA PSE ABAP RSA 加密解密 ABAP SAP ABAP SESSION传递 ABAP SMARTFORMS 默认 WORD 编辑 ABAP Table ABAP Toolbar ABAP tools ABAP wait abap xml 日期格式 ABAP 报错 ABAP 笔记 ABAP 常见错误 ABAP 程序开发 abap 程序模板 ABAP 初级技术 abap 创建出口历程 abap 调用java abap 发送json报文 ABAP 关键字 ABAP 基础知识 ABAP 技巧 ABAP 接口 ABAP 开发 ABAP 乱乱记 ABAP 内表 ABAP 内表 排序 abap 内表 条件查找 ABAP 配置相关 ABAP 批量创建货源清单 ABAP 屏幕开发激活显示 ABAP 人事模块 abap 上传excel数字去除千分符 ABAP 实用程序记录 ABAP 事务代码 ABAP 数据字典 ABAP 替换 ABAP 替换字符 ABAP 条件断点 DEBUG ABAP 未按大小排序 ABAP 销售模块 ABAP 新语法 ABAP 选择屏幕 ABAP 学习 ABAP 学习笔记 ABAP 一些常用技巧 ABAP 语法备忘 ABAP 增强 abap 指定长度服务器上传数据 ABAP 中级技术 abap 转换成字符串 ABAP 字符查找 abap 字符串操作 ABAP  屏幕流 ABAP 开发模块 ABAP/4 ABAP_01 ABAP_02 ABAP_BASIS ABAP_FUNCTION MODULE ABAP_OTHERS ABAP_SYNTAX ABAP_各路小技能 ABAP2XLSX ABAP4 ABAP7.50 ABAP740新语法 abapdata定义方法 abaper ABAP-FICO ABAP报表程序结构框架 ABAP报错 abap捕获当前功能键sy ABAP查找代码块 ABAP常用代码段 ABAP程序例子 ABAP初级 ABAP创建搜索帮助 ABAP打印 ABAP的BAPI ABAP调优 LOOP ABAP定时job abap动态变量 ABAP动态修改屏幕 abap读取sap服务器文件名 abap对接外围系统 abap分页 ABAP工具 ABAP关键字 ABAP函数 abap获取日期 ABAP基础 abap基础入门 ABAP基础语法 ABAP基础知识 ABAP技能树 ABAP技巧之游标 ABAP技术 abap技术栈 ABAP加密 ABAP-接口 ABAP开发 ABAP开发回顾总结 ABAP开发随便记录 ABAP开发学习 ABAP开发语言 abap开发注释快捷键 ABAP开源项目清单 ABAP快捷键 abap连接mysql ABAP模块 ABAP内表汇总 abap判断包含字符当中包含小数点 ABAP屏幕相关 ABAP其他增强 ABAP入门 ABAP时间戳 ABAP实例分享 ABAP使用技巧 abap视图字段限制 ABAP数据库删除 abap数据类型转换 ABAP四代增强 ABAP四舍五入 ABAP随笔 ABAP提取汉字 abap文件上传 abap文件下载导出 ABAP问题记录 abap系列 ABAP相关 ABAP小工具 ABAP小记 ABAP小技巧 ABAP校验时间日期格式 abap新语法 ABAP新语法汇总 ABAP新语法收集整理 ABAP修改删除数据 ABAP选择屏幕 ABAP选择屏幕开发 ABAP学习 ABAP学习记录 ABAP学习实用网址 abap语法 ABAP语法优化 ABAP语言 ABAP增强 ABAP知识点总结 ABAP指针 ABAP中RANGES的用法 ABAP中的同步和异步调用 abap字符串值变量 Abaqus ABLDT ABLDT_OI ABMA AC_DOCUMENT Account Group ACDOCA Activate ADD NEW FONT ADO.NET Adobe Form ADT AES AFAB/AFABN AFAMA AG1280 AirByte AJAB ajax AL11 ALE all in one Allocation Rule ALV ALV List ALV SEL_MODE alv 刷新 ALV报表 ALV横列单元格颜色 ALV模板 ALV鼠标右键 alv下拉 alv显示基础 ALV知识点 AMDP amp AMS系列产品 android android studio Android9设备打开WIFI热点 android不同版本风格 android模拟器 android热点流程 Android网络接入框架分析 Android系统开发 Angular angular.js ANSYS Ant Anywhere数据库监控 AO25 aof apache Apache DolphinScheduler API api document APM APO APO函数 APO开发 app App Service for Window application app测试 app服务器设计文档 app服务器数据库文件夹下 aps APT Architecture Archiving Area Menu arm arraylist ar路由器的虚拟服务器 ASAP asp.net asp.net MVC Assortment ATO Attribute AuCs authorization Automatic AutomaticScrg automation AVForamt AW01N Awesome Java awk awr AWS AWS SAP AWS SAP认证 aws认证 AWS战报 Azure Azure Storage B2B增长 Backflush BADI BANK Bank Account BAPI bapi sap 创建物料 BASE base64 bash BASIS Basis Consultant Questionnaire BASIS基础知识 BASIS模块 BASIS系统配置及操作 BASIS中遇到的问题 batch Batch Data Conversion BD87 BDC bdv021-clickHouse Beginning WF 4.0翻译 BGP路由器协议排错 bgRFC BI BI+BW+BO仓库管理 big data BigData ble bluetooth BO BOBF bom bom成本分析模型 bom更改编号 sap books bookv001——navigationing Boost完整实战教程 bootstrap BOPF BP BPC BPC开发 BP共用编码 BP和客商关联后台表 BP-客商 BP配置 bp配置 sap BP文档 break BRF+ BRFplus BSP BSTAT=U bt BTE BTEs BTP BUG BUG问题解决 BulkStorage BurpSuite插件 Business Suite BusinessPartner BUT000 BW BW/4 HANA BW4 bw4/ hana BW4/HANA BW4HANA BW报表使用操作手册 BW技术 BW建模 BW实施 ByteDance C# C# IO相关 C# sap集成 C# WPF C# 编程 C# 窗体应用 C# 读取txt文本数据 C# 读取文本每行每列数据 C# Stopwatch C#Winform C#编程 C#高级 C#格式转化 C#基础 C#基础知识 C#教程 C#入门经典 C#算法演义 c#学习 C#知识点笔记 C/4 C/4HANA c/c++ C++ C4C CA CS CO cad项目数据库服务器 Calculation CapacityCheck case when Cash Management cast CA周记 CBS CCNP题库 CDISC CDS CDS View CDS Views CDS视图 Cell Popin centos certificate CertificateType Change Log ChatGPT CHECK_ACCESS_KEYS CHECKBOX CheckBoxGroup Check按钮 chrome CI & CD CIO ci上传文件到不同服务器 cj20n sap 报错未知列的名称 CKM3 CKMLCP CL_GUI_ALV_GRID cl_ukm_facade Class ClickHouse clickhouse数据库 Client Copy CLIENTCOPY Cloud Cloud Native Cloud Platform CloudFoundry CMS CMU15-445 (Fall 2019) CO CO01 co88 sap 实际结算 COCA单词表 COCA高频单词 COCA核心词汇 COCA英语分频词汇 COCA英语语料库 CO-CCA CODE COGI COKEY Commerce Commvault Commvault技术知识点 Configuration connect_by_path ContentServer continue Control ControlQuantity CONV Conversion COPA COPC COPY来源 Cording Block Core Data Service(CDS View) CO控制 CO配置 CPI CPI技术小知识 CPLD CPM cpu CRM CRM系统 crm系统服务器要求 cross warehouse Crystal Reports CS CSharp CSI SAP2000 CSI SAP2000安装教程 css css3 CSV认证 CTCM ctf CTF-MISC CTF-Misc-wp CTS Customers CVI_CUST_LINK CVI_VEND_LINK C和C++Everything教程 C语言 C语言程序设计 Dapr Data Services Data sources database datagridview dataTable交换列 dataTable列操作 DATAX date DateNavigator DB DB LUW DB2 dba DBA 实战系列 DBCO DD08V DDIC DDS算法 debian debian云服务器项目 Debug debug方法 DEBUG改SAP表数据 Decal Decline demo DEMO程序 des DESADV DESTINATION DestinationProvider devexpress v22.1 devops DevSecOps DIalog Dictionary Encoding Diff discuz服务器系统 disk dms dns怎么修改默认服务器 docker docker容器 dom dont show this message again Driver E5调用API E5开发者 E5续订 EBS Ecc ECC_常用标准函数标准方法 ECC6 ECC6是否支持linux7 echarts eclips Eclipse eclipse报错 ECM ecmascript ECM企业管理 ecn EDI EDIT Ehancement EHP EHP4 EHP8 elasticsearch elementui ELT emqx English Enhancement enhancement MBCF0007 Enterprise Servers and Development Entity Linking Enumeration EOS空项目添加服务器 EPIC EPIC_PROC epoll EPPM erp erp oracle数据库连接失败 ERP 增强 erp5 ERP-SAP erp服务器系统分区多大 ERP供应链 ERP实施 erp无线架设服务器 ERP系统 erp系统 服务器在哪里的 ERP项目 ERP小讲堂 es6 esb ESP8266 esri ESXI ETBAS二次开发 eth节点计划服务器维护 ETL etl工程师 ETL工具 ETL开发规范 ETL社区版 ETL数据集成 ETO events EWM EWM模块 Example examples EXCEL Excel服务器数据库修改 Exception EXCLUDING express F.13 F-02 F110 F5080 FAA_CMP_LDT FAGL_FC_VAL FAGLGVTR FB05 FBB1 FBL1N ffmpeg FI FI01 FI12 FI12_HBANK FI-AA FICO fico bapi FICO Integration FICO-AA FICO模块 FICO-年结 FICO问题点 FICO-月结 FICO增强 field-symbols fifaol服务器不稳定 file Fine finereport FINSC_LEDGER Fiori fiori 2.0 fiori app configuration fiori launchpad Fiori-Web FIORI配置 Fixed point arithmetic FixedStorageBin FI财务 FI金额 FI配置 FLCU00 flex FLVN00 FM Focus FONT FONTS For FOR ALL ENTRIES IN FPGA fpga开发 FPGA项目例子总结 FPM framework freemarker Freight标签页 freshman to ABAP FS15会计科目扩充 FTP ftp 网页如何上传到服务器 ftp传输文件到其他服务器 ftp服务器存放文档 ftp服务器端文件大小设置 ftp服务器设置上文件大小 ftp服务器生成xml文件 FTP服务器收不到传送的文件 ftp服务器数据存放位置 ftp服务器文件路径怎么写 ftp服务器限制文件大小 function Function ALV Function Modules functional programming Functions Game Gartner Gateway GATEWAY100 GBase gdal GeneXus GeneXus 2021 gentoo 安装php7 GeoTools GET Parameter GIS Git github Gizmos gnu go google Google 微软 亚马逊 阿里 腾讯 字节跳动面试总结 GR GR Date GR/IR GR/IR余额清单 GRaph Process groovy GroupNumber gui GUI STATUS gui740的消息服务器 GUID GW100 H3c 服务器bmc管理芯片 h3c服务器 raid 型号 h3虚拟服务器 h5修改服务器数据 hadoop HAHA SQL halcon HANA HANA Advanced Data Modeling HANA Advanced Data Modeling 读书笔记 HANA DB HANA DBA hana s4 服务器 HANA SQL hana sql mysql oracle HANA SQLScript HANA Studio HANA VIEW hana vs oracle hana 表空间 hana 查看表字段 HANA 导入数据 hana 服务器性能测试 HANA Studio HANA安装 hana查询去重 HANA常用函数 hana抽数到mysql hana的date对应oracle日期 hana服务器销售资质 HANA进阶学习 hana生产系统服务器 HANA实战 hana数据库 hana数据库 字段长度 hana数据库导入mysql hana数据库导入到oracle hana数据库服务器文件丢失 hana数据库教程php hana数据库连接mysql hana数据库连接oracle hana数据库与mysql HANA信息建模 Hana性能优化 hana修改字段 HANA学习 hana语法 HANA在线日志 Hashid hash-identifier hbase HCM HCP HDI Container HEC hibernate hierarchy Hints his系统服务器数据存在哪里 His系统数据库服务器关系 hive HNUST湖南科技大学计科专业考试复习资料 hp380G5服务器系统安装 hp服务器产品文档 HR HR模块 HR薪资发放过账 HR增强 HTAP HTAP for MySQL html html5 HTML5/CSS/Bootstrap http http://95u.free.fr/index.php httpcompnents https https://mp.weixin.qq.com/s/keb HU Hybris I/F IBAN IBP ICF ID ide idea idea中项目如何上传到服务器中 IDES IDoc idoc java IDOC技术 IDT ifm_research_notes IFRS16 iis ftp服务器文件大小 ijkplayer IM image imessage IMG子菜单 import IM层面 Include Informatica inspection point intellij idea Inter-company Intergration Internal table Interview INVOIC ios iot IP ipad协议 ipfs存储服务器销售 IQ02 IQ09 IR IRPA ISO IS-RETAIL issue IT IT - Linux ITS ityangjia IT技术 IT企划 IT生涯 IT项目与团队 IT养家 j2ee J3RCALD jar Java java b1 b1 be a9 Java Connector java jco sap 重连 JAVA PI PO SOAP JAVA PO SOAP java sap总账凭证接口 java webservice调用sap Java Why java 访问hana java 薪水完爆abap JavaScript javaSE基础篇 Java并发 Java调用SAP java调用sap接口 JAVA调用SAP接口地址 java对接sap java更换sap配置不生效 Java工具类 JAVA工作日常 java函数调用报错 java获取hana接口数据 java获取sap数据 java开发 java连接hana java连接sap Java连接sap无明显报错信息 java实战 java项目所需服务器 JAVA学习 java云服务器怎么上传文件大小 java怎么安装apple JAVA重点部分的笔记 java转sap hybris方向 JCo jco.client.saprouter JCo3 JCO连接 jdbc JDBC连接 JDK jira JOC Join JOIN 内表 jpa jquery js json json 服务器 文件 js基础笔记 junit JVM jwt K3 kafka KANBAN KE24 kernel kettle KEY kohana KP06与KP26 KSU5 KSV5 kubernetes labview lambda lamp LAN leetcode LEFT DELETING LEADING LENGTH Leonardo less linq Linux linux 64位vcs linux hana linux hana 版本查询 linux 安装sap linux 划分两个VDisk linux 命令是 的sap linux64 solvers Linux查看hana数据库进程 linux登录Hana数据库 linux调用rfc函数配置 Linux开发分享 Linux启动SAP服务 linux如何查看MBFE版本信息 Linux网络 linux系统的服务器怎么重启 linux相关 linux中停sap服务 lisp list LISTING Lock Logic LogicSystem lpfs存储服务器怎样维护 LQ02 LSETBF01 LSMW LT23 LT41 LT42 LT45 LTMC LTMC和LSMW等 LTMOM LX03 LX09 LX10 LX11 LX12 LX29 LX39 M_MSEG_LGO mac mac os x macos Mail makefile Manage Banks manager mariadb Markdown mass MASTER DATA MAST表 matdoc Material Group Material Ledger MaterialSpec matplotlib matrix maven MaxDB MaxWeight MB04 MB51清单格式 MB5B MB5M MBSM MBST MBST冲销 mcu md01和md02区别 MD04 MD04中例外信息30 MDBS MDG MDG 2021 MDG 2022 MDG BP MDG顾问 MDG项目 ME me15 me21nme22nme23n增强ME_ ME22N ME57界面看到的供应源跟Source List主数据不一致 MEBV memcached MES Mesh Message Messages MetaERP Method List MF47和COGI MI10 MIBC microsoft Microsoft Access Microsoft Azure Microsoft365 E5 MIGO MIGO 241 migo 311 MIGO+201 migo初始化库存 s4 MIGO事务代码 MIGO增强 MIGO子功能 migration Migration cock MIRO MIRO发票校验 MIRO发票校验多采购订单选择 mkpf ml MM mm bapi MM/SD mm17 MM41创建的商品主数据 MM41创建商品主数据 MM60 MMBE MMPV MMSC MM-报表功能开发 MM-采购管理 MM-采购审批 MM常用BAPI MM-定价过程 MM更改物料类型 MM顾问 MM教程 MM模块 MM配置 MM物料管理 mobile MODIFY table MOVE TO movement type mp3 MP38 MPN MPN物料的采购初探 mps MQTT mqtt服务器数据存储位置 mqtt协议库服务器 MRP MRP标识 MRP处理代码 MRP过程 MRP组 MS SQL mseg mssql MTE MTO MTO/MTS MTS MTS/MTO/ATO/ETO MTS/MTO/ETO Mule ESB 开发 Mule ESB 社区版 实施 Mule ESB 实施 Mule ESB开发 Mule ESB社区版实施 Mule ESB实施 MultipleBOM MultipleSpecifications MultipleSpecs Muxer mvc MWSI mybatis mybatis-plus myeclipse mysql mysql 1060指定的服务未安装 mysql hana数据同步 mysql版本情况 Mysql等数据库 MySQL高级 mysql和hana mysql数据库停库停不下来 MZ SAP FICO精讲视频 MZ SAP那些事 nagios name_mappings Naming Convention NAST nas怎么备份服务器文件夹 NativeLibrary.Load nat服务器性能 nc 二次开发 NCO NCO3.0 nc文件服务器 数据库文件 NDSS NetSuite 案例 NetSuite新闻 Netweaver network New NineData nlp Node node.js nodejs nokia NoSQL NOTE npm null Number Range numbers numpy NW751 nwa key-storage NWBC NX文档服务器 o365 OA OAAQ OABL oa办公 OB07 OB08 OB13 OB52 OB62 OB74 OBBH OBJK ObjType OBR1 OBR2 OBR3 OBYC-DIF OBYC-PRD oceanbase ocx OData odbc odoo office OI-题解 olap OMIR OMSJ OMSY OMX6 Onenote_DB Onenote_Others onetime vendor On-premise OO OOALV OOALV进阶 OOALV增删改查 OPEN open item OPEN SQL Open Storage Opengauss openGauss核心技术 OPENSAP UI5 扫盲 OPENSQL Openui5 openwrt系统安装到云服务器异常 ops$ oracle数据库用户 ora 01005 linux Oracle oracle 60401 oracle clob minus oracle dba Oracle EBS oracle e-business suite 下载 Oracle ERP oracle ftp 文件乱码 oracle hana 字段长度 oracle logon 乱码 oracle nid ora 24324 oracle sap 备份 oracle sap金蝶 oracle set newpage Oracle Tuning oracle 抽数据到 hana oracle 创建一揽子协议 oracle 打开数据库三步 oracle 应用系统 oracle创建服务出错1073 oracle和netsuite培训 Oracle数据库 oracle数据库恢复版本不一致 oracle与用友的差别 OS other Others Outbound Overtime p2p PA PaaS PACKAGE SIZE Pandas parallel Parameter Partner payment Payment method Payment Terms PA认证 PB00 PBXX PC PC00_M99_CIPE PCo PCP0 PC安装服务器系统 PDA pdf performance PE安装服务器系统6 PFCG PGI Pharos(小白路标) php php功能函数 PHP开发erp功能模块 php连接sap hana数据库 php清理服务器文件大小 php与sap系统 php转行自学java PhysicalSamples PI PI/PO ping pip PIPO PIR PI接口常见问题处理 pi节点虚拟服务器怎么弄 Plant Group PLG PLG Application跳转传参 plm PLSQL PLSQL13 PLSQL弹出框 PM pmp pms PMW PO po 价格条件表 PO&amp poi PolarDB Popup Port Portal POS POS Interface PostgreSQL posting key postman Postman 接口测试 Power BI PowerBI PowerBuilder Powered by 金山文档 powerpoint PowerQuery&amp PO接口常见问题处理 PO中基于GR的IV清单 PP PP &amp PP Module PPM PP模块 pp模块常用表 sap PP生产订单 PP生产过程 PR PREPACK Pricing Print PROCEDURE Product Hierarchy project management PS PS模块 pu Purchase Purchase Order History Categor pyautogui pycharm python Python Golang 人工智能 机器学习 图像处理 Python场景积累 python获取sap数据 Python基础 PYTHON接口开发 python连接sap接口 python能连sap吗 python学习 python与sap QA08 QA11 QC51 QE01 QE23 QM QM Control Key QM采购质量管理 QM质量管理 QP01 qRFC QS28 QS61 qt qt5 Quality Certificate Quant QUERY R3 rabbitmq rac 服务器 修改时间 RadioButtonGroup Random react react.js READ receive idoc redhat redis REDUCE Reflex WMS REM REP Report ReRAM rest REST ADAPTER RESTful RETAIL ReturnDelivery RFC rfcv函数实现 RFC查询SAP数据库 rfc方式的集成 sap RFC封装WEBService RFC函数 rfc垮端口 sap RFSEPA02 RIGHT DELETING TRAILING Rollout project Routing RPA RPA机器人 RPA机器人流程自动化 RPA魔力象限 RPA资讯 RPC0 RSA RSA Encryption RSA PRIVATE KEY RSS RTMP协议云服务器 runtime rust RV_ORDER_FLOW RWBE r语言 R语言入门课 S/4 S/4 HANA S/4 HANA 1809 S/4HANA S/4HANA 2020 S/4HANA 2021 S/4HANA 2022 S/4HANA迁移 S/4补0 去0 s_alr_87013127 S_ALR_87013611 S_ALR_870136XX s2k S4 S4 CLOUD/ FIORI S4 CRM S4 HANA s4 hana ecc S4 HANA 功能变化清单 S4 HANA数据迁移工具 S4 HAVA S4 Kernel S4CRM S4H PA S4HANA S4HANA Conversion S4HC S4HC产品相关 S4新表ACDOCA S4新型数据导入工具 saas SAC Sales Area SALES PRICE SampleSize SAP sap abap SAP ABAP学习 SAP Basis SAP / 后台配置 SAP 1809 sap 46c oracle 从unix 迁移至 windows SAP ABAP SAP ABAP  Excel模板上传及Excel数据批导 SAP ABAP AES128 SAP ABAP AES256 SAP ABAP for HANA SAP ABAP HANA SAP ABAP Runtime Error SAP ABAP SHA512 SAP ABAP 编程教程 SAP ABAP 并发 SAP ABAP 核心代码 SAP ABAP 基础 学习 SAP ABAP 李斌的分享笔记本 SAP ABAP 问题整理 SAP ABAP 学习资料 SAP ABAP 增强 SAP ABAP(总结) sap abap接口篇 SAP ABAP开发 sap abap开发从入门到精通 SAP ABAP开发实战——从入门到精通 SAP ABAP开发问题记录 SAP ABAP开发专栏 SAP ABAP零碎知识 SAP ABAP浅尝截止 SAP ABAP实例大全 SAP ABAP性能优化 SAP ABAP增强 SAP ABAP自学教程 SAP Adapter SAP Adobe Form SAP AES加密解密 SAP ALE SAP ALV SAP Analytics Cloud sap and oracle SAP APO SAP APO 介绍 SAP Ariba SAP ARM SAP B1 SAP B1 License Serve SAP B1原创 SAP BAPI SAP Basis SAP Basis Tips SAP Basis 系统学习 SAP Basis&amp SAP BDC SAP BDC MODE SAP BDC模式 SAP BI on HANA SAP BO SAP BOBF/FPM/WEBDYNPRO SAP BOBJ SAP BOM反查 SAP BOM记录查询 SAP BOM修改记录 SAP BP SAP BTP SAP business one SAP Business One 二次开 SAP BW sap bw、echar、smart bi sap bw4 sap C/4HANA SAP C4C SAP CAR sap cds view SAP client2.0 download SAP Cloud SAP Cloud Platform SAP Cloud Platform Cockpit SAP CO SAP Consultancy SAP CP SAP CPI SAP CRM sap crm button SAP Data Service sap dbco访问oracle SAP DEMO数据增加 SAP Dialog调用 SAP Dialog开发 SAP Dialog学习 SAP ECC SAP ECC6 SAP ECC6 / CO SAP ECC6 / FI SAP EDI SAP EPIC SAP ERP SAP ERP系统 SAP EWM SAP excel数据导入 SAP FI sap fi  凭证跳号 SAP FI-AA SAP FICO SAP FICO 报错处理办法 SAP FICO 开发说明书03(源代码仅做参考) SAP FICO 系统配置 SAP FICO 资料免费分享 SAP FICO开发说明书_01(源代码仅作参考) SAP FICO开发说明书_02(源代码仅作参考) SAP Fiori SAP Fiori & SAP(open) UI5 SAP Fiori 开发实践 SAP FM SAP freelancer SAP Frori SAP Gateway SAP GUI sap gui script SAP GUI 登录不需要密码 SAP GUI 界面 SAP GUI 快捷方式密码 SAP GUI 密码保存 SAP GUI 免密登录 SAP GUI 主题 SAP GUI 主题切换 SAP GUI+WEBGUI SAP GUI界面切换 SAP GUI密码设定 SAP GUI切换 SAP HAN SAP HANA SAP HANA Hint sap hana oracle exadata SAP HANA SDI sap hana 迁移 oracle SAP HANA 数据库学习 SAP HANA  上云 SAP HANA2.0 SAP HANA总结 SAP HCM SAP HCM学习 SAP HR sap http SAP IBP SAP IDOC sap idoc java SAP INBOX SAP IRPA SAP ISSUE sap java客户端 sap java乱码 SAP JCO NCO SAP JCO 负载均衡 SAP License sap linux客户端 sap linux系统安装教程 sap linux下配置文件 SAP List Viewer(ALV) SAP LOGON SAP LSMW SAP LSMW教程 SAP LUW SAP MASS SAP material classification SAP MDG SAP ME sap me21n增强 sap me22n增强 sap me23n增强 sap mes java SAP MII SAP MM SAP MM BAPI SAP MM 对于MRKO事务代码的几点优化建议 SAP MM 后台配置 SAP MM 特殊库存之T库存初探 SAP MM 小贴士 SAP MM/SD 业务相关 SAP MM06 SAP MM基础配置 SAP MM模块面试 SAP MRP默认值 SAP MRP默认值设置 SAP MRP配置 sap mysql SAP Native SQL SAP Nco 3 Connector 连接SAP 并接收数据 SAP NetWeaver sap netweaver 7.02 sap netweaver application server java SAP NetWeaver RFC library SAP NWBC sap nwds as java SAP ODATA SAP OData 开发实战教程 - 从入门到提高 sap oracle client SAP PA证书 SAP PI SAP PI - 同步 vs. 异步 SAP PI PO 接口调用 SAP PI PO 接口问题 SAP PI SSL证书 SAP PI&amp SAP PI/PO SAP PI/PO 系统集成 SAP PI架构 SAP PLM SAP PM SAP PM 工厂维护 SAP PO SAP PO PI 系统接口集成 SAP PO SSL证书 SAP PO 导入SSL证书 SAP PO/PI接口 sap powerdesigner SAP PO安装 SAP PP SAP project SAP PS SAP QM sap query SAP R/3 SAP R3 SAP R3 ABAP4 SAP R3 主流系统EAI接口技术剖析 sap r3的lanuage 代码 SAP REST API SAP REST JSON SAP Retail SAP RFC SAP RFC 与 Web有啥区别 SAP ROUTRE SAP RSA 加密解密 SAP S/4 SAP S/4 HANA SAP S/4 HANA Cloud Sap S/4 Hana 和Sap ERP有什么不同 SAP S/4 HANA新变化-FI数据模型 SAP S/4 HANA新变化-MM物料管理 SAP S/4 HANA新变化-SD销售与分销 SAP S/4 HANA新变化-信用管理 SAP S/4 HANA新变化-主数据:物料主数据 SAP S/4 HANA新变化-主数据:业务伙伴之后台配置 SAP S/4 HANA与SAP Business Suit SAP S/4 MM SAP S/4HANA SAP S/4HANA表结构之变 SAP S4 SAP S4 HANA SAP S4 HANA CLOUD SAP S4  有用链接 SAP S4/Cloud应用 SAP S4/HANA FICO都有哪些改变? SAP S4HANA SAP S4HANA里委外加工采购功能的变化 SAP SBO9.1 SAP SBO重装 SAP SCM EWM SAP script SAP SD SAP SD MM PP FICO SAP SD 常用表 SAP SD 基础知识之定价配置(Pricing Confi SAP SD 基础知识之计划行类别(Schedule Lin SAP SD 基础知识之物料列表与物料排除 SAP SD 基础知识之行项目类别(Item Categor SAP SD 销售中的借贷项凭证 SAP SD 信贷管理的操作流程 sap sdi mysql SAP SD常用表 SAP SD基础知识之凭证流(Document Flow) SAP SD基础知识之输出控制(Output Control SAP SD模块 SAP SD模块-送达方和售达方的区别和联系 SAP SD微观研究 SAP SHIFT SAP SICF REST SAP smartforms乱码 SAP smartforms转pdf SAP smartforms转pdf乱码 SAP SQL sap srm SAP SRM 开发 SAP SRM  函数 sap strans解析json SAP TIPS SAP UI5 SAP UI5&amp SAP Variant 配置 SAP VC SAP Web Service SAP Web Service简介与配置方法 SAP Webservice SAP WM SAP WORKFLOW SAP XI/PI SAP 案例方案分享 sap 报错 注册服务器错误 SAP 报错集合大全 SAP 标准功能 SAP 标准教材和自学方法 sap 标准委外和工序委外 sap 查看服务器文件夹 SAP 常规 SAP 常用表 SAP 常用操作 sap 成本中心下的po SAP 成都研究院 SAP 导出 HTML sap 导出系统所有的单位 SAP 登录图片修改 SAP 顶级BOM查询 sap 订单状态修改时间 SAP 端口 SAP 发票合并与拆分 sap 发送mesage SAP 反查顶级BOM SAP 反查一级BOM sap 服务器信息 SAP 功能函数 sap 供应商表 SAP 顾问宝典 SAP 函数 SAP 后台表 SAP 后台配置 sap 计划订单 sap 假脱机请求 SAP 接口 SAP 接口测试 SAP 结账流程 sap 界面创建凭证 SAP 金税接口介绍 SAP 开发 sap 流程图 退货销售订单 sap 默认屏幕变式 SAP 配置 &amp SAP 批量创建货源清单 SAP 请求号 SAP 权限 SAP 权限配置 SAP 商超订单统一管理系统 SAP 商品主数据 SAP 数据库删除 SAP 数据字典 sap 双计量单位 sap 思维导图 SAP 锁机制认识 SAP 通用功能手册 SAP 透明表 SAP 图片修改 sap 文档服务器安装 SAP 问题以及报错 SAP 物料版次 SAP 物料不一致 SAP 物料删除标记 SAP 物料在启用序列号管理或者不启用序列号管理之间快速切换 SAP 系统 sap 消耗策略999 sap 消息服务器 bat sap 小技巧 sap 新建事务 sap 新增科目表 sap 修改服务器时间格式 sap 修改许可服务器 SAP 虚拟机配置1-FI SAP 虚拟机配置2-CO SAP 虚拟机配置3-MM SAP 虚拟机配置7-WM SAP 序列号与库存关联起来? SAP 选择屏幕 SAP 选择屏幕开发 SAP 演示数据增加 SAP 业务 SAP 业务顾问成长之路 sap 一代增强 SAP 银企直连 SAP 银企直联 SAP 银行对账 sap 用户权限表 SAP 语法(Syntax) SAP 员工主数据 SAP 原材料 SAP 云 SAP 杂项 SAP 增強 SAP 增强 SAP 之门 01 SAP 中国研究院 SAP 主题 SAP 字段增强 SAP 自动化 SAP  ERROR sap  hana SAP  MM知识点 SAP  PP SAP  配置 BOM SAP Enhancement SAP Migration SAP SD SAP STMS SAP&amp SAP* sap*账号 SAP,SD SAP/ABAP SAP/ABAP 相关汇总 SAP/ABAP记录 SAP/ERP SAP/FICO sap/hana SAP_ABAP SAP_ABAP知识点 SAP_BAPI SAP_BASIS SAP_FICO sap_mm SAP_PP SAP_SD SAP_Table SAP_TCODE SAP_モジュール_MM SAP_モジュール_SD SAP_常见问题集合 SAP_常用BAPI SAP_常用表 SAP_各路小技能 SAP_基本配置 SAP_接口 SAP_视图 SAP·SD SAP2000 sap2000学习笔记 SAPabap SAP-ABAP SAP-ABAP-Function SAP-ABAP基础语法 SAP-ABAP-基础知识 SAP-ABAP小白学习日常 SAP-ALL SAP-ALV SAPB1 SAP-BASIC SAP-Basis SAP-Bassic-基础知识 SAP-C01 SAP-CO SAPECC6.0 SAPFI SAP-FI SAP-FI/CO SAP-FICO SAP-FICO-CO SAP-Fiori SAP-GR SAPGUI SAPHANA SAP-HANA saphana服务器操作系统说明 saphana服务器硬件评估 SAP-IR sapjco SAPJCO3 sapjco配置文件下载 sapjoc3 SAPLINK SAP-MDG SAP-MDG-GEN SAP-MDG-HOWTO SAP-MDG-INTEGRATION SAPMM SAP-MM SAP--MM SAP-MM-采购管理 SAP-MM-后台 SAP-MM-前台 SAP-MM问题集锦 SAP-MM-问题记录 sapmto生产模式配置及操作详解 sapnco sapnco3 receive idoc sapnco3 接收 idoc sapnco3.0 SapNwRfc.dll SAPOSS SAP-Other SAP-PM SAP-PO SAPPP SAP-PP SAP-PP模块 SAP-PS SAP-QM SAP-RETAIL SAProuter SAP-RPA SAP-SD SAPUI5 SAP-UI5 SAPUI5核心内容 SAPUI5教程 SAP-WDA SAP-WM SAP案例教程 SAP宝典 SAP报表开发工具 Report Painter SAP边做边学(自学)-看看坚持多久 SAP标准工具程序 SAP表 SAP--表相关 sap采购订单更改记录 SAP采购订单增强 sap采购申请自动转采购订单 SAP仓储单位SU SAP-操作文档 SAP策略组 sap产品 sap产品图谱 - road to sap.pdf SAP常规功能 SAP-常见问题 SAP常用BAPI SAP常用表 SAP超时设置 sap成本流怎么看 SAP创建自定义权限 SAP呆滞库存的计算 SAP代码分享 SAP单链接 SAP的NOTE sap的pod确认 sap的工作日历 SAP的技术战略 SAP的竞争战略 sap的清账是什么意思 SAP调用 SAP队列 SAP访问本机虚拟机服务器 sap放弃java sap服务器安全证书 sap服务器查看系统日志目录 sap服务器出pdf文件 sap服务器迁移性能问题 sap服务器数据库配置文件 sap服务器文件上传 sap服务器怎么安装双系统 sap服务器之间文件复制 SAP改表 SAP--概念 SAP干货分享 SAP各种BOM汇总——含义解释 SAP更改物料类型 sap更改主题 SAP工具 SAP-工作 SAP公司 sap供应商更改组 sap固定资产号码范围 SAP顾问 SAP顾问进行时 SAP顾问那些事 SAP管理 SAP核心模块 SAP后台配置 sap后台配置原因代码 SAP环境配置 sap获取系统时间 SAP基本安装 sap基于mysql安装 SAP技巧 SAP技巧集 SAP技术 SAP技术端 SAP技术文档 SAP技术小知识 SAP技术总结 SAP加解密 SAP加密 SAP架构 SAP-架构 sap假脱机打印机设置 SAP监控 SAP监控常用TCODE sap脚本运行 SAP教程 SAP接口 SAP接口 证书和密钥 SAP接口编程 SAP接口常见问题处理 SAP接口开发 SAP接口数据库 SAP接口相关设置 SAP解密 SAP界面设置 SAP经验 SAP开发 SAP-开发 sap开发需要java吗 sap开发语言 sap可以指定应用服务器 SAP客户数据 SAP客户数据导出 sap客户信贷 sap客户主数据bapi SAP-跨模块知识 SAP零售 SAP零售行业 SAP密码过期设置 sap模糊搜索闪退 SAP模块 SAP模块知识 sap内部顾问 sap内部运维 sap培训 SAP培训机构 SAP配置 SAP批量打开工单 SAP批量导出客户 SAP批量导出客户数据 SAP批量修改 sap期初导资产代码 sap清账使用反记账 SAP请求传输 SAP取历史库存(可查询期初期末库存和指定日期之库存) SAP权限管理 sap权限激活 SAP认证 SAP如何发布webservice SAP入门 SAP软件 SAP删除物料 SAP上云 sap生产工单报工 SAP实施 SAP实施攻略 SAP实施知识 SAP使用技巧 sap事务代码 sap事务代码如何收藏 SAP视频 SAP视频教程 SAP视图 SAP视图批量维护 SAP视图维护 SAP数据表 SAP数据导入导出 SAP数据分析 SAP-数据库 sap税码配置 SAP索引不存在 SAP通用技能 sap外币重估流程图 SAP维护 SAP-未分类 sap未分摊差异怎么处理 sap文化 SAP文章 SAP问题处理记录 sap无法正常启动服务器配置文件 SAP物料classification SAP物料类型 SAP物料删除 SAP物料视图批量维护 SAP物料视图维护 SAP物料特性值 SAP物料主数据 SAP稀有模块 sap系统 SAP--系统 sap系统ftp服务器下文件 SAP系统-MM模块 sap系统搭建教程 sap系统登录时没有服务器 SAP系统管理 SAP系统界面 SAP系统配置 sap系统前台数据与后台表之间 SAP系统研究 sap系统中的batch sap相关知识 SAP项目 sap项目部署到服务器 SAP-项目经验 SAP项目实施 SAP-项目实施随笔小计 SAP项目问题 sap消息服务器错误 SAP--消息号 SAP消息监控器 SAP销售订单邮件 sap销售发货的流程 sap销售凭证流mysql表 sap销售维护 SAP销售员维护 SAP小问题 SAP写入mysql SAP心得 SAP新产品系统 SAP修改已经释放了的请求号 sap虚拟机 多个服务器 sap虚拟机作为服务器 SAP选择屏幕 SAP选择屏幕开发 SAP学习 SAP业务 SAP异常处理 SAP银企直连 SAP银企直联 SAP银行账户管理(BAM) sap应用服务器超载 SAP邮件发送 SAP邮件记录 SAP邮件记录查询 SAP云平台 SAP运维 SAP-运维记录 SAP杂谈 SAP-杂谈 SAP杂项 SAP在采购和销售中的税务处理-增值税 sap增加事务代码权限 SAP增强 SAP战报 SAP战略中的机器学习 SAP知多少 SAP知识点 SAP制造集成和智能 SAP智能云ERP SAP中CK11N成本估算 sap中re凭证是什么意思 SAP中s_p99_41000062查询物料价格数据库表 SAP中报表清单导出的常用方法 SAP中的client SAP中的贷项凭证、借项凭证 SAP中的移动类型 SAP中方会计凭证解决方案 sap中国 sap中文使用手册 模块指南 SAP中销项税MWSI和MWST有什么区别? SAP中执行没有权限的事务 SAP中自动登出 SAP转储订单(STO) SAP咨询公司 SAP资讯 sap字段及描述底表 sap自带samples sap自动化 SAP自习室 SAP组连接 SAP最大用户数设置 sara SAST SAT SBO开发 SCA scala SCC4 Schema schema增强 scipy scm SCP SCP Cockpit scpi Screen SCRIPTFORM scripting Tracker SD sd bapi SD Module SDI SD常用表 SD模块 SD销售 se09 SE11索引 SE16N SE16和SE16N修改后台表数据方法 SE37 SE38 se91 SE93 Search search help security segw SELECT Select Screens select sql Selenium SEN SER01 Serial  Numbers SERVER Serverless service servlet Set SET Parameter setting SFW5 ShaderGraph sharepoint Sharepoint Or Online shell SLD SLT SM02 sm36 SM37 SM50 SM59 smartbi问题 Smartform smartforms SNOR SNP BLUEFIELD SNP 中国数据转型公司 SNUM SOA soamanager soap SoapUI 接口测试 socket SOD Software Development Notes Sort and Filter Sotap Source Scan spa Hana SPAD Spartacus标准开发 Spartacus二次开发 SPC SPED SPOOL打印 spring Spring Boot SpringBoot SPRO spss打开oracle SQL SQL server SQL Trace sqlite Sqlmap使用教程 sql-sap SQLSERVER SQLSERVER内部研究 SqlSugar sql笔记 SQL语法 sqoop SR2 sRFC srm SSCRFIELDS ssh SSIS ssl SSL证书 ST05 ST12 START STE stm32 STO Stock Type stocktransfer Stopwatch StorageLocationControl StorageType StorageUnitType StorLocControl streamsets string SU20 SU21 SU24 Submission SUBMIT sudoku SUM Suport SUSE SUSE 11 SP4 SUSE Linux SU号码 SXI_MONITOR SXMB_MONI SXMSPMAST Sybase Sybase迁移数据到Oracle Sybase数据库迁移数据到Oracle SYSAUX Sysbase system System_failure s云服务器 网站群服 T184L T681 table TABLE FUNCTION Tableau Tabstrip TCode T-Code tcp/ip TCP/UDP Socket TCPH TCP客户端显示服务器图片 TDSQL-C TeamViewer Tech 专栏 TechArt Teradata Test Automation test-tools Textbox TH_POPUP TiDB TikTok tim发文件服务器拒绝 TITLE TM TMS TODO tomcat tomcat报错 ToPrintControl Tough tp5部署虚拟机服务器 tp5服务器信息 tp5网站 服务器部署 tp5项目链接服务器数据库端口888 TR TR LIST Trace Transact-SQL transformer tree control tRFC trigger TryHackMe typescript T公司 T库存 u3d微信小游戏 u8信息服务器 UB UB STO ubuntu UD udp UD配置 uefi ugui ui UI5 Uibot Uipath UI开发 UI控件 UI自动化 unicode unity Unity 100个实用技能 Unity UGUI Unity3D Unity开发 Unity日常开发小功能 Unity微信小游戏 unity项目部署到服务器上 unity游戏开发 Unity坐标转换 unix Url URP user Userid usual UUID ux U盘 U盘文件拷贝到服务器 VALUE VARIANT VariantBOM vasp计算脚本放在服务器的位置 vb.net VBA VBA开发专栏 VBFA v-bind vbs Vendor CoA VendorCOA VendorRebate Verilog-HDL veth vhm在服务器上创建虚拟机 v-html VIEW vim visual studio visualstudio vite VKM3 VKM4 VL02N VL04 VL10B VL31N VL32N VMware VN VOFM v-on VS Code vscode v-show Vue vue.js vue2 Vue3 基础相关 vue项目如何放到服务器上 VulnHub渗透测试 WA01 WA21 WBS WCF WCN WDA WDA的配置 wdb WE20 WeAutomate Web web app Web Dynpro web gui Web IDE Web Service WebDispather WEBGUI WEBI webm webrtc WebService WEBSOCKET webvervice webview web安全 Web安全攻防 web渗透工具 WF 4.0 while Wifi热点java win10服务器系统数据库 win7系统创建ftp服务器地址 win7系统数据库服务器 Window windows windows服务 windows服务器版本系列 windows系统部署git服务器 Windows系统电脑操作 winform wireshark wlan WM WMS WM仓库管理 WM层面盘点 WM模块 WM配置 WM移动类型 Work Work Flow workflow wpf wps WR60 WRMO wsdl xaf xml xp系统怎么上传到ftp服务器 XS HANA XS Job xsdbool yara规则 yqv001-navigation Y企业信息化集成 Zabbix ZIP zk zookeeper zypper in 安装下载不了 阿里云 阿明观察 埃森哲 X SAP:智慧转型高手论剑 安鸾靶场 安全 安全分析 安全工具 安全架构 安全手册 安全与测试 安阳虚拟服务器 安装 安装报错 安装服务器系统数据库服务器 安装数据库服务器需要的文件 安装完数据库服务器为空 安卓 安卓服务器文件 案例 案卓盒子建立文件服务器 靶机 百度 办公自动化 包含服务器数据库的聊天系统 保护交货计划 保留空格 报表 报表优化 报错 报工 贝叶斯 备份及容灾 备份文件到内网服务器 被合并的公司 笔记 笔记本通过服务器提升性能 币别转换 编程 编程技术 编程世界 编程语言 编程语言排名 编辑器 编辑器转换 变更物料类型 变化 变式物料 标题 标准 标准成本历史清单 标准价 标准价和移动平均价 标准解决方案 表白网站怎么上传到服务器 表关系 表维护生成器 博弈论 补丁 补货监控 不常用 不能从服务器上获取视频文件格式 不同系统可以用一个数据库服务器吗 布局 部署 部署网页到华为云服务器 部署系统时访问服务器 财务报表 财务报表版本 财务管理 财务会计 财务科目导入 财务凭证行项目 财务增强 财务账期 采购 采购订单 采购订单和内部订单对应关系清单 采购订单价格与发票价格差异 采购订单审批 采购订单收货和订单收货区别 采购订单修改触发重新审批 采购订单增强 采购订单状态标准查询配置 采购附加费 采购附加数据 采购合同与采购计划协议关联性 采购价格 采购凭证模板 采购申请 采购审批 采购审批过程 采购收货及发票校验记录清单 采购退货 采购退货操作 采购退货测试 采购退货流程 采购退货业务 采购退货移动类型 采购信息记录 采购组 踩坑 踩坑日记 菜根发展 菜鸟日记 菜鸟之家 参数文件 参与MRP 仓库 苍穹ERP 操作符 操作系统 测绘程序 测试 测试工程师 测试工具 测试环境 策略组 层级查询 查看ftp服务器里的文件 查看服务器上文件命令 查询分析器 查询服务器系统类型有哪些 查找代码段 查找增强点 差异 差异分析 产品 产品成本估算 产品成本核算号 产品创新 产品经理 产品驱动增长 产品运营 常见端口 常见问题 常用bapi 常用sql 常用函数 常用数据类型 常用问题收集 常用自建函数 超自动化 成本对象 成本分割 成本估价历史清单 成本估算 成本估算的取价逻辑 成本核算表计算间接费用 成本核算结构 成本核算中BOM和工艺路线 成本收集器 成本要素 成本要素不可更改 成本中心标准报表 成本中心实际/计划/差异报表 成都最稳定的dns服务器地址 程序/PROGRAM 程序导出 程序人生 程序人生 ABAPer 程序人生和职场发展 程序设计 程序下载 程序员 程序员职业发展 持久类 持续集成 冲销扣料 初级成本要素 初阶 初学 初学者 处理外向交货单 触发器 传媒 传输 传输层 传输请求 传输日期 串口通信 创建服务器共享文件夹 创建物料主数据时的视图状态 创新 创新案例 创新战略 垂直居中 磁盘管理虚拟磁盘服务器 次级成本要素 从u盘引导进入linux6 存储 错误处理 错误解决 达梦 打印 打印次数 打印机 大厂面试 大庆服务器维修 大数据 大数据分析 大数据工程师 大数据可视化 大小写 大型服务器安装什么系统 代码规范 代码片段 代码在哪用到了 带格式的邮件附件 带你走进SAP项目 单片机 单片机系列 单位 单文件 单元测试 弹出框问题 弹性计算 导出电子表格问题 导出内表数据至Excel文件中 导出期末或指定日期库存 导入 导入license 导入数据库显示服务器发生意外 倒冲 到期发票清单VF04功能 登陆语言 登录oa系统输入服务器地址 登录日志怎么实现 低代码 低功耗文件服务器 地球 递归 第三方 第三期间 第一个ABAP程序 点击ftp服务器的文件弹出登录界面 电话 电商 调试 调试器 调用sap接口 调用接口 调用子屏幕修主屏幕 调优 调制与编码策略 鼎信诺显示连接服务器失败 订单 定价 定价过程 定价例程 定价值 定时采用ajax方式获得数据库 定时器 定时任务 定时同步文件到ftp服务器 定义 定义详解 动态安全库存 动态获取字段名 动态类 动态属性和事件绑定 冻结功能 冻结库存 冻结库存转库 读取文件内表数据 端口 队列 队列末尾 对象 对象不支持属性或方法dbzz.html 多扣料冲销 多流 多人共用 不能访问目录 多送或者少送 多线程 多引擎数据库管理系统 多源异构数据汇聚平台 多重科目分配 俄罗斯报表 二代增强 二级标题-003-Pacemaker 发票处理系统 发票冻结原因 发票冻结原因及解除冻结 发票小金额差异 发票自动化 翻译 反冲 反记账 反记账数据转换 返工 泛微OA调用SAPwebservice详解 泛微OA开发 方便小函数 方格子无盘服务器怎么用 访问后台接口 非技术区 非技术文章 非限制库存 分包后续调整 分布式 分类 分类账 分配表 分配分摊 分三个屏幕的OOALV 分析云 分享学习 服务 服务类采购订单的收货审批确认 服务器 服务器 文件类型 服务器 稳定 重要性 服务器1g内存装什么系统 服务器cpu只显示一个核 服务器host文件目录 服务器raid1做系统 服务器vos系统怎么装 服务器安全证书登陆失败怎么办 服务器安装系统sles系统 服务器安装系统如何选择网关 服务器安卓系统安装教程 服务器被攻击 文件被删除 服务器比对数据库差异文件 服务器标识信息 服务器部署的参数文档 服务器操作系统套什么定额 服务器操作系统用什么好 服务器操作系统与数据库 服务器查看操作系统类型 服务器查看数据库日志文件 服务器查文件 服务器出生点配置文件 服务器传送过来的是什么信息 服务器搭建网站方案500字 服务器大内存系统吗 服务器的ftp数据库信息 服务器的参数配置文件 服务器的地址信息 服务器的共享文件地址 服务器的系统文件怎么恢复出厂设置密码 服务器登录需要信息吗 服务器定时任务系统 服务器读取不了文件 服务器放文件 服务器故障修复费用需要摊销吗 服务器光纤存储系统 服务器接入协议是什么 服务器快照能代替网站备份吗 服务器扩容文档说明 服务器链接数据库配置文件 服务器两个网站公用一个数据库 服务器默认文档 服务器内存扩展板位置 服务器内存条的种类文档 服务器内存性能好 服务器内存在哪个位置 服务器内核文件在哪 服务器迁移操作系统 服务器迁移需要哪些操作系统 服务器如何查看文件个数据库文件夹 服务器如何分多个文件 服务器设计虚拟内存 服务器设置上传文件大小 服务器适合安装深度系统deepin 服务器数据库查看版本信息 服务器数据库查看版本信息失败 服务器数据库的文件读取数据库 服务器数据库系统 服务器数据库协议 服务器数据库用什么系统 服务器数据系统 服务器网站关联数据库 服务器微端位置 服务器维护 吸尘器 服务器维护费入什么科目 服务器文件地址 服务器无盘镜像导入 服务器物理机部署 服务器物理内存只增不降 服务器物理组成 服务器系统安全方案 服务器系统安装ansys 服务器系统安装oracle数据库 服务器系统安装报价 服务器系统版本选择 服务器系统方案 服务器系统和数据库的用处 服务器系统架构讲解 服务器系统盘50g什么意思 服务器系统盘大文件检测指令 服务器系统盘分多少 服务器系统数据库安装 服务器系统性能灯 服务器系统有多大 服务器系统与数据库 服务器系统怎么恢复出厂设置 服务器修改mime类型 服务器修改密码规则 服务器虚拟化与企业私有云 服务器虚拟机的c盘怎么加 服务器选择系统版本 服务器与本地文件共享 服务器怎么清除日志文件 服务器只读团体字信息 服务器中文档存储在哪 服务器主板坏了怎么维修 服务器主板维修电子书 服务器装系统快吗 服务器装系统无显示屏 服务器租赁文档 服装信息化 浮点运算 福建工程学院计算机网络技术期末考试试卷 辅助线框 付款 付款流程 付款条款 付款信息 负号前置 负库存的相关设定 复合角色 复制创建采购申请 复制控制 复制文件到服务器 内容不足 概念整理 感悟 高级退货管理 高阶 高可用架构 高斯坐标 高性能服务器一体机 高性能有限元计算服务器 个人经历 个人开发 个税系统代理服务器参数是什么 个性化定制 给标准报表添加字段 给一个oracle账号密码是什么 更改成本要素类别 更改物料类型 更新服务器数据库文件位置 工厂 工厂管理 工厂内库存转移 工厂日历 工具 工具集锦 工具类 工具使用 工具使用指南 工具手册 工具系列 工业软件 工艺路线 工资发放和结算 工资计提 工作 工作笔记 工作量法 工作流程自动化 工作流自动化解决方案 工作杂记 工作总结 公式计算 公司财务系统html 公司代码货币 公司服务器可以查询员工哪些信息 公司间STO 公司间STO‘ 公司间过账 公有云-华为 功能 功能测试 功能开发说明书 供应链 供应链管理 供应商 供应商采购冻结 供应商评估 供应商清单输出 供应商子范围 沟通能力 购买云服务器配置项目 估价容差测试 固定点算术 固定资产 固定资产会计 固定资产折旧 固定资产折旧码 顾问之路 挂微群发软件需要什么服务器信 关闭 关系模型 关于R/3 关于赛锐信息 关于信用管理--信用更新 管理 管理数据库 广播 消息 没有服务器 归档 规格说明书 国产器件 国产软件 国产数据库 国科大学习 国内服务器内存缓冲芯片 国外服务器显示数据库 哈希算法 海康4200服务器进不去系统 海口服务器系统租用 海纳百川 含税价 邯郸虚拟服务器 函数 函数/FUNCTION 函数技巧 函数模块 函数式编程 好书推荐 合作案例 合作伙伴 和车神哥一起学 核心主数据 黑盒测试 黑名单 恨ta就教ta  SAP 红蓝攻防篇 后端 后端开发 后鸿沟时代 后台Job 后台表 后台导出表数据 后台服务器 后台开发 后台作业 胡思乱想 湖仓一体 互联网-开源框架 华为 华为2012服务器系统安装教程 华为hana服务器型号齐全 华为服务器gpu芯片 华为服务器raid1装系统 华为服务器安装2012系统怎么分区 华为服务器安装nas系统 华为服务器扩容内存进不去系统 华为服务器修改root密码 华为无线局域网 华为云 华为云服务器更换操作系统 华为云服务器还需要确定位置吗 华为云服务器系统备份 华为云服务器自己维护吗 华为怎么安装服务器系统版本 环境搭建 缓存 汇率维护 汇率转换 汇总 会计 会计分录 会计基础资料 会计科目 会计科目表 会计科目删除 会计凭证批量导出 会计凭证清账 会计凭证替代 会计凭证中的注释项目 会用到的 绘图 绘图工具 惠普服务器G8系列做raid 活动 伙伴功能 货币过期 货币类型 货币停用 货源清单 获取窗体下的所有控件 获取汇率 机器人流程自动化 机器学习 鸡肋 积累 基本单位 基本配置 基础 基础模块 基础入门 基于收货的发票校验配置过程 基准日期 集成 集团货币 集中采购 己建立BOM清单 计划策略 计划策略40 计划订单 计划时界应用 计划时界应用测试 计划数量小于收货或发票数量 计划协议 计划行类别 计划行类别中请求/装配 计划行统计清单 计量单位 计入物料成本 计算步骤 计算机 计算机毕业设计 计算机基础 计算机基础知识 计算机科学分成什么模块 计算机体系 计算机图书 计算机网络 计算机网络 王道 计算机网络rip路由表题目 计算机网络理论概述 计算机网络原理(谢希仁第八版) 计算机网络远程管理作业答案 计算机维护 计算机信息管理自考-04741计算机网络原理 计算机自学考试 记录问题 记账冻结 记账码 技能 技巧 技术 技术分享 技术干货 技术交流 技术类 技术沙龙 技术渗透 技术文档 技术总结 寄售 寄售交货 寄售结算规则 寄售模式 加密 加密算法 加前导零 加速器 价格修改历史 架构 架构设计 架设企业文件服务器 假期日历 监控 监控服务器系统备份 监控服务器系统密码忘了怎么办 监控平台 监控事件 监控系统 监控系统里服务器 监控系统是否要服务器 减值准备 检验点 检验计划 检验类型 检验类型89 检验批 检验批系统状态 简单窗体实现 简单的数据库管理系统 用什么云服务器 简述客户 服务器系统的组成 建议组件分配到BOM 渐变色UI描边 将服务器上数据库复制到本地文件 将已有项目转移到云服务器 交互 交货单 交货计划固定 交货计划期间保护 角色 角色继承 角色设计 教程 教育电商 阶梯价格 接管日期 接口 接口测试 接口方式 接口问题处理 接口-银企直连 结算会计年度 截取年月日在hana中怎么写 解决方案 界面 借贷 金丹期 金蝶 金蝶 系统服务器繁忙 金蝶K3 金蝶二次开发好跳槽吗 金蝶服务器维护 金蝶云星空操作手册 金蝶中间件部署报栈溢出 金额转换 金税接口 仅在总账中过账 仅装配 仅组件 进口采购 进入文档服务器不能输入密码 进销存 进销存报表 进销存系统怎么部署到自己服务器 经历 经验 经验分享 经验总结 精诚MES 精诚智慧工厂 精选 境外服务器稳定 镜像 玖章算术 就是玩儿 矩阵 聚合函数 聚集函数 开发 开发笔记 开发工具 开发管理报表 开发环境 开发平台 开发语言 开发者 开发知识点 开源 开源ERP 开源-JDK-镜像 开源系列谈 开源项目 看板 考试 考试复习 考研 科技 科技公司 科目行项目不显示 可配置物料 客供料 客户 客户冻结 客户端往服务器写文件 客户端修改opc服务器的数据 客户服务 客户-服务器数据库系统举例 客户服务器系统的特点是 客户关系处理能力 客户关系管理 客户贸易伙伴 客户信贷管理解析 客户主数据 课程 课程笔记 课堂笔记 空调控制系统节点服务器 空间管路 口碑效应 库存地点MRP 库存地点权限控制 库存管理 库存决定 库存批次 库存需求天数关系 库龄 跨公司STO 跨国跨公司间转储 块设备驱动 快捷 快捷键 快手服务器协议 快速定制 框架 鲲鹏服务器系统重装 扩充存储地点 扩展 扩展知识 来也科技 蓝桥杯 蓝牙 蓝牙A2dp 浪点服务器芯片 乐鑫 类型强转 理解 历史库存sap 利润表 利用云服务器传递信息 连接 链表 良仓太炎共创 两步法拣配 料主数据中的屏幕字段 列表 列存索引 列存引擎 零基础快速学习 ABAP 零散知识 零售 零售行业 零碎(凑数)的算法[题] 零停机 流程自动化 流水号 流水码 流星的程序集 漏洞预警 录屏 录像机显示服务器 乱码 论文 论文阅读笔记 蚂蚁无线管理器服务器 买个服务器来挂协议 买了一个服务器修改密码 漫谈计算机网络 贸易伙伴的应用 没有MANDT字段 没有中间凭证冲销 媒体 每日摸鱼新闻 门店视图 门店主数据 免费流量获取 免关税 面试 面向对象编程 面向对象方法 敏捷 敏捷开发 命名规范 模板语法 模块 模块测试 莫队 莫队算法 目标跟踪 内表 内表类型 内表字段 内部订单 内部订单清单 内部订单删除问题 内部订单月结差异 内存管理 内存数据库 内存图片 内核 内核驱动 内核驱动开发记录 内嵌Excel 内容服务 内容服务平台 内容服务软件 内容库 内外码转换 内网 内网渗透 内向交货单 那个网站的服务器不限制内容 能不能用pe安装服务器系统安装系统 能力建设 能源 年结 爬虫 排行榜 排序算法 盘点 盘点流程 培训 配额协议 配置 配置SAP服务器外网登陆以及网络故障解决示例 配置笔记 配置高性能文件服务器方案 批次 批次拆分 批次管理 批次号 批次确定 批次特定单位 批次特性 批导程序模板 批导模板下载 批量采购冻结 批量导出表数据 批量更改会计凭证文本 批量维护 批量用户账户锁定 平行记账 凭证冲销的种类和处理逻辑 凭证打印 凭证流 凭证状态 凭证状态S 屏幕(Dialog)开发 屏幕SCREEN字段属性 屏幕程序 屏幕设计 破坏式创新 破解 期初库存金额 期初资产数据导入 期刊阅读 期末不挂科 期末复习 期末库存金额 其他 其他应付款-代扣代缴 其他知识点 奇技淫巧 麒麟服务器数据库协议 企业/办公/职场 企业安全 企业服务器文件管理 企业管理软件 企业级应用 企业解决方案 企业内部控制 企业内容管理 企业软件 企业微信 企业文件服务器备份 企业系统 企业信息化 企业信息化前沿 企业资源计划 启用WEBGUI服务 迁移驾驶舱 前端 前端基础练手小项目 前端架构 前端开发 前端开发相关 前端框架 前后端 前台操作 嵌入式 嵌入式开发 嵌入式学习--STM32 嵌入式硬件 清软英泰plm服务器安装文档 清帐 清账 清账凭证 请求 请求传输再还原 请求号 区块链 区块链技术 区域菜单 驱动开发 取价逻辑 取消审批 取样策略 取值相关 去前导零 全角半角转换 全球最大sap hana系统建立在以下哪个厂商的服务器产品上 全球最大的采购服务平台 权限 权限对象 权限管理 权限合规检查系统 权限控制 権限 缺料提醒及警报 热点开启 流程 人工智能 日常ABAP开发记录 日常Bug 日常工作 日常记录 日常学习工作经验分享 日常知识分享 日记 日历 日期 日期函数 容器 容器服务 容灾 如何安装华为服务器系统软件 如何把项目部署到内网服务器 如何传输本地文件到服务器 如何从服务器上更新文件 如何导出序时账 如何读取服务器文件数据 如何复制服务器数据库文件大小 如何将CRM系统上传到服务器 如何将hana数据同步到oracle 如何设置sap生产订单自动关闭 如何统计输出条目数量 如何修改服务器root密码 如何知道有哪些物料存在BOM 入后在服务器修改数据库 入库 入门 入侵一个网站的服务器拿数据 入行SAP咨询 入职甲方 软件 软件安全 软件部署 软件测试 软件测试知识 软件程序 软件工程 软件教程视频集合 软件开发 软件生态 软件下载 软件显示未找到服务器 软考 软实力 软硬件运维 赛锐信息 三代增强 扫描代码 删除 删除记录 商城小程序买哪种服务器 商品主数据 商务智能 商业软件 商业智能 上传 上传附件出错 上传图片 上传文件到云服务器存储路径 上架策略B 上架策略C 上架策略P 上线 上云 设备维修 设计模式 设计与维护类 设置参数缺省值 社保管理系统连接不上服务器 社区活动 深度学习 深度优先 深澜系统服务器架构 审计导出表数据 审计序时账 审批策略 审批代码 渗透 渗透笔记 渗透测试 渗透测试自学日志之基础原理篇 渗透工具之信息收集 升级 生产版本 生产版本排序规则 生产版本选择规则 生产版本选择逻辑 生产版本选择顺序 生产版本优先顺序 生产成本收集 生产排程 生产系统服务器主机名怎么看 生活 生活感悟 什么情况使用一次性供应商及客户 什么是BAPI 什么是序时账 时间比较 时间对象 时序数据库 实施 实施SAP 实施项目 实时集成变式 实时库存 实体服务器怎么配置文件 实习 实习生 实战SAP程序开发 使用感受 使用决策 事务代码 事务代码LX04 事务代码WRCR 事务技术名称的显示与隐藏 事务码/TCODE 视觉语言导航 视频 视频处理 视频监控选择服务器的配置文件 视图 收货冲销 收货处理 手动加载ICU库 手机主服务器怎么配置文件 售后管理 输入历史记录 暑假复习 树查询 树莓派 数独 数据安全 数据仓库 数据仓库学习分享 数据从hana倒回Oracle的方法 数据导入 数据导入和处理 数据分析 数据分析 + 机器学习 数据分页 数据服务器 操作系统 数据服务器什么系统软件 数据服务器文件夹 数据服务器与文件服务器 数据格式 数据湖 数据结构 数据结构与算法 数据科学入门 数据可视化 数据库 数据库备份到文件服务器 数据库表字段 数据库操作 数据库的文件服务器配置 数据库服务器部署文档 数据库服务器网页 数据库服务器系统 数据库服务器系统崩溃 数据库服务器系统的 研发 数据库服务器系统软件 数据库服务器压缩文件 数据库管理与维护 数据库规划、部署 数据库和服务器什么协议 数据库和服务器系统怎么安装 数据库技术 数据库架构 数据库监控 数据库监控软件 数据库开发 数据库文件共享服务器配置 数据库系统概论 数据库系统原理 数据库系统怎么与软件连接到服务器 数据库与服务器通讯协议 数据库最新排名 数据类型 数据链路层 数据浏览器的字段名称显示 数据迁移 数据迁移驾驶舱 数据迁移完整性检查 数据挖掘 数据治理 数据中台 数据中心IDC架构及容灾与备份 数据重置 数据字典 数学建模篇 数字化 数字化管理 数字化转型 数字货币 数字业务整合 双计量单位 双路服务器只显示一半内存 双碳 双网文件服务器 水晶报表 税改 税率 税友报税软件让修改服务器地址 私有云虚拟化服务器群 思爱普 思科里服务器的dns配置文件 死锁 四代增强 四元数 搜索帮助 搜索引擎 搜索引擎营销 速食 算法 随便看看 随机方向 随机数 损益表 所见即所得的打印功能 锁定 锁定事务代码 抬头文本被强制清空 探测服务器操作系统版本 特殊库存 特殊移动标记 特性 腾讯云 提升工作效率的工具 题解 替代 替代/校验/BTE 天正服务器不显示 添加列到指定位置 条件 条件表 条件类型 条码系统 跳槽 跳过代码 贴花 通过SQVI增加表格字段 通信协议 同步 同方服务器系统安装 统驭科目理解 透明表 图论 图像处理 吐槽 外币评估 外币评估记账 外部采购 外部断点 外贸管理软件 外贸软件 外向交货单 外协加工 外语能力 完美汽配管理系统v12服务器 完整的采购订单业务信息凭证流 玩转STM32 万彩录屏服务器不稳定 网吧无盘用华为服务器 网卡 网卡驱动 网络 网络安全 网络安全学习 网络存储服务器的系统 网络管理定时备份服务器网站文件 网络接口 网络配置 网络通信 网络拓扑仿真模拟 网络文件服务器有哪些 网络协议 网络协议栈 网络设备 网络规划 网络工具开发 网络营销 网页 服务器 数据库 网页如何从服务器获取数据 网页与服务器数据库 网易数帆精彩活动 网站服务器存储数据库吗 网站服务器没有安装数据库 网站服务器没有数据库备份 网站服务器与系统部署策略 网站跨域访问服务器数据库 网站上传到服务器需要上传数据库 网站数据库断连重启服务器 网站虚拟服务器1核1g速度 网站需要数据库服务器吗 网站与数据库不在同一服务器 网站云服务器需要数据库吗 往来余额结转 往年购置资产 微前端 微软 微软azure 微信 微信小程序 为服务器安装操作系统的流程图解 为什么文件上传不了服务器上 为资产分类定义折旧范围 维护视图 维护思路 委托加工 委托租赁云服务器协议 委外 委外加工 委外加工采购流程里副产品的收货 委外库存 委外销售订单库存 未能找到使用主机名称的服务器 未能注册模块 未清项管理 文本编辑器 文本表 文档管理 文档管理软件 文档协作 文档资料 文华软件登录显示请选择服务器 文件存储服务器 方案 文件服务器 华为 文件服务器 内存需求 文件服务器 内存需求大么 文件服务器报码表xls 文件服务器存储 文件服务器放在哪里 文件服务器和nas存储 文件服务器和数据库的区别 文件服务器可以存储的文件类型有 文件服务器内存 文件服务器内存要大吗 文件服务器网盘 文件服务器为何存不了大文件 文件服务器帐号切换 文件服务器属于固定资产吗 文件共享服务器所需虚拟机资源 文件名带中文上传ftp服务器变乱码 文件虚拟服务器 文件一般存在数据库还是服务器 问答 问题 问题处理 问题记录 问题解决 问题总结 我的SAP系统开发里程碑 我的问题 无代码 无代码开发 无法输入事务代码 无盘服务器工作流程 无盘服务器内存多大好 无盘服务器配置20台 无线监控设置smtp服务器 无值记账 物定工厂物料状态 物联网 物料 物料编号 物料编码 物料编码更改 物料变式 物料单位更改 物料分类账 物料管理 物料价格清单 物料库存/需求及供应天 物料凭证 物料凭证类型和交易/事件类型 物料帐 物料账 物料账期 物料主数据 物料主数据视图 物料主数据视图维护状态 物料组 物料组的分配规则 物流 习题 系统/网络/运维 系统安全 系统安装 系统服务器常见出厂密码有哪些 系统集成 系统架构 系统开发 系统未配置文件服务器是啥意思 系统相关 系统云端服务器 系统怎么访问数据库服务器 系统中的缺料情况及控制 下架策略A 下架策略M 下拉框 下载 下载程序 先后顺序 先进的数据库服务器操作系统 先进生产力工具大全 现金管理 现金流量表 线段树 线性规划 响应函数 向上取整 向下取整 项目 项目表 项目部署在服务器上的形式 项目管理 项目迁移 项目前端 项目实施经验贴 项目实战 消耗冲销 消息服务器待办事项数据库 消息控制采购订单 销售 销售(SD)凭证流 销售订单 销售订单冻结 销售订单库存 销售订单项目类别 销售订单信用冻结 销售订单中的条件类型 销售发货冻结 销售发货可用性检查 销售交货 销售开票冻结 销售税 销售项目开票 销售员 小白 小白的SAP问题积累 小程序 小程序云服务器磁盘怎么分区 小丁的blog 小记 小结 小项目(新手可做) 小型服务器的操作系统 小型企业网络存储服务器系统方案 效率 协议 心得感悟 新程序员 新基建 新建表维护程序SM30 新收入准则 新手时期 新闻 新语法 新增漏洞报告 新增移动类型科目确定配置 新总帐 薪酬核算 薪酬计提和发放 信贷 信息安全 信息安全顶会论文导读 信息化 信息化建设 信息记录 信息收集 信用额度 信用管理 行业 行业客户信息 行业趋势 性能测试 性能优化 修改,F4帮助,添加按钮 修改Q系统代码 修改表数据 修改服务器端的访问模式 修改服务器网络 修改服务器信息使密钥不过期 修改记录 修改交货单 修改历史 修改数据库安装的服务器 系统时间 修改物料组 虚拟服务器需要网关吗 虚拟服务器英文翻译 虚拟服务器资源 虚拟服务器资源配置 虚拟服务器最大磁盘2TB 虚拟化 虚拟机 虚拟机迁移后服务器无法启动 虚拟机如何做服务器系统 需求分析 需求类型 需要访问其他服务器信息吗 序列号 序列号管理 序列号清单 序时账导出方法 序时账核对 选型 选择屏幕 选择屏幕打开文件路径 学术前沿 学习 学习ABAP笔记 学习笔记 学习方法 学习人生 学习问题 学校三级项目 循环 压力测试 压力测试 闪存 亚马逊 亚马逊云科技 研发管理 研发效能 业财一体化 业务 业务处理 业务范围 业务分析 业务功能 业务顾问 业务顾问的小需求 业务伙伴 业务价值 一般总账科目数据转换 一次性供应商及客户 一次性供应商及客户应用经验 一个服务器 定时从各个系统取数据 一键还原服务器系统 一台服务器能存放几个系统 一台服务器如何部署多个项目 一套适合 SAP UI5 开发人员循序渐进的学习教程 医药行业 移动开发 移动类型 移动类型101/102 移动类型325 移动类型343 移动类型配置 移动平均价 异步Function 异常 异速联客户端未获取到服务器信息 音频编码解码 音视频 音视频开发 银企直连 银企直连接口 银企直联 银行 银行账户管理 隐式增强 印度 印资企业 应付职工薪酬 应收应付 应用设计 应用性能监控 英一 英语 硬件服务器搭建系统步骤 用户 用户定义的消息搜索配置 用友 优化 由于质量原因而冻结 邮件发送 邮件服务器及相关配置 邮件合并居中,框线 邮件预警 游戏 游戏服务器修改其他玩家数据 游戏开发 游戏引擎 有没有便宜一点的网站服务器 有限元模拟 余额不平 与SAP集成相关 语言 语言概览 语音 预留 预算管理 预制凭证 原创 原创项目 原力计划 源码 源码分析 月结 阅读分享 云 文件 服务器 文件怎么恢复出厂设置密码 云ERP 云安全 云备份 云财经服务器维护 云存储系统服务器版安装 云打印 云端 云服务 云服务器 云服务器 ftp上传文件大小 云服务器 选择什么系统版本 云服务器 重做系统软件 云服务器1和1g装什么系统好 云服务器cpu系列 云服务器ecs销售渠道 云服务器ubuntu修改密码 云服务器安装其他版本系统 云服务器部署mqtt协议通信 云服务器部署tomcat文件修改 云服务器磁盘怎么安装系统 云服务器存放位置 云服务器搭建推流系统 云服务器可以存放文件吗 云服务器免费suse系统 云服务器哪种系统好用 云服务器如何修改ssh密码是什么 云服务器软件文件管理 云服务器数据库密码修改zoc 云服务器网络配置信息查询 云服务器维护安全管理制度 云服务器物理部署位置 云服务器系统类别怎么选 云服务器系统租赁费用 云服务器修改ssh密码 云服务器需要装系统吗 云服务器怎么存文件大小 云服务器怎么多人进去编辑文档 云服务器怎么设置数据库文件 云服务器转租赁协议 云基础架构 云计算 云计算/大数据 云解决方案 云排产 云平台 云文档管理 云文档管理系统 云原生 云运维&&云架构 运算符 运维 运维开发 运维实施 运维系统 服务器监控 运维相关 运行效率 杂货铺 杂记 杂谈 杂项 再次冲销 在服务器删除的文件 恢复出厂设置密码 在服务器上建一个文件夹 在建工程 在建工程期初数据 在没有配置的dns服务器响应之后名称 在制品 怎么看系统服务器类型 怎么修改存储在服务器的数据 怎么修改服务器php版本信息 怎么在服务器上备份数据库文件在哪里 怎么在服务器上复制网站 怎么找到服务器的文档 怎样读取服务器上的数据库文件 怎样修改美国的服务器节点 增长策略 增长黑客 增强 增删改查 增值税 增值税调整 掌握物料库存,需求及供应情况 账号 账期设置 账期未开 折旧记账数据不在BSEG 正确使用一次性供应商及客户 正则表达式 证书 知识分享 知识管理 知识库 知识图谱 直线折旧法 职场 职场和发展 职业 职业发展 只存放文件的服务器 指纹识别 指纹字典 指针 制造 制造商物料 质量部门 质量管理 质量信息记录 质量证书 智慧企业 智能开发 智能运维 智能制造IT规划 智能制造执行系统 中国本地化内容 中间件 中阶 中维监控显示无法连接服务器失败怎么办 中文名称的文件传不到ftp服务器 中小企业 中小型网站服务器搭建方案 中转 重复打印 重复制造 重置期初数据 重置业务数据 重置主数据 重置资产会计数据 主检验特性 主批次 主数据 主数据导入 注册机 注解 注塑行业ERP 注意事项 转换Lookup功能 转义字符 转载 装服务器得时候选择系统版本 状态栏 咨询 资产 资产负债表 资产会计 资产接管 资产年初切换上线 资产折旧 资金 资料 资讯 子屏幕 字典 字段符号 字符操作 字符串 字符串拆分 字符串前导0 字节跳动 自动补货 自动创建交货单 自动登录SAPGUI 自动化 自动化测试 自动化工具 自动清账 自动邮件 自考 自然语言处理 自学成才 综合 综合资源 总结 总账 总账科目 总账行项目中凭证缺失 总账余额结转 租赁mt4虚拟服务器 组件 组织架构 组织结构 最大限制 最佳业务实践 最具性价比的方式 作业返冲 作业价格计算 坐标反算