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.