Sam & Max: Python, Django, Git et du cul » mysql http://sametmax.com Deux développeurs en vadrouille qui se sortent les doigts du code Wed, 05 Feb 2014 14:20:37 +0000 en hourly 1 http://wordpress.org/?v=3.3.1 Afficher le queryset d’une requête dans les logs SQL sous Django http://sametmax.com/afficher-le-queryset-dune-requete-dans-les-logs-sql-sous-django/ http://sametmax.com/afficher-le-queryset-dune-requete-dans-les-logs-sql-sous-django/#comments Wed, 25 Dec 2013 11:38:12 +0000 Max http://sametmax.com/?p=8447 C’est Noël, 2 articles rien que pour vous dont un très interressant de Sam.

L’ORM de django pour les bases de données est chouette, agréable à utiliser mais construit des requêtes SQL qu’on ne peut reconnaître lors de l’analyse des logs MYSQL du premier coup d’oeil. Et quand on a des centaines de requêtes par secondes c’est carrément impossible de s’y retrouver.

Ce que je vous propose ici c’est d’afficher le queryset (sa ligne et le fichier qui le contient) qui a permit d’exêcuter la requête SQL que vous voyez défiler dans les logs SQL sous forme de commentaires SQL.

L’application se nomme Django Sql StackTrace. C’est facile à installer et ça peut sauver des heures de debug.

Installation Django Sql StackTrace:

Une bonne PIP comme toujours pour bien commencer.

pip install django-sql-stacktrace

Dans votre fichier settings de django.

INSTALLED_APPS = (
    .........................
    'sqlstacktrace',
    .........................
)
 
SQL_STACKTRACE = True

La variable SQL_STACKTRACE sert à activer le debug.
Pensez à le désactiver lorsque vous n’en avez pas besoin.

Où se trouve mes super infos de debug ?

D’après la doc vous pouvez executer un watch

watch -n1 mysqladmin -u login -pmot_de_passe processlist --verbose

Chez moi ça n’a rien donné. Mais du côté des logs MySQL la magie a opérée.
Vérifiez tout d’abord que vos logs sont activés dans mysql.

vi /etc/my.cnf
[mysqld]
......
log = /var/logs/mysql.log
......

Comment on teste ça ?

Redemarrez votre serveur web, surfez sur les pages de votre projet et observez les logs MySql. Vous deviez voir quelques chose de similaire:

tail -F /var/logs/mysql.log
		  644 Query	SELECT `auth_user`.`id`, `auth_user`.`password`, `auth_user`.`last_login`, `auth_user`.`is_superuser`, `auth_user`.`username`, `auth_user`.`first_name`, `auth_user`.`last_name`, `auth_user`.`email`, `auth_user`.`is_staff`, `auth_user`.`is_active`, `auth_user`.`date_joined` FROM `auth_user` WHERE `auth_user`.`id` = 65290
/* File "/Users/max/work/mon_projet/apps/mon_apps/views/others.py", line 146, in user_public_page
	user = User.objects.get(pk=user_id)
*/

Observez cette merveille !
Entre /* */ sont les infos générées par django-sql-stacktrace. J’ai nettoyé quelques fichiers pour plus de lisibilité.
Vous avez droit au chemin du fichier de la requête, à la ligne de la requête et à la requête django elle-même.

Une alternative ? J’ai pas envie d’installer d’app.

Pour les grosses feignasses ou si vous voulez juste tester occasionnellement quelques queries vous pouvez utiliser la méthode extra pour ajouter vos propres commentaires.

videos = Video.objects.filter(status='online').extra(where=['1=1 /* ceci apparaitra dans les logs mysql ! */'])

Cependant le WHERE 1=1 peut causer quelques baisses de performances, mais lorsqu’on est en debug en local ça peut servir !

PS: Je rappelle également le formidable outil django-debug-toolbar qui devient vite indispensable.

Alors ? Elle est pas belle la vie ?

flattr this!

]]>
http://sametmax.com/afficher-le-queryset-dune-requete-dans-les-logs-sql-sous-django/feed/ 2
La stack techno qu’on utilise pour faire un site Web, et pourquoi http://sametmax.com/la-stack-techno-quon-utilise-pour-faire-un-site-web-et-pourquoi/ http://sametmax.com/la-stack-techno-quon-utilise-pour-faire-un-site-web-et-pourquoi/#comments Mon, 11 Nov 2013 06:40:38 +0000 Sam http://sametmax.com/?p=7648 Une stack techno n’est pas une référence. Il n’y a pas de combo absolu qui rox absolument tout, c’est une question de contexte technique, financier, humain…

Mais c’est vrai que ça aide bien d’avoir sous les yeux les pratiques des autres.

Je ne vais pas expliquer pourquoi Python, je l’ai déjà fait.

Commençons plutôt par la partie purement Web, pour laquelle on utilise Django, le framework Web Python.

Max et moi avons tout deux fait du PHP avant, j’ai tâté des frameworks internes, du Symfony et plus tard du Zope. J’ai regardé du côté de Pyramid et de ses prédécesseurs, et Django est celui qui me plaît le plus. J’ai juste un peu forcé la main à Max :-)

Car oui, le framework a été avant tout un choix de goût.

Ce n’est pas un choix de performances : le framework n’a aucun impact dessus. Aucun. Les architectures ont un impact. Le framework, non. Votre bottleneck sera sur les IO, pas sur le CPU. Le choix de technos asynchrones peut avoir un impact, mais ce n’est pas une question de framework. Tornado, Twisted ou NodeJS, on s’en fout.

Donc Django, essentiellement parce qu’il me plait. Et il me plaît pour ces raisons :

  • Il y a un bon équilibre entre découplage et intégration. En général c’est soit très découplé et mal intégré, soit très bien intégré et très couplé.
  • C’est bien foutu et bien documenté. Et c’est stable. Vraiment très stable. Les core devs sont hyper sérieux.
  • C’est très versatile et ça peut faire plein de trucs out of the box, petits comme gros.
  • C’est assez facile à apprendre. Ça reste un framework, donc ce n’est pas la plus simple des démarches, mais dans le royaume des frameworks de cette taille, ça reste vraiment le plus simple.
  • La communauté est fantastique : il y a des centaines d’apps qui couvrent pratiquement tous les besoins.
  • Et bien entendu, c’est en Python.

En terme de base de données, on a fait du MySQL pendant longtemps. Ça a plutôt bien marché. Maintenant je commence mes nouveaux projets avec PostGres, qui est plus solide. Parfois je fais juste du Sqlite, parce que ça suffit.

Pas de NoSQL. Après plusieurs expériences avec MongoDB et CouchDB, je n’ai pas été convaincu que les bénéfices dépassaient le coût. Il faudrait un article complet là dessus (qu’on m’a d’ailleurs demandé).

Question OS. c’est du CentOS avec Max (il a plus l’habitude) ou du Ubuntu Server pour mes autres projets. Je reste sur les LTS. Ce n’est pas un choix très réfléchi, c’est surtout par habitude.

Pas de machine virtuelle. On a essayé, sans y trouver un grand intérêt :

  • Il faut quand même faire des scripts de migration, donc autant s’en servir pour le déploiement.
  • On perd en perfs.
  • Les erreurs liées au mal-fonctionnement d’une VM sont absolument indébuggable.
  • Si on ne fait pas la VM soit-même, il faut mettre ses couilles dans les mains d’un pestataire de service. J’ai horreur de ça.
  • Trouver des gens avec la compétence pour gérer une VM, c’est difficile. Un script de déploiement, c’est du code que tout dev saura déjà lire. Par extension ça veut dire que je m’y replonge facilement des semaines plus tard.

Et donc pour le déploiement, j’utilise fabric, avec fabtools.

Ce n’est pas la solution la plus efficace, d’autant que ça limite à Python 2.7, mais c’est la plus simple. C’est juste du code Python. N’importe qui peut comprendre le déploiement en 15 minutes. Ça se modifie vite, s’adapte facilement.

Il faut comprendre qu’on a jamais plus d’une dizaine de serveurs pour un projet, ces choix sont donc fait en fonction de cela. Il va sans dire que si vous gérez un parc de centaines de machines, ça ne sera pas du tout le même choix technique. Peut être que Chef ou des VM seront alors carrément plus interressant. Peut être que le NoSQL et sa capacité au scalling sera bien plus rentable.

Il ne s’agit pas de décrier les technos que nous n’utilisons pas. Il s’agit juste de dire, voilà les choix que nous avons fait, dans tel contexte, pour telles (bonnes ou mauvaises) raisons.

Durant les dernières années, on a ajouté Redis à notre stack. C’est un outil fantastique qui sert à tout : de la base de données pour les trucs simples (il y a des fois ou un schéma est overkill) à la solution de caching. C’est ce qu’on a de plus proche du NoSQL.

L’outil est tellement simple à installer (vraiment le degré zero de la maintenance, c’est beau) et à utiliser que ça ne vaut juste pas le coup de s’en priver.

Du coup, plus de memcache. Toutes les grosses requêtes sont sauvegardées dans Redis, dès qu’on fait un script qui a besoin de persistance temporaire, Redis, pour communiquer entre plusieurs process, Redis, pour toutes les opérations qui ont besoin de grosses perfs comme les stats, Redis. Vive Redis.

D’ailleurs on utilise Redis aussi comme broker pour notre gestionnaire de queues et de taches : celery. Si vous pythonez, je vous recommande chaudement celery pour toutes les tâches en background, les crawlers, les chaînes de process, etc.

On a aussi du moteur de recherche. Là on tappe dans du Solr (avec haystack). C’est très puissant, en tout cas syntaxiquement car ça ne fait pas de sémantique. Ne vous attendez-donc pas à rattraper Google. Mais c’est aussi méga chiant à configurer et très lourd. Je pense qu’un jour on va migrer sur ElasticSearch, mais c’est pas la priorité. Don’t fix what ain’t broken.

Devant tout ça on a Nginx. Comme beaucoup on a fait Apache => Cherokee => lighttp => nginx. Et franchement, je ne reviendrais jamais en arrière : plus léger, plus rapide, plus facile à installer et à configurer, plus versatile. Nginx fait tout, et mieux.

En proxy on a du gunicorn. Parce qu’on avait la flemme de configurer uwsgi et qu’on a pris l’habitude.

Après on utilise plein de libs, de petits outils, etc. Mais ça c’est le gros de notre archi.

flattr this!

]]>
http://sametmax.com/la-stack-techno-quon-utilise-pour-faire-un-site-web-et-pourquoi/feed/ 25
MySQL: créer un utilisateur avec une base de données à son nom sur laquelle il a tous les droits http://sametmax.com/mysql-creer-un-utilisateur-avec-une-table-a-son-nom-sur-laquelle-il-a-tous-les-droits/ http://sametmax.com/mysql-creer-un-utilisateur-avec-une-table-a-son-nom-sur-laquelle-il-a-tous-les-droits/#comments Mon, 15 Oct 2012 15:07:09 +0000 Sam http://sametmax.com/?p=2575 Le truc classique, qu’on fait tout le temps, et qu’on oublie toujours comment on a fait la dernière fois.

Vu qu’on a pas toujours PhpMyAdmin installé…

CREATE DATABASE nom_db;
GRANT ALL PRIVILEGES ON nom_db.* TO "nom_utilisateur"@"localhost" IDENTIFIED BY 'mot_de_passe';
FLUSH PRIVILEGES;

flattr this!

]]>
http://sametmax.com/mysql-creer-un-utilisateur-avec-une-table-a-son-nom-sur-laquelle-il-a-tous-les-droits/feed/ 11
Optimiser Mysql en mettant en cache les requetes SELECT avec query_cache_size + benchmark http://sametmax.com/optimiser-mysql-en-utilisant-le-cache-query_cache_size/ http://sametmax.com/optimiser-mysql-en-utilisant-le-cache-query_cache_size/#comments Tue, 28 Aug 2012 23:25:24 +0000 Max http://sametmax.com/?p=1912 Voici une petite astuce pour mettre en cache les requetes mysql. Pour donner une idée du gain j’ai fait un petit script de benchmark, le gain à la lecture est plutôt convaincant. Je ne suis pas benchmarkeur de profession alors si il y en a dans la salle qui pensent que ce test n’est pas réaliste merci d’apporter votre contribution ;)

Editer le fichier my.cnf pour activer le cache des requetes:

query_cache_type = 1
query_cache_size = 256M

query_cache_type est le type de cache que l’on va adopter:
0 = pas de cache
1 = met en cache toutes les requetes sauf celles qui ont le flag “SELECT S_NO_CACHE”
2 = met en cache seulement les requetes qui comportent le flag “SELECT SQL_CACHE”

Ci dessous le script pour tester les perfs:

#!/usr/bin/python
# -*- coding: utf-8 -*-
 
import MySQLdb as mdb
import sys
 
from timeit import Timer 
 
def benchmark(cur):
    """
        execute query
    """
    cur.execute("SELECT * FROM Writers")
    rows = cur.fetchall()
    #     for row in rows:
    #         print row
 
def create_fixtures():
    """
        Create dummy datas for test
    """
 
    with con:
        cur = con.cursor()
        cur.execute("DROP TABLE IF EXISTS Writers")
        cur.execute("CREATE TABLE IF NOT EXISTS Writers(Id INT PRIMARY KEY AUTO_INCREMENT, Name VARCHAR(25))")
        cur.execute("INSERT INTO Writers(Name) VALUES('Jack London')")
        cur.execute("INSERT INTO Writers(Name) VALUES('Honore de Balzac')")
        cur.execute("INSERT INTO Writers(Name) VALUES('Lion Feuchtwanger')")
        cur.execute("INSERT INTO Writers(Name) VALUES('Emile Zola')")
        cur.execute("INSERT INTO Writers(Name) VALUES('Adolf Hitler')")
        cur.execute("INSERT INTO Writers(Name) VALUES('Ronald McDonalds')")
        cur.execute("INSERT INTO Writers(Name) VALUES('Mamie Nova')")
        cur.execute("INSERT INTO Writers(Name) VALUES('Sam & Max')")
 
def set_query_cache(query_cache_type=1):
    """
        Set query cache
        0 : Don't cache results in or retrieve results from the query cache.
        1 : Cache all query results except for those that begin with SELECT S_NO_CACHE.
        2 : Cache results only for queries that begin with SELECT SQL_CACHE
    """
 
    with con:
        cur = con.cursor()
        cur.execute("SET GLOBAL query_cache_size = 16777216")
        cur.execute("SET SESSION query_cache_type = %s" % str(query_cache_type)) 
 
def start_benchmark(nb_queries=1000, cached_queries=1):
    """
 
    """
    # use cached query benchmark
    print "Starting benchmark: %s reads - query cache type = %s" % (nb_queries, cached_queries)
 
    # set query cache
    set_query_cache(cached_queries)
 
    # run the test
    t = Timer("queries()", "from __main__ import queries")
    print t.timeit(number=nb_queries) 
 
if __name__ == '__main__':
 
    # connect to db
    con = mdb.connect('localhost', 'root', '12345', 'test');
 
    # create dummy datas
    create_fixtures()
 
    with con:
        cur = con.cursor()
        queries = lambda: benchmark(cur=cur)
 
    # launch benchmark
    start_benchmark(nb_queries=10000, cached_queries=1)
    start_benchmark(nb_queries=10000, cached_queries=0)
 
    con.close()

Ce qui me donne…

python mysql_tests.py
Starting benchmark: 10000 reads - query cache type = 1
1.47591710091
Starting benchmark: 10000 reads - query cache type = 0
1.96538686752

Conclusion:
Il semblerait qu’en effet le gain dû au cache est plutôt pas mal, en plus c’est juste 2 params à mettre dans son fichier de config.

Faites part de vos retours, ça peut toujours servir ;)

flattr this!

]]>
http://sametmax.com/optimiser-mysql-en-utilisant-le-cache-query_cache_size/feed/ 6
Le backup wordpress du pauvre http://sametmax.com/le-backup-wordpress-du-pauvre/ http://sametmax.com/le-backup-wordpress-du-pauvre/#comments Wed, 18 Jul 2012 15:36:09 +0000 Sam http://sametmax.com/?p=1198

Backup’s, can’t live with them, can’t live without them.

Mais quand c’est le blog que vous faites pour le fun, vous n’avez pas envie de mettre en place un système compliqué pour faire un backup facile à restaurer.

Typiquement, SamEtMax est là pour le lulz, pas pour la corvée. Ce n’est pas grâve si il se fait pirater, defaced ou whatever. Pas besoin d’un truc hyper secure. Juste de quoi le remettre sur les rails rapidement, avec le minimum d’effort.

La solution inspirée par la méthode Rache:

  • Dans le cron: 28 22 * * 1 mysqldump -u user -ppassword database > /chemin/vers/site/backup.sql
  • Un petit git init, git add ., git commit -m "OSEF";
  • Un petit git pull sur son laptop (histoire d’avoir le code sous la main offline)

Avec les thêmes le premier fetch fait quand même 250 Mo, le coquin.

Ensuite, un cron sur un autre server qui bouffe pas de BP (genre un server d’encoding) avec le git pull dedans, et voilà. Sauvegarde complète, automatique et facile à restaurer.

flattr this!

]]>
http://sametmax.com/le-backup-wordpress-du-pauvre/feed/ 0
Faire une sauvegarde de sa base de données Mysql http://sametmax.com/faire-une-sauvegarde-de-sa-base-de-donnees-mysql/ http://sametmax.com/faire-une-sauvegarde-de-sa-base-de-donnees-mysql/#comments Tue, 17 Apr 2012 21:09:26 +0000 Max http://sametmax.com/?p=419 Une chose à laquelle on pense rarement (enfin surtout qu’on ne fait pas par fainéantise) c’est sauvegarder les données générées sur le serveur, comme la base de données mysql par exemple.

Voici une petite ligne de code que l’on met dans un cron et qui va saucegarder tous les lundis à 20h00 la base de données dans un répertoire de votre choix, vous pouvez également rajouter à la suite l’upload de cette sauvegarde sur un serveur de backup à l’aide de Rsync (voir article sur Rsync)

dans un crontab :

00 20 * * 1 mysqldump -u DB_USERNAME -pDB_PASSWORD DB_NAME > /home/backup/db_name_backup.sql

où:
DB_USERNAME = nom du’tilisateur pour se connecter à mysql
DB_PASSWORD = mot de passe mysql
DB_NAME = nom de la base de données mysql

flattr this!

]]>
http://sametmax.com/faire-une-sauvegarde-de-sa-base-de-donnees-mysql/feed/ 0