[PYTHON] J'ai essayé d'utiliser aiomysql

À PyConJP2016, j'ai appris que Python récent avait asyncio. J'ai compris qu'asyncio permet d'utiliser efficacement le temps d'attente lorsque le temps d'attente se produit dans le programme tel que le traitement d'E / S (probablement), j'ai donc pensé qu'il pourrait être utilisé pour accélérer la requête vers MySQL. Recherchez d'autres articles pour plus d'informations sur asyncio. En laissant MySQL exécuter une requête, j'ai vérifié s'il y avait une différence de temps de traitement entre l'utilisation d'une bibliothèque prenant en charge asyncio et le moment de ne pas l'utiliser. Utilisez aiomysql comme bibliothèque pour la connexion à MySQL qui prend en charge asyncio. aiomysql semble être une bibliothèque basée sur PyMySQL, mais comme il utilisait à l'origine MySQL-Connector-Python, la cible de comparaison est MySQL-Connector-Python.

Conclusion

En conclusion, il n'y avait pas de grande différence de temps de traitement entre aiomysql et MySQL-Connector-Python ... Est-ce que je fais une erreur dans la situation en utilisant asyncio? Est-il coincé dans un verrou à l'intérieur de MySQL?

Ce résultat ne conclut pas que aiomysql est lent, il montre simplement que les requêtes spatiales ne s'accélèrent pas (bien que cette requête SELECT s'accélère également lorsqu'elle est parallélisée dans plusieurs processus ...).

Côté base de données

J'exécute SQL en utilisant l'index spatial de MySQL. Le tableau que j'ai utilisé est le suivant. Ce tableau contient les données de polygones de limites [1] des villes, quartiers, villes et villages japonais.

create table if not exists {TABLE} ( 
    code mediumint(5) not null, 
    name varchar(100) not null, 
    shape multipolygon not null, 
    center point, 
    primary key (code), 
    spatial key shape (shape) 
) engine=MyISAM default charset=utf8;

La requête que j'ai exécutée est:

select code from {TABLE} 
    where st_contains(shape, geomfromtext(%s))

Programme utilisé

Un programme qui lit un fichier TSV contenant la latitude et la longitude et génère une zone correspondante.

Programmes qui utilisent aiomysql

asyncmatcher.py


# coding: utf-8

import sys
import csv
csv.field_size_limit(10000000000)
import asyncio
import aiomysql

TABLE = 'gxmlcity'
contains_sql = ('SELECT code from {table} '
                'WHERE St_Contains(shape, GeomFromText(%s))').format(table=TABLE)


import time
def record_time(func):
    def record(*args, **kwargs):
        start = time.time()
        ret = func(*args, **kwargs)
        elapsed_time = time.time() - start
        print('Elapsed time: {} [sec]'.format(elapsed_time), file=sys.stderr)
    return record


def print_result(cols, result):
    if result:
        print(*(tuple(cols) + result[0]), sep='\t')
    else:
        print(*(tuple(cols) + ('No Match',)), sep='\t')
    if len(result) > 1:
        print(cols, result)

async def match(cur, lat, lon):
    p_str = 'POINT({} {})'.format(lat, lon)
    await cur.execute(contains_sql, (p_str,))
    result = await cur.fetchall()
    return result

async def iterate_to_match(cur, args):
    for cols in csv.reader(args.infile, delimiter='\t'):
        if cols[2] != 'None':
            result = await match(cur, float(cols[2]), float(cols[3]))
            print_result(cols, result)

async def match_areas(loop, args):
    conn = await aiomysql.connect(user='root', password='', db=args.dbname, loop=loop, charset='utf8')
    try:
        cur = await conn.cursor()
        await iterate_to_match(cur, args)
        await cur.close()
    finally:
        conn.close()


def parse_args():
    import argparse
    parser = argparse.ArgumentParser(description='Correspondance de zone asynchrone')
    parser.add_argument('--infile', type=argparse.FileType('r', encoding='utf-8'), default=sys.stdin)
    parser.add_argument('--dbname', required=True, help='Nom de DB avec zone DB')
    return parser.parse_args()

@record_time
def main():
    loop = asyncio.get_event_loop()
    loop.run_until_complete(match_areas(loop, args))
    loop.close()

if __name__ == '__main__':
    args = parse_args()
    main()

Programmes qui utilisent mysql-connector-python

singlematcher.py


# coding: utf-8

import sys
import csv
csv.field_size_limit(10000000000)
import mysql.connector


TABLE = 'gxmlcity'
contains_sql = ('SELECT code from {table} '
                'WHERE St_Contains(shape, GeomFromText(%s))').format(table=TABLE)


import time
def record_time(func):
    def record(*args, **kwargs):
        start = time.time()
        ret = func(*args, **kwargs)
        elapsed_time = time.time() - start
        print('Elapsed time: {} [sec]'.format(elapsed_time), file=sys.stderr)
    return record


def print_result(cols, result):
    if result:
        print(*(tuple(cols) + result[0]), sep='\t')
    else:
        print(*(tuple(cols) + ('No Match',)), sep='\t')
    if len(result) > 1:
        print(cols, result)

def match(cur, lat, lon):
    p_str = 'POINT({} {})'.format(lat, lon)
    cur.execute(contains_sql, (p_str,))
    result = cur.fetchall()
    return result

def iterate_to_match(cur, args):
    for cols in csv.reader(args.infile, delimiter='\t'):
        if cols[2] != 'None':
            result = match(cur, float(cols[2]), float(cols[3]))
            print_result(cols, result)

def match_areas(args):
    conn = mysql.connector.connect(user='root', password='', db=args.dbname, charset='utf8')
    try:
        cur = conn.cursor()
        iterate_to_match(cur, args)
        cur.close()
    finally:
        conn.close()


def parse_args():
    import argparse
    parser = argparse.ArgumentParser(description='La correspondance de zone est généralement effectuée')
    parser.add_argument('--infile', type=argparse.FileType('r', encoding='utf-8'), default=sys.stdin)
    parser.add_argument('--dbname', required=True, help='Nom de DB avec zone DB')
    return parser.parse_args()

@record_time
def main():
    match_areas(args)

if __name__ == '__main__':
    args = parse_args()
    main()

Comparaison

Lors de l'utilisation de aiomysql (ceux qui veulent demander en parallèle)

time ( gzip -dc json_2014-08-01.txt.gz | head -n 1000 | python scripts/asyncmatcher.py --dbname reftest > /dev/null )
Elapsed time: 29.44952368736267 [sec]

real    0m29.581s
user    0m0.736s
sys     0m0.044s

Lors de l'utilisation de mysql-connector-python (généralement)

$ time ( gzip -dc json_2014-08-01.txt.gz | head -n 1000 | python scripts/singlematcher.py --dbname reftest > /dev/null )
Elapsed time: 27.986697673797607 [sec]

real    0m28.183s
user    0m0.620s
sys     0m0.024s

Résumé

Même si j'utilise asyncio, ça ne va pas plus vite ... Est-ce la bonne situation à utiliser?

Environnement d'expérimentation

Je fais une comparaison, mais la version Python est différente. MySQL-Connector-Ceci est dû au fait que Python ne supportait pas Python 3.5.

MySQL-Connector-Lors de l'utilisation de Python

Lors de l'utilisation de aiomysql

Les références

[1] Taihei Morikuni, Mitsuo Yoshida, Masayuki Okabe, Kyoji Umemura. Méthode de filtrage de mots pour estimer la position de publication de tweet. Journal of the Information Processing Society, 2015, vol. 8, n ° 4, p. 16–26.

Recommended Posts

J'ai essayé d'utiliser aiomysql
J'ai essayé d'utiliser paramétré
J'ai essayé d'utiliser argparse
J'ai essayé d'utiliser la mimesis
J'ai essayé d'utiliser anytree
J'ai essayé d'utiliser Summpy
J'ai essayé d'utiliser coturn
J'ai essayé d'utiliser Pipenv
J'ai essayé d'utiliser matplotlib
J'ai essayé d'utiliser "Anvil".
J'ai essayé d'utiliser Hubot
J'ai essayé d'utiliser ESPCN
J'ai essayé d'utiliser openpyxl
J'ai essayé d'utiliser Ipython
J'ai essayé d'utiliser PyCaret
J'ai essayé d'utiliser cron
J'ai essayé d'utiliser ngrok
J'ai essayé d'utiliser face_recognition
J'ai essayé d'utiliser Jupyter
J'ai essayé d'utiliser doctest
J'ai essayé d'utiliser du folium
J'ai essayé d'utiliser jinja2
J'ai essayé d'utiliser du folium
J'ai essayé d'utiliser la fenêtre de temps
[J'ai essayé d'utiliser Pythonista 3] Introduction
J'ai essayé d'utiliser easydict (mémo).
J'ai essayé la reconnaissance faciale avec Face ++
J'ai essayé d'utiliser RandomForest
J'ai essayé d'utiliser BigQuery ML
J'ai essayé d'utiliser Amazon Glacier
J'ai essayé d'utiliser git inspector
J'ai essayé d'utiliser magenta / TensorFlow
J'ai essayé d'utiliser AWS Chalice
J'ai essayé d'utiliser l'émojinateur Slack
J'ai essayé d'utiliser Rotrics Dex Arm # 2
J'ai essayé d'utiliser Rotrics Dex Arm
J'ai essayé d'utiliser GrabCut d'OpenCV
J'ai essayé d'utiliser Thonny (Python / IDE)
J'ai essayé de communiquer avec le client serveur en utilisant tmux
J'ai essayé l'apprentissage par renforcement avec PyBrain
J'ai essayé l'apprentissage en profondeur avec Theano
J'ai essayé d'utiliser le notebook jupyter d'une manière ou d'une autre
[Kaggle] J'ai essayé le sous-échantillonnage en utilisant un apprentissage déséquilibré
J'ai essayé de photographier une vague de tortue en utilisant OpenPose
J'ai essayé d'utiliser l'API checkio
J'ai essayé le traitement asynchrone en utilisant asyncio
J'ai essayé de gratter
J'ai essayé PyQ
J'ai essayé AutoKeras
J'ai essayé le moulin à papier
J'ai essayé django-slack
J'ai essayé Django
J'ai essayé spleeter
J'ai essayé cgo
J'ai essayé d'utiliser Amazon SQS avec django-celery
J'ai essayé de jouer au jeu ○ ✕ en utilisant TensorFlow
J'ai essayé d'utiliser l'API de données YOUTUBE V3