SELECTSELECT

SELECT

Clones de banco 60x mais rápidos no Snowflake

By Niall WoodwardOct 22, 20223 min read

Esta página também está disponível em English, Deutsch, Español, Français, Italiano e 日本語.

Introdução

Tive o prazer de participar do Coalesce, conferência do dbt em Londres, na semana passada, e assisti a uma palestra excelente do Felipe Leite e do Stephen Pastan, da Miro. Eles contaram como ganharam bastante velocidade trocando os clones de banco por vários clones de tabela. Eu tinha que testar.

Experimentos

Os resultados foram coletados com a query abaixo, que mede a duração de cada processo passando o horário de início da primeira query e o de término da última para a função DATEDIFF:

select
    count(*) as query_count,
    datediff(seconds, min(start_time), max(end_time)) as duration,
    sum(credits_used_cloud_services) as credits_used_cloud_services
from snowflake.account_usage.query_history where query_tag = X;

Otimize seu uso do Snowflake

O SELECT otimiza automaticamente e te ajuda a gerenciar seu uso do Snowflake com facilidade.

Setup

Crie um banco com 10 schemas e 100 tabelas em cada um:

import snowflake.connector

con = snowflake.connector.connect(
    ...
)

for i in range(1, 11):
    con.cursor().execute(f"create schema test.schema_{i};")
    for j in range(1, 101):
	    con.cursor().execute(f"create table test.schema_{i}.table_{j} (i number) as (select 1);")

Controle - Clone do banco

1create database test_1 clone test;

A operação levou 22min 34s para ser executada.

Resultados:

Total de queries 1
Duração 22min 34s
Créditos de cloud services 0,179

Experimento 1 - Clones por schema

import snowflake.connector
from snowflake.connector import DictCursor

def clone_database_by_schema(con, source_database, target_database):
    con.cursor().execute(f"create database {target_database};")
    cursor = con.cursor(DictCursor)
    cursor.execute(f"show schemas in database {source_database};")
    for i in cursor.fetchall():
        if i["name"] not in ("INFORMATION_SCHEMA", "PUBLIC"):
            con.cursor().execute_async(f"create schema {target_database}.{i['name']} clone {source_database}.{i['name']};")

con = snowflake.connector.connect(
    ...
    session_parameters={
        'QUERY_TAG': 'test 2',

Expandir código

Resultados:

Total de queries 12
Duração 1min 47s
Créditos de cloud services 0,148

Com execute_async, cada instrução SQL é executada sem esperar a anterior terminar, e os 10 schemas acabam sendo clonados em paralelo. Resultado: 10x mais rápido de ponta a ponta em comparação com o clone de banco tradicional.

Experimento 2 - Clones por tabela

import snowflake.connector
from snowflake.connector import DictCursor

def clone_database_by_table(con, source_database, target_database):
    con.cursor().execute(f"create database {target_database};")
    cursor = con.cursor(DictCursor)
    cursor.execute(f"show tables in database {source_database};")
    results = cursor.fetchall()
    schemas_to_create = {r['schema_name'] for r in results}
    tables_to_clone = [f"{r['schema_name']}.{r['name']}" for r in results]

    for schema in schemas_to_create:
        con.cursor().execute(f"create schema {target_database}.{schema};")

    for table in tables_to_clone:

Expandir código

Levou 1 minuto e 48s para concluir, e o fator limitante foi a velocidade com que o cliente conseguia despachar as queries (provavelmente por causa da latência de rede). Para amenizar, distribuí os comandos em 10 threads:

1import snowflake.connector

2from snowflake.connector import DictCursor

3import threading

4

5class ThreadedRunCommands():

6    """Helper class for running queries across a configurable number of threads"""

7    def __init__(self, con, threads):

8        self.threads = threads

9        self.register_command_thread = 0

10        self.thread_commands = [\
\
11            [] for _ in range(self.threads)\
\
12        ]

13        self.con = con

14

15    def register_command(self, command):

Expandir código

Resultados:

Total de queries 1012
Duração 22s
Créditos de cloud services 0,165

Com 10 threads, o tempo entre o início do create database e a conclusão do último create table ... clone foi de apenas 22 segundos. Isso é 60x mais rápido que o comando create database ... clone. O gargalo continua sendo a velocidade com que as queries conseguem ser despachadas.

Resumindo

Os resultados completos:

Estratégia de clonagem Controle - Clone do banco Experimento 1 - Clones por schema Experimento 2 - Clones por tabela
Total de queries 1 12 1012
Duração 22min 34s 1min 47s 22s
Créditos de cloud services 0,179 0,148 0,165

Todas as queries usaram apenas cloud services e não exigiram um warehouse em execução nem retomaram um warehouse suspenso.

Tomara que o Snowflake melhore a clonagem de schemas e bancos, mas, por enquanto, clonar tabelas parece ser o caminho.

Mais uma vez, valeu Felipe Leite e Stephen Pastan, da Miro, por compartilharem isso!

Niall Woodward·Cofundador e CTO do SELECT

Niall é cofundador e CTO do SELECT, uma plataforma SaaS de gestão e otimização de custos do Snowflake. Antes de fundar o SELECT, Niall foi engenheiro de dados na Brooklyn Data Company e em várias startups. Entusiasta de open-source, também é mantenedor do SQLFluff e criador de três pacotes dbt: dbt_artifacts, dbt_snowflake_monitoring e dbt_query_tags.