首页 文章 精选 留言 我的

精选列表

搜索[数仓建模],共10000篇文章
优秀的个人博客,低调大师

详解数仓中sequence的应用场景及优化

摘要:本文简单介绍sequence的使用场景及如何修改sequence的cache值提高性能。 本文分享自华为云社区《GaussDB(DWS)关于sequence的那些事》,作者:Arrow0lf 。 什么是sequence sequence,也称作序列,是用来产生唯一整数的数据库对象。序列的值按照一定的规则自增/自减,一般常被用作主键。GaussDB(DWS)中,创建sequence时会同时创建一张同名的元数据表,用来记录sequence相关的信息,例如: postgres=# create sequence seq; CREATE SEQUENCE postgres=# select * from seq; sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called | uuid ---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------+--------- seq | -1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | f | f | 1600007 (1 row) 其中,sequence_name表示sequence的名字,last_value当前无意义,start_value表示sequence的初始值,increment_by表示sequence的步长,max_value表示sequence的最大值,min_value表示最小值,cache_value表示为了快速获取下一个序列值而预先存储的sequence值个数(定义cache后不能保证sequence值的连续性,会产生空洞,详见下文)。log_cnt表示WAL日志记录的sequence值个数,由于在DWS中sequence是从GTM获取和管理,因此log_cnt无实际意义;is_cycled表示sequence在达到最小或最大值后是否循环继续,is_called表示该sequence是否已被调用(仅表示在当前实例是否被调用,例如在cn_5001上调用之后,cn_5001上该原数据表的值变为t,cn_5002上该字段仍为f),uuid代表该sequence的唯一标识。 GaussDB(DWS)中,通过GTM(Global Transaction Manager,名为全局事务管理器)负责生成和维护全局事务ID、事务快照、Sequence等需要全局唯一的信息。sequence在DWS中的创建流程如下图所示: 具体过程为: 接受SQL命令的CN从GTM申请UUID; GTM返回一个UUID; CN将拿到的UUID与用户创建的sequenceName绑定; CN将绑定关系下发给其他节点上,其他节点同步创建sequence元数据表; CN将UUID 和sequence的startID发送到GTM端,在GTM行进行永久保存。 因此,sequence的维护和申请实际是在GTM上完成的。当申请nextval,每个执行nextval调用的实例会根据该sequence的uuid到GTM上申请序列值,每次申请的序列值范围与cache有关,只有当cache消耗完之后才会继续到GTM上申请。因此,增大sequence的cache有利于减少CN/DN与GTM通信的次数。接下来,将详细介绍sequence在DWS中的使用场景和注意事项。 如何创建sequence GaussDB(DWS)中,有两种创建sequence的方法: 方法一:直接创建sequence,并通过nextval调用,举例: postgres=# create sequence seq; CREATE SEQUENCE postgres=# insert into t_dest select nextval('seq'),* from t_src; INSERT 0 0 方法二:建表时使用serial类型,会自动创建一个sequence,并且会将该列的默认值设置为nextval,举例: postgres=# create table test(a int, b serial) distribute by hash(a); NOTICE: CREATE TABLE will create implicit sequence "test_b_seq" for serial column "test.b" CREATE TABLE postgres=#\d+ test Table "public.test" Column | Type | Modifiers | Storage | Stats target | Description --------+---------+--------------------------------------------------+---------+--------------+------------- a | integer | | plain | | b | integer | not null default nextval('test_b_seq'::regclass) | plain | | Has OIDs: no Distribute By: HASH(a) Location Nodes: ALL DATANODES Options: orientation=row, compression=no 本例中,会自动创建一个名为test_b_seq的sequence。其实严格来讲,serial类型是一个“伪类型”,本质上,serial其实是int类型,只不过在创建时会同时创建一个sequence,并与该列相关联,本质上,方法二中的例子与下面的写法等价: postgres=# create table test(a int, b int) distribute by hash(a); CREATE TABLE postgres=# create sequence test_b_seq owned by test.b; CREATE SEQUENCE postgres=# alter sequence test_b_seq owner to jerry; --jerry为test表的属主,如果当前用户即为属主,可不执行此语句 ALTER SEQUENCE postgres=# alter table test alter b set default nextval('test_b_seq'), alter b set not null; ALTER TABLE postgres=# \d+ test Table "public.test" Column | Type | Modifiers | Storage | Stats target | Description --------+---------+--------------------------------------------------+---------+--------------+------------- a | integer | | plain | | b | integer | not null default nextval('test_b_seq'::regclass) | plain | | Has OIDs: no Distribute By: HASH(a) Location Nodes: ALL DATANODES Options: orientation=row, compression=no sequence在业务中的常见用法 sequence在业务中常被用作在导入时生成主键或唯一列,常见于数据迁移场景。不同的迁移工具或业务导入场景使用的入库方法不同,常见的方法主要可以分为copy和insert。对于seqeunce来讲,这两种场景在处理时略有差别。 场景一:insert下推场景 postgres=# create table test1(a int, b serial) distribute by hash(a); NOTICE: CREATE TABLE will create implicit sequence "test1_b_seq" for serial column "test1.b" CREATE TABLE postgres=# postgres=# create table test2(a int) distribute by hash(a); CREATE TABLE postgres=# postgres=# postgres=# explain verbose insert into test1(a) select a from test2; QUERY PLAN ------------------------------------------------------------------------------------------------ id | operation | E-rows | E-distinct | E-memory | E-width | E-costs ----+------------------------------------+--------+------------+----------+---------+--------- 1 | -> Streaming (type: GATHER) | 1 | | | 4 | 18.41 2 | -> Insert on public.test1 | 40 | | | 4 | 18.25 3 | -> Seq Scan on public.test2 | 40 | | 1MB | 4 | 16.24 Targetlist Information (identified by plan id) --------------------------------------------------------- 1 --Streaming (type: GATHER) Node/s: All datanodes 3 --Seq Scan on public.test2 Output: test2.a, nextval('test1_b_seq'::regclass) Distribute Key: test2.a ====== Query Summary ===== ------------------------------- System available mem: 4669440KB Query Max mem: 4669440KB Query estimated mem: 1024KB Parser runtime: 0.045 ms Planner runtime: 12.622 ms Unique SQL Id: 972921662 (22 rows) 由于在nextval在insert场景下可以下推到DN执行,因此,不管是使用default值的nextval,还是显示调用nextval,nextval都会被下推到DN执行,在上例的执行计划中也能看出,nextval的调用在sequence层,说明是在DN执行的。此时,DN直接向GTM申请序列值,且各DN并行执行,因此效率相对较高。 场景二:copy场景 在业务开发过程中,入库方式除了insert外,还有copy入库的场景。此类场景多见于将文件内容copy入库、使用CopyManager接口入库等,此外,CDM数据同步工具,其实现方式也是通过copy的方式批量入库。在copy入库过程中,如果copy的目标表使用了默认值,且默认值为nextval,处理过程如下: 此场景下,由CN负责向GTM申请序列值,因此,当sequence的cache值较小,CN会频繁和GTM建联并申请nextval,出现性能瓶颈。下面,将针对此种场景说明业务上的性能表现和优化方法。 sequence相关的典型优化场景 业务场景:某业务场景使用CDM数据同步工具做数据迁移,从源端入库目标端GaussDB(DWS)。导入速率与经验值相差较大,业务将CDM并发从1调整为5,同步速率仍无法提升。查看语句执行情况,除copy入库外,其余业务均正常执行,无性能瓶颈,且观察无资源瓶颈,因此初步判断为该业务自身存在瓶颈,查看该表copy相关的作业等待视图情况: 如图所示,由于CDM作业起了5个并发,因此在活跃视图中可以看到5个copy语句,根据这5个copy语句对应的query_id查看等待视图情况如上图所示。可以看到,这5个copy中,同一时刻,仅有1个copy在向GTM申请序列值,其余的copy在等待轻量级锁。因此,即使作业中开启了5并发在运行,实际效果比1并发并不能带来明显提升。 问题原因:目标表在建表时使用了serial类型,默认创建的sequence的cache为1,导致在并发copy入库时,CN频繁与GTM建联,且多个并发之间存在轻量锁争抢,导致数据同步效率低。 解决方案:此种场景下可以调大sequence的cache值,防止频繁GTM建联带来的瓶颈。本例中,业务每次同步的数据量在10万左右,综合其他使用场景评估,将cache值修改为10000(实际使用时应根据业务设置合理的cache值,既能保证快速访问,又不会造成序列号浪费)。 当前GaussDB(DWS)不支持通过alter sequence的方式修改cache值,那么如何修改已有sequence的cache值呢?以第二节中方法二的test表为例,可以通过如下方式达到修改cache的目的: -- 解除当前sequence与目标表的关联关系 alter sequence test_b_seq owned by none; alter table test alter b drop default; -- 记录当前的seqeunce值并删除sequence select nextval('test_b_seq'); --记录该值,作为新建sequence的start value drop sequence test_b_seq; -- 新建seqeunce并绑定目标表 create sequence test_b_seq START with xxx cache 10000 owned by test.b; -- xxx替换为上一步查到的nextval alter sequence test_b_seq owner to jerry; --jerry为test表的属主,如果当前用户即为属主,可不执行此语句 alter table test alter b set default nextval('test_b_seq'), alter b set not null; 参考链接: https://bbs.huaweicloud.com/blogs/180833 https://bbs.huaweicloud.com/blogs/338904 点击关注,第一时间了解华为云新鲜技术~

优秀的个人博客,低调大师

解读数仓中的数据对象及相关关系

摘要:为实现不同的功能,GaussDB(DWS)提供了不同的数据对象类型,包括索引、行存表、列存表及其辅助表等。这些数据对象在特定的条件下实现不同的功能,为数据库的快速高效提供了保证,本文对部分数据对象进行介绍。 本文分享自华为云社区《GaussDB(DWS)之数据对象及相互关系总结》,作者:我的橘子呢 。 为实现不同的功能,GaussDB(DWS)提供了不同的数据对象类型,包括索引、行存表、列存表及其辅助表等。这些数据对象在特定的条件下实现不同的功能,为数据库的快速高效提供了保证,本文对部分数据对象进行介绍。 1.索引(index) 索引是关系型数据库中对某一列或者多个列的值进行预排序的数据结构。如果数据库的记录非常多,通过建立索引可以获得非常快的查询速度,当对某一列建立索引之后,通过该列进行相关查询时数据库系统就不必扫描整个表,而是直接通过索引定位到符合条件的记录,在一定程度上能够大幅提升查询得速度。 假如需要执行如下的语句进行查询: SELECT name FROM test_1 WHERE number =10; 一般情况下数据库需要对每一行进行遍历查询,直到找到所有满足条件number=10的元组信息。当数据库的记录很多,而满足where条件的记录又很少时,顺序扫描的性能就会很差。这时如果在表test_1的number属性上建立索引,用于快速定位需要匹配的元组信息,数据库只需要根据索引的数据结构进行搜索,由于常用的索引结构有B-Tree、Hash、GiSt、GIN等,这些索引结构的查询都是快速高效的,因此可以在少数几步内完成查询,大大提高了查询效率。 对表test_1的number属性建立索引语句如下: CREATE INDEX numberIndex ON test_1(number); 由于GaussDB里的所有索引都是“从属索引”,索引在物理文件上与原来的表文件分离,执行上述创建索引语句后,系统会生成relname为numberIndex的索引类型。表和索引都是数据库对象,在pg_class里会有该索引的记录,有与之相对应的oid,同时在pg_index表里会记录索引及其对应主表的信息。对应属性信息如图1所示。 图1 pg_index部分属性 2.toast表 toast(The Oversized-Atttibute Storage Techhnique)即超尺寸字段存储技巧,是数据库提供的一种存储大数据的机制。只有一些具有变长表现形式的数据类型才会支持toast,比如TEXT类型。由于在GaussDB(DWS)的行存储方式中,一条数据的所有列组合在一起称之为一个tuple,多个tuple组成一个page。page是数据在文件存储中的基本单位,其大小是固定的且只能在编译器指定,之后无法修改,默认发大小为8KB,当某行数据很大超过page的大小时,数据库系统就会启动toast,对数据进行压缩和切片。实际数据以行外存储的形式存储在另外一张表中,这张表就是toast表。 当一张表的任何一个属性是可以toast的,则这张表会有一张关联的toast表,在pg_class里表的reltoastrelid属性里记录了该toast表的oid,如果没有关联的toast表,reltoastrelid=0。那么如何判断一张表的属性是否是可以toast的呢?我们可以在表的Storage选项中查看对应属性的存储策略。有以下四种不同的存储策略: PLAIN:避免压缩或者行外存储;此外,它禁止为变长类型使用单字节的头。 这只对那些不能TOAST的数据类型的列才有可能。 EXTENDED:允许压缩和行外存储。 这是大多数TOAST数据类型的缺省策略。首先会尝试对数据进行压缩, 如果行仍然太大,则进行行外存储。 EXTERNAL:允许行外存储,但是不许压缩。 使用EXTERNAL,将使那些数据类型为text和bytea的字段上的子字符串操作更快 (代价是增加了存储空间),因为这些操作是经过优化的:如果行外数据没有压缩,那么它们只会获取需要的部分。 MAIN:允许压缩,但不允许行外存储。 实际上,在这样的字段上仍然会进行行外存储, 但只是作为没有办法把数据行变得更小以使之足以放置在一个页面中的最后选择。 假如创建表语句如下: CREATE TABLE test_t(id int,description text); 创建了一张test_t表,该表有id和description两个属性,分别属于int和text类型,查看该表的属性对应的Storage策略: 图2 test_t表相关信息 我们可以看出description属性的Storage策略为EXTENDED,是可以toast的,系统会为test_t表创建一张关联的toast表。 图3 test_t表对应toast表 通过查询pg_class,可以的看到表test_t关联的toast表的oid为52579,进一步以此oid为条件在pg_class里就会得到toast表的相关信息。 图4 toast表相关信息 下图为test_t表和其对应的toast表之间的关系,以及toast表一些基本属性的介绍。 图5 test_t与其toast表关系图 3.cudesc表 GaussDB(DWS)除了提供行存储方式外,还支持列存储方式。列存储方式在数据压缩、列批量数据的运算、大数据统计分析等场景中有着显著的优势。CU(Compress Unit)压缩单元是列存储的最小单位,每列默认60000行存储在一个CU中,CU生成后数据 固定不可更改。CUDesc本身是一张行存表,它用来辅助记录列存表的cu信息,该表的每一行描述一个CU,包括最大值最小值以及CU在文件中的偏移量和大小,连续多个行中各个不同的列的cu_id相同,可以认为就是把连续多个行截断拿出来,然后再根据不同的列,放到不同的cu中,这些CU所在的行数都是一致的,用一个cu_id表示,但是col_id不一样。同时还增加了一个col_id=-10的列,这个列为VCU,表示这些连续的行中,有哪些行已经是被删除了,用delete_map记录删除信息。如图6所示。 图6 cudesc表示意图 每张列存表都有一张对应的CUDesc表,CUDesc表的oid可以在pg_class中对应列存表元组的relcudescrelid属性中查到,所有CUDesc表默认存储在namespace oid = 100,name为cstore的namespace下。 4.delta表 在列存储方式中,无论是向列存表中插入1条还是60000条数据,都只会生成一个CU,在多次插入少量数据时,不能有效的利用列存压缩能力,导致数据膨胀影响查询的性能和磁盘使用率。CU只支持追加写的方式,也就是说,后面对这个CU中的数据做更新或删除都不会真正更改这个CU,删除是将老数据在字典中标记为作废,更新操作是标记老数据删除后,再写入一条新记录到新CU,原来的CU不会有任何的修改。 从这里我们可以看出,在对列存表进行多次更新/删除,或每次只插入很少量的数据后,会导致列存表空间膨胀,大量空间无法有效利用,这是因为列存表在设计上就是为了大批量数据导入以及海量数据按列存储/查询。Delta表正是为了解决这两个问题。在启用delta表后,单条或者小批量数据导入时,数据将进入delta表中,避免小CU的产生,delta表的增删改查与行存表一致。开启delta表后,将显著提升列存表单条导入的性能。 delta表同样是一张行存表,为了辅助列存表而存在。在创建列存表时系统会为该列存表创建一张对应的delta表,delta表的oid可以在pg_class中对应列存表元组的reldeltarelid属性中查到,所有delta表也默认存储在namespace oid = 100,name为cstore的namespace下。 创建一张列存表col_test,同时设置reloption属性enable_delta=true。在pg_class中查看该表对应的delta表oid。 图7 创建列存表并开启delta表 进一步根据该oid信息可以查到delta表的对应信息。 图8 查询delta表相关信息 可以指定reloption选项设置是否为该列存表开启delta表: 图9 开启/关闭delta表操作 5.分区表 分区表就是把逻辑上的一张表根据某种方案分成几张物理块进行存储。这张逻辑上的表称之为分区表,物理块称之为分区。分区表是一张逻辑表,不存储数据,数据实际是存储在分区上的。分区表的定义不难理解,下面我们通过一个例子说明分区表的用法。 创建一张有id和name两个属性的分区表part_test,该表以id的大小进行分区,其中id<10的数据存储在分区location_1,10≤id<20的数据存储在分区location_2,所有id≥20的数据存储在分区location_3。 CREATE TABLE part_test(id int,name text) partition BY range(id) (partition locatition_1 values less than (10),partition locatition_2 values less than (20),partition locatition_3 values less than (maxvalue)); 创建好part_test表后,我们所有的增删改查都是直接对part_test表操作的,对用户操作来说part_test表与普通表没有什么区别,但实际的存储方式却是严格按照分区的划分方式进行存储的,数据存储在各个分区上,part_test表作为一张逻辑表不保存数据。我们可以通过pg_partition这张系统表查询到一张分区表的分区信息。 图10 part_test表分区信息 分区表和分区的关系如图所示: 图11 分区表和分区关系图 6.各类表相关对象总结 点击关注,第一时间了解华为云新鲜技术~

优秀的个人博客,低调大师

ERD Online 4.0.7 在线数据库建模、元数据管理

❝ feat(erd): 增加新春火红主题 feat(erd): 增加团队协作人员进入、退出提示 fix(erd): 修复权限配置页面显示混乱 doc(erd): 修改更新通告地址 ❞ 变化一览 增加新春火红主题 新春主题 所有按钮、菜单、元素由原来的蓝色改为火红色 修复权限配置页面显示混乱 团队功能 增加团队协作人员进入、退出提示 团队提示 交流渠道 官方论坛:https://www.zerocode.net.cn/ ERD Online文档:https://portal.zerocode.net.cn/ ERD Online体验:https://erd.zerocode.net.cn/ 低代码LOCO体验:https://loco.zerocode.net.cn/

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册