Melhores práticas do banco de dados SEQUENCE
Uma sequência, também chamada de sequência, é um objeto de banco de dados usado para gerar um inteiro único. O valor de uma sequência aumenta ou diminui automaticamente com base em determinadas regras. Geralmente, uma sequência é usada como chave primária. No GaussDB(DWS), quando uma sequência é criada, uma tabela de metadados com o mesmo nome é criada para registrar informações de sequência. Por exemplo:
1 2 3 4 5 6 7 8 |
CREATE SEQUENCE seq_test; CREATE SEQUENCE SELECT * FROM seq_test; sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called | uuid ---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------+--------- seq_test | -1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | f | f | 1400050 (1 row) |
No comando anterior:
- sequence_name indica o nome de uma sequência.
- last_value não tem sentido.
- start_value indica o valor inicial da sequência.
- increment_by indica o passo da sequência.
- max_value indica o valor máximo de uma sequência.
- min_value indica o valor mínimo de sequência.
- cache_value indica o número de valores de sequência que são pré-armazenados para obter rapidamente o próximo valor de sequência. (Depois que o cache é definido, a continuidade dos valores de seqüência não pode ser assegurada, os furos são gerados e os segmentos de número de sequência são desperdiçados.)
- log_cnt indica o número de valores de sequência registrados nos logs WAL. No GaussDB(DWS), os valores de sequência são obtidos e gerenciados a partir do GTM. Portanto, log_cnt não tem sentido.
- is_cycled indica se deve continuar o loop após a sequência atingir o valor mínimo ou máximo.
- is_called indica se a sequência foi invocada. (Só indica se a sequência foi invocada na instância atual. Por exemplo, depois que a sequência é invocada em cn1, o valor da tabela de dados original em cn1 muda para t, e o valor do campo em cn2 ainda é f.)
- uuid indica o ID exclusivo da sequência.
Processo de criação de uma sequência
No GaussDB(DWS), o Global Transaction Manager (GTM) gera e mantém informações globalmente exclusivas, como IDs de transações globais, instantâneos de transações e sequências. A figura a seguir mostra o processo de criação de uma sequência no GaussDB(DWS).
O processo específico é o seguinte:
- O CN que aceita o comando SQL solicita um UUID do GTM.
- O GTM retorna um UUID.
- O CN vincula o UUID obtido ao sequenceName criado pelo usuário.
- O CN fornece a relação de vinculação para outros nós, e outros nós criam a tabela de metadados de sequência de forma síncrona.
- O CN envia o UUID e startID da sequência para o GTM para armazenamento permanente.
Portanto, a manutenção da sequência e a solicitação são realmente concluídas no GTM. Ao solicitar nextval, cada instância que invoca nextval solicita um valor de sequência do GTM com base no UUID da sequência. O intervalo de valores de sequência solicitado para cada vez está relacionado ao cache. A instância solicita um valor de sequência do GTM somente depois que o cache é usado. Portanto, aumentar o cache da sequência ajuda a reduzir o número de vezes que o CN/DN se comunica com o GTM.
Dois métodos de criar uma sequência
Método 1: execute a instrução CREATE SEQUENCE para criar uma sequência e use nextval para chamar a sequência na nova tabela.
1 2 3 4 5 |
CREATE SEQUENCE seq_test increment by 1 minvalue 1 no maxvalue start with 1; CREATE SEQUENCE CREATE TABLE table_1(id int not null default nextval('seq_test'), name text); CREATE TABLE |
Método 2: se o tipo serial for usado durante a criação da tabela, uma sequência será criada automaticamente e o valor padrão da coluna será definido como nextval.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
CREATE TABLE mytable(a int, b serial) distribute by hash(a); NOTICE: CREATE TABLE will create implicit sequence "mytable_b_seq" for serial column "mytable.b" CREATE TABLE \d+ mytable Table "dbadmin.mytable" Column | Type | Modifiers | Storage | Stats target | Description --------+---------+-----------------------------------------------------+---------+--------------+------------- a | integer | | plain | | b | integer | not null default nextval('mytable_b_seq'::regclass) | plain | | Has OIDs: no Distribute By: HASH(a) Location Nodes: ALL DATANODES Options: orientation=row, compression=no |
Neste exemplo, uma sequência chamada mytable_b_seq é criada automaticamente. A rigor, o tipo serial não é um tipo real. É apenas um conceito para definir um identificador exclusivo em uma tabela. Quando um tipo serial é criado, uma sequência é criada e associada à coluna.
É equivalente à seguinte afirmação:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
CREATE TABLE mytable01(a int, b int) distribute by hash(a); CREATE TABLE CREATE SEQUENCE mytable01_b_seq owned by mytable.b; CREATE SEQUENCE ALTER SEQUENCE mytable01_b_seq owner to u1; --u1 is the owner of the mytable01 table. If the current user is the owner, you do not need to run this statement. ALTER SEQUENCE ALTER TABLE mytable01 alter b set default nextval('mytable01_b_seq'), alter b set not null; ALTER TABLE \d+ mytable01 Table "dbadmin.mytable01" Column | Type | Modifiers | Storage | Stats target | Description --------+---------+-------------------------------------------------------+---------+--------------+------------- a | integer | | plain | | b | integer | not null default nextval('mytable01_b_seq'::regclass) | plain | | Has OIDs: no Distribute By: HASH(a) Location Nodes: ALL DATANODES Options: orientation=row, compression=no |
Uso comum de sequências em serviços
Sequências são frequentemente usadas para gerar chaves primárias ou colunas exclusivas durante a importação de dados em cenários de migração de dados. Diferentes ferramentas de migração ou cenários de importação de serviços usam diferentes métodos de importação. Os métodos comuns de importação são classificados em copy e insert. Para sequência, o processamento nos dois cenários é ligeiramente diferente.
- Cenário 1:inserir pushdown
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
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 CREATE TABLE test2(a int) distribute by hash(a); CREATE TABLE 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 | 16.34 2 | -> Insert on dbadmin.test1 | 30 | | | 4 | 16.22 3 | -> Seq Scan on dbadmin.test2 | 30 | | 1MB | 4 | 14.21 RunTime Analyze Information ---------------------------------------------------- "dbadmin.test2" runtime: 9.586ms, sync stats Targetlist Information (identified by plan id) --------------------------------------------------------- 1 --Streaming (type: GATHER) Node/s: All datanodes 3 --Seq Scan on dbadmin.test2 Output: test2.a, nextval('test1_b_seq'::regclass) Distribute Key: test2.a ====== Query Summary ===== ------------------------------- System available mem: 1351680KB Query Max mem: 1351680KB Query estimated mem: 1024KB Parser runtime: 0.076 ms Planner runtime: 12.666 ms Unique SQL Id: 831364267 (26 rows)
No cenário INSERT, nextval pode ser empurrado para baixo para DNs para execução. Portanto, nextval é enviado para DNs para execução, independentemente de nextval com o valor padrão ser usado ou nextval ser chamado explicitamente. O plano de execução no exemplo anterior também mostra que nextval é empurrado para baixo para DNs para execução, a invocação de nextval está na camada de sequência, indicando que nextval é executado em DNs. Neste caso, os DNs solicitam diretamente valores de sequência do GTM, e os DNs executam a solicitação simultaneamente. Portanto, a eficiência é relativamente alta.
- Cenário 2: copiar cenário
Durante o desenvolvimento do serviço, além de INSERT, COPY pode ser usado para importar dados para o banco de dados. Esse método é usado para copiar o conteúdo do arquivo para o banco de dados ou usar a interface do CopyManager para importar o conteúdo do arquivo para o banco de dados. Além disso, a ferramenta de sincronização de dados do CDM importa dados para o banco de dados em lotes, copiando dados. Se a tabela de destino a ser copiada usar o valor padrão de nextval, o processo será o seguinte:
No cenário de cópia, o CN solicita valores de sequência do GTM. Portanto, quando o valor de cache da sequência é pequeno, o CN frequentemente estabelece conexões com o GTM e solicita para nextval, causando um gargalo de desempenho. Cenários típicos de otimização relacionados a sequências descreve o desempenho do serviço nesse cenário e fornece métodos de otimização.
Cenários típicos de otimização relacionados a sequências
Cenário de serviço: em um cenário de serviço, a ferramenta de sincronização de dados do CDM é usada para migrar dados e importar dados da extremidade de origem para o GaussDB(DWS) de destino. A taxa de importação difere muito do valor empírico. Depois que a simultaneidade do CDM é alterada de 1 para 5, a taxa de sincronização ainda não pode ser melhorada. Verifique o status de execução da instrução. Exceto COPY, outros serviços são executados corretamente, sem gargalos de desempenho ou gargalos de recursos. Por isso, apura-se preliminarmente que o serviço tem um gargalo. Verifique a exibição de trabalho em espera relacionada a COPY.
Como mostrado na figura anterior, cinco trabalhos do CDM são executados simultaneamente. Portanto, você pode ver cinco instruções COPY na exibição ativa. Verifique a exibição em espera com base em query_id correspondente às cinco instruções COPY. Entre as cinco cópias, apenas uma cópia está solicitando um valor de sequência do GTM ao mesmo tempo, e outras cópias estão esperando por um bloqueio leve. Portanto, mesmo que cinco trabalhos simultâneos estejam ativados, o efeito real não é significativamente melhorado em comparação com o de um trabalho concorrente.
Motivo: o tipo serial é usado quando a tabela de destino é criada. Por padrão, o cache da sequência criada é 1. Como resultado, quando os dados são copiados simultaneamente para o banco de dados, o CN frequentemente estabelece conexões com o GTM, e a contenção de bloqueio leve existe entre várias tarefas simultâneas, resultando em baixa eficiência de sincronização de dados.
Solução: nesse cenário, aumente o valor de cache da sequência para evitar gargalos causados pelo estabelecimento de conexão do GTM frequente. Neste exemplo de cenário de serviço, cerca de 100.000 registros de dados são sincronizados cada vez. Com base na avaliação do serviço, altere o valor do cache para 10.000. (Na prática, defina um valor de cache adequado com base em serviços para garantir o acesso rápido e evitar o desperdício de números sequenciais.)
Nas versões de cluster 8.2.1.100 e posteriores, você pode usar ALTER SEQUENCE para alterar o valor do cache.
Em clusters de 8.2.1 e versões anteriores, o valor de cache de GaussDB(DWS) não pode ser alterado usando ALTER SEQUENCE. Você pode alterar o valor de cache de uma sequência existente da seguinte forma (a tabela mytable é usada como exemplo):
- Remova a associação entre a sequência atual e a tabela de destino.
1 2
ALTER SEQUENCE mytable_b_seq owned by none; ALTER TABLE mytable alter b drop default;
- Registre o número de sequência atual como o valor inicial da nova sequência.
1
SELECT nextval('mytable_b_seq');
Elimine uma sequência.
1
DROP SEQUENCE mytable_b_seq;
- Crie sequência e vincule-a à tabela de destino. Substitua xxx pelo valor de nextval obtido na etapa anterior.
1 2 3
CREATE SEQUENCE mytable_b_seq START with xxx cache 10000 owned by mytable.b; ALTER SEQUENCE mytable_b_seq owner to u1;--u1 is the owner of the mytable table. If the current user is the owner, you do not need to run this statement. ALTER TABLE mytable alter b set default nextval('mytable_b_seq');