À 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.
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 ...).
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))
Un programme qui lit un fichier TSV contenant la latitude et la longitude et génère une zone correspondante.
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()
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()
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
Même si j'utilise asyncio, ça ne va pas plus vite ... Est-ce la bonne situation à utiliser?
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.
[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