Solución:
La respuesta clásica a este problema es utilizar 127.0.0.1
o la IP del anfitrión o la nombre de host en lugar del “nombre especial” localhost
. De la documentación:
[…] conexiones en Unix a localhost se hacen usando un archivo de socket Unix por defecto
Y después:
En Unix, los programas MySQL tratan el nombre de host localhost especialmente, de una manera que probablemente sea diferente de lo que espera en comparación con otros programas basados en la red. Para las conexiones a localhost, los programas MySQL intentan conectarse al servidor local utilizando un archivo de socket Unix. Esto ocurre incluso si se proporciona una opción –port o -P para especificar un número de puerto. Para asegurarse de que el cliente realiza una conexión TCP / IP con el servidor local, utilice –host o -h para especificar un valor de nombre de host de 127.0.0.1, o la dirección IP o el nombre del servidor local.
Sin embargo, este simple truco no parece funcionar en su caso, por lo que de alguna manera debe fuerza el uso de un socket TCP. Como usted mismo lo explicó, al invocar mysql
en la línea de comando, usa el --protocol tcp
opción.
Como se explica aquí, desde SQLAlchemy, puede pasar las opciones relevantes (si las hay) a su controlador como opciones de URL o utilizando el connect_args
argumento de palabra clave.
Por ejemplo usando PyMySQL, en un sistema de prueba que configuré para ese propósito (MariaDB 10.0.12, SQLAlchemy 0.9.8 y PyMySQL 0.6.2) obtuve los siguientes resultados:
>>> engine = create_engine(
"mysql+pymysql://sylvain:[email protected]/db?host=localhost?port=3306")
# ^^^^^^^^^^^^^^^^^^^^^^^^^^
# Force TCP socket. Notice the two uses of `?`
# Normally URL options should use `?` and `&`
# after that. But that doesn't work here (bug?)
>>> conn = engine.connect()
>>> conn.execute("SELECT host FROM INFORMATION_SCHEMA.PROCESSLIST WHERE ID = CONNECTION_ID()").fetchall()
[('localhost:54164',)]
# Same result by using 127.0.0.1 instead of localhost:
>>> engine = create_engine(
"mysql+pymysql://sylvain:[email protected]/db?host=localhost?port=3306")
>>> conn = engine.connect()
>>> conn.execute("SELECT host FROM INFORMATION_SCHEMA.PROCESSLIST WHERE ID = CONNECTION_ID()").fetchall()
[('localhost:54164',)]
# Alternatively, using connect_args:
>>> engine = create_engine("mysql+pymysql://sylvain:[email protected]/db",
connect_args= dict(host="localhost", port=3306))
>>> conn = engine.connect()
>>> conn.execute("SELECT host FROM INFORMATION_SCHEMA.PROCESSLIST WHERE ID = CONNECTION_ID()").fetchall()
[('localhost:54353',)]
Como notó, ambos usarán una conexión TCP (lo sé por el número de puerto después del nombre de host). Por otra parte:
>>> engine = create_engine(
"mysql+pymysql://sylvain:[email protected]/db?unix_socket=/path/to/mysql.sock")
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
# Specify the path to mysql.sock in
# the `unix_socket` option will force
# usage of a UNIX socket
>>> conn = engine.connect()
>>> conn.execute("SELECT host FROM INFORMATION_SCHEMA.PROCESSLIST WHERE ID = CONNECTION_ID()").fetchall()
[('localhost',)]
# Same result by using 127.0.0.1 instead of localhost:
>>> engine = create_engine(
"mysql+pymysql://sylvain:[email protected]/db?unix_socket=/path/to/mysql.sock")
>>> conn = engine.connect()
>>> conn.execute("SELECT host FROM INFORMATION_SCHEMA.PROCESSLIST WHERE ID = CONNECTION_ID()").fetchall()
[('localhost',)]
# Alternatively, using connect_args:
>>> engine = create_engine("mysql+pymysql://sylvain:[email protected]/db",
connect_args= dict(unix_socket="/path/to/mysql.sock"))
>>> conn = engine.connect()
>>> conn.execute("SELECT host FROM INFORMATION_SCHEMA.PROCESSLIST WHERE ID = CONNECTION_ID()").fetchall()
[('localhost',)]
Ningún puerto después del nombre de host: este es un socket UNIX.
En mi configuración (estoy usando mysql-python) simplemente usando 127.0.0.1 en lugar de localhost en la url MySQL SQLAlchemy funciona. La URL completa que estoy usando exactamente para ese escenario (túnel con puerto local 3307) es:
mysql:/user:[email protected]:3307/
Estoy usando SQLAlchemy 1.0.5, pero supongo que eso no importa demasiado …
Esto funcionó para mí:
import pandas as pd
import pymysql
from sqlalchemy import create_engine
cnx = create_engine('mysql+pymysql://<username>:<password>@<host>/<dbname>')
df = pd.read_sql('SELECT * FROM <table_name>', cnx) #read the entire table
Donde las credenciales se agregan a la base de datos mysql de esta manera:
CREATE USER '<username>' IDENTIFIED BY '<password>';
GRANT ALL PRIVILEGES ON *.* TO '<username>' WITH GRANT OPTION;
FLUSH PRIVILEGES;