mysql e memcache

Certa vez coloquei um artigo sobre memcache no blog, e agora estou colocando outro  que mostra um pequeno exemplo em python para mascarar da leitura de dados do mysql usando memcache, automaticamente.

A classe abstrata __cache__ servirá de base para criação de outras, que devem fornecer os seguintes parâmetros:

  • __dbinf__: dados do mysql (host, db, user e pass)
  • __query__: o comando a ser executado no banco (tipo SELECT)
  • __label__: a chave que irá guardar os resultados no memcache
  • __tmout__: tempo que a chave ficará na memória (em segundos)

Para rodar esse programa, será necessário instalar o memcached (no debian e ubuntu, basta executar sudo apt-get install memcached), e ter o módulo de cliente do memcached para python. Clientes em outras linguagens também estão disponíveis no wiki do memcached.

Além disso, é necessário ter o mysql instalado, bem como o módulo mysqldb do python (no debian e ubuntu, basta executar sudo apt-get install mysql-server-5.0 python-mysqldb) e criar ao menos um database com alguns dados fictícios, para que o programa possa executar o SELECT.

Na primeira vez que o programa for executado, os dados fornecidos pelo SELECT no mysql serão armazenados no memcache por N segundos (de acordo com __tmout__), e daí em diante a execução consecutiva do mesmo programa irá ler esses dados diretamente da memória, sem usar o mysql.

O código:

#!/usr/bin/env python
# 20090104 AF - mysql and memcache

from memcache import Client
from MySQLdb import connect
from MySQLdb.cursors import DictCursor

mc = Client(['127.0.0.1:11211'], debug=0)

class __cache__:
    __dbinf__ = None
    __query__ = None
    __label__ = None
    __tmout__ = 0

    def __init__(self):
        self.__data__ = mc.get(self.__label__) or self.__load__()

    def __load__(self):
        print 'loading from mysql'
        hs, db, usr, pwd = self.__dbinf__
        cursor = connect(host=hs, db=db, user=usr, passwd=pwd, cursorclass=DictCursor).cursor()
        cursor.execute(self.__query__)
        rs = cursor.fetchall()
        mc.set(self.__label__, rs, time=self.__tmout__)
        return rs

    def __iter__(self):
        for n in self.__data__: yield n

class MyTable(__cache__):
    __dbinf__ = ('localhost', 'mydb', 'myuser', 'mypassword')
    __query__ = 'SELECT * FROM mytable'
    __label__ = 'mydb_mytable'
    __tmout__ = 10

if __name__ == '__main__':
    for item in MyTable(): print item

Reduzir a quantidade de acesso ao banco de dados pode proporcionar um grande desempenho nas aplicações. Por outro lado, armazenar muitos dados na memória pode consumir mais recursos que o desejado, portanto, deve-se usar esse modelo apenas quando o objetivo principal do algoritmo do programa é desempenho.

Ainda, a classe __cache__ pode ser adaptada para suas reais necessidades, e  permitir que determinados dados sejam gravados de volta no mysql quando necessário. A opção cursorclass usada para conectar no mysql oferece um recurso interessante, onde os resultados do SELECT são formatados como dicionários ao invés de simples sets do python – porém, para cada linha retornada, há também o nome das colunas do banco, que causa maior consumo de memória.

Tenho usado bastante o recurso de cache de dados, e minhas aplicações têm se comportado muito bem com isso. Especialmente quando as consultas no banco são demoradas, esse recurso pode otimizar muito o tempo de resposta pois após uma busca, os resultados já ficam na memória e as buscas consecutivas são executadas com extrema rapidez.


python decorator usando classe

Normalmente, usamos os decorators baseados em uma função, com um wrapper dentro – conforme a documentação do PEP 318. É extremamente chato, e pior ainda quando o decorator deve ser aplicado a um método de uma classe. O wrapper é diferente, tudo tem que incluir o self, etc.

A melhor, e mais simples maneira que encontrei, é fazer o decorator como uma própria classe. Além de mais organizado, o código fica muito mais limpo e as possibilidades são muito maiores. O artigo mais completo sobre isso, com exemplos, é do Avinash.

Veja aqui.


meta classes em python

Outro dia precisei fazer com que itens de um dicionário se tornassem atributos de uma classe. Sempre vi isso nos frameworks tipo Django, Turbogears, WebPy e etc. Aí, usando introspecção fiz um loop pelos itens do dicionário e usando setattr criei os atributos da classe com seus respectivos valores.

Esse esquema é trabalhoso, manual, e tosco. Procurando um pouco, encontrei uma função que fazia algo semelhante a essa que escrevi, e que já é padrão no Python.

Veja só:

$ python
Python 2.5.1 (r251:54863, Mar  7 2008, 03:39:23) 
[GCC 4.1.3 20070929 (prerelease) (Ubuntu 4.1.2-16ubuntu2)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from new import classobj
>>> class ZZ: pass
... 
>>> oo = classobj('myobj', (ZZ,), dict(aa=True, bb=False, tt='woah!'))
>>> dir(oo)
['__doc__', '__module__', 'aa', 'bb', 'tt']
>>> oo.aa
True
>>> oo.tt
'woah!'

Agora, posso transformar qualquer conteúdo de dicionário em atributos de um objeto. Usando classobj posso ainda fornecer uma classe base (no exemplo, ZZ) com outros métodos pré-definidos.

Vários outros exemplos estão disponíveis neste link.