web-dev-qa-db-fra.com

Comment connecter à distance la base de données MySQL à l'aide de Python + SQLAlchemy?

J'ai de la difficulté à accéder à MySQL à distance. J'utilise le tunnel SSH et souhaite connecter la base de données MySQL en utilisant Python + SQLALchemy.

Lorsque j'utilise MySQL-client dans ma console et spécifie "ptotocol=TCP ", alors tout va bien! J'utilise la commande:

mysql -h localhost —protocol=TCP -u USER -p

J'ai accès à une base de données distante via SSH-tunnel.

Cependant, lorsque je veux me connecter à la base de données à l'aide de Python + SQLAchemy, je ne trouve pas une telle option comme —protocol=TCP Sinon, je ne me connecte qu'aux bases de données MySQL locales. Dites-moi s'il vous plaît, existe-t-il un moyen de le faire en utilisant SQLAlchemy.

10
strevg

La réponse classique à ce problème consiste à utiliser 127.0.0.1 ou IP de l'hôte ou nom d'hôte au lieu du "nom spécial" localhost. De la documentation :

[...] les connexions sous Unix à localhost sont établies en utilisant un fichier socket Unix par défaut

Et ensuite:

Sous Unix, les programmes MySQL traitent le nom d'hôte localhost spécialement , d'une manière qui est probablement différente de ce que vous attendez par rapport à d'autres programmes basés sur le réseau. Pour les connexions à localhost, les programmes MySQL tentent de se connecter au serveur local en utilisant un fichier socket Unix. Cela se produit même si une option --port ou -P est donnée pour spécifier un numéro de port. Pour vous assurer que le client établit une connexion TCP/IP avec le serveur local, utilisez --Host ou -h pour spécifier une valeur de nom d'hôte de 127.0.0.1, ou l'adresse IP ou le nom du serveur local.


Cependant, cette astuce simple ne semble pas fonctionner dans votre cas, vous devez donc en quelque sorte forcer l'utilisation d'un socket TCP. Comme vous l'avez expliqué vous-même, lorsque vous appelez mysql sur la ligne de commande, vous utilisez le --protocol tcp option.

Comme expliqué ici , à partir de SQLAlchemy, vous pouvez transmettre les options pertinentes (le cas échéant) à votre pilote sous la forme d'options d'URL ou en utilisant le connect_args argument de mot clé.

Par exemple, en utilisant PyMySQL, sur un système de test que j'ai configuré à cet effet (MariaDB 10.0.12, SQLAlchemy 0.9.8 et PyMySQL 0.6.2), j'ai obtenu les résultats suivants:

>>> engine = create_engine(
      "mysql+pymysql://sylvain:passwd@localhost/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:passwd@localhost/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',)]

Comme vous l'avez remarqué, les deux utiliseront une connexion TCP (je le sais à cause du numéro de port après le nom d'hôte). Par contre:

>>> engine = create_engine(
      "mysql+pymysql://sylvain:passwd@localhost/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:passwd@localhost/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',)]

Aucun port après le hostname: il s'agit d'un socket UNIX.

18
Sylvain Leroux

Dans ma configuration (j'utilise mysql-python) en utilisant simplement 127.0.0.1 au lieu de localhost dans les travaux d'URL MySQL SQLAlchemy. L'URL complète que j'utilise exactement pour ce scénario (tunnel avec le port local 3307) est:

mysql:/user:[email protected]:3307/

J'utilise SQLAlchemy 1.0.5, mais je suppose que cela n'a pas trop d'importance ...

7
jgbarah