Publicado por Levy Carneiro Jr. em 18 de February de 2009

Um adaptador MySQL para escalar sua aplicação

O sempre popular MySQL é muito usado por aplicações Rails. E como você sabe o adaptador padrão que geralmente usamos em uma aplicação Rails é o ruby mysql:

  production:
    adapter:  mysql
    database: myapp_production
    username: root
    password:
    host: localhost

Este adaptador faz um lock no interpretador MRI (o interpretador Ruby mais utilizado), fazendo com que sua aplicação fique esperando o resultado da consulta ao banco para poder continuar. Então mesmo que você crie várias threads, cada uma com uma conexão ao MySQL simplesmente esperando por uma query SQL, quando a query for realmente enviada ao banco de dados o adaptador ruby mysql vai segurar a onda, "travando" a aplicação, e só retornando quando uma query for concluída. Por que isso acontece? Porque esta biblioteca é síncrona. Bom, isto não escala muito bem, certo?

A boa notícia é que foi lançada a primeira versão de produção de um novo adaptador para o MySQL, chamada mysqlplus. Com este adaptador, as operações no banco de dados são feitas de forma assíncrona, deixando a aplicação livre para processar outras instruções.

E já que desde a versão 2.2 do Rails, temos o recurso de connection pooling, e o framework também é ThreadSafe (o framework, não o código criado pelo usuário, ou plugins e gems) podemos usar:

config.threadsafe!

Esta configuração remove o lock global do Dispatcher, e permite que à cada requisição seja designada uma conexão do pool.

Finalmente para poder utilizar o adaptador mysqlplus você precisa trocar mysql por mysqlplus em seu arquivo database.yml:

  production:
    adapter:  mysqlplus
    database: myapp_production
    username: root
    password:
    host: localhost
    pool: 10

Existem diversos recursos oferecidos com esta nova biblioteca. Dentre eles temos o Warmup, que permite que as conexões do pool sejam estabelecidas com antecedência, para sites com muito tráfego. Deferrable Results possibilita que o controller envie uma query ao banco de dados para ser rodada em background, retornando o processamento à aplicação, mas referenciando o resultado na view, dessa forma permitindo que o controller tenha um fôlego adicional (de alguns milisegundos) para terminar de rodar a renderização do template (na verdade tudo que acontecer entre o Post.find() e a renderização da view, por exemplo). Isto é muito interessante para escalar sites, aumentando a responsividade de uma página. Você também pode desabilitar o Garbage Collector do MySQL, que por padrão roda a cada 20 queries.

O adaptador expõe seus métodos assíncronos através da instância de conexão:

MysqlUser.connection.send_query( "SELECT * FROM mysql.user WHERE User = 'root'" ) # Retorna no mesmo instante, sem espera por operações de IO
MysqlUser.connection.get_result # Retorna o resultado da query assíncrona prévia

Existe um pequeno cuidado que deve ser tomado:

Model.connection.send_query( "first query" )
Model.connection.send_query( "second query" )
Model.connection.get_result # Retorna o resultado de "second query"

Para mais informações sobre a biblioteca, vá direto ao repositório no Github.

3 Comentários para “Um adaptador MySQL para escalar sua aplicação”

  1. #1
    Pablo Melo Disse:

    Eu ganho alguma vantagem com esse adapter usando o Ruby 1.8?

  2. #2
    Levy Carneiro Jr. Disse:

    Pablo, sim.

    Como na realidade este adapter precisa de threads, você precisa usar Rails 2.2 ou maior.

  3. #3
    Otimização dinâmica do ActiveRecord com Scrooge Disse:

    [...] entra Lourens Naudé, o mesmo criador do mysqlplus_adapter, que criou um plugin chamado [...]

Deixe um Comentário