如何使用 Python+SQLAlchemy 远程连接 MySQL 数据库?

Posted

技术标签:

【中文标题】如何使用 Python+SQLAlchemy 远程连接 MySQL 数据库?【英文标题】:How to connect MySQL database using Python+SQLAlchemy remotely? 【发布时间】:2015-06-04 00:39:05 【问题描述】:

我无法远程访问 mysql。我使用 SSH 隧道,想使用 Python+SQLALchemy 连接数据库 MySQL。

当我在控制台中使用 MySQL 客户端并指定“ptotocol=TCP”时,一切都很好! 我使用命令:

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

我通过 SSH 隧道访问远程数据库。

但是,当我想使用 Python+SQLAchemy 连接到数据库时,我找不到像 —protocol=TCP 这样的选项 否则,我只连接到本地 MySQL 数据库。 请告诉我,有没有办法使用 SQLAlchemy 来做到这一点。

【问题讨论】:

设置一个 ssh 隧道,然后将本地 mysql 指向本地机器上的隧道端口。 mysql 不会知道它正在被隧道化,ssh 会负责将所有内容重定向到应该去的地方。 隧道已经从 X.X.X.X:3306 安装 -> localhost:3306 也许我需要将隧道设置到另一个端口,例如 localhost:3307? @strevg 为了澄清事情:你有一个 MySQL 服务器在你的本地主机上运行。第二个 MySQL 服务器通过 SSH 隧道远程访问。两者同时运行?哪个服务器绑定到本地主机上的哪个端口? 两台服务器都在运行。在远程主机的 3306 端口 - 本地主机的 3306 端口(127.0.0.1)之间建立了隧道。当我使用选项--protocol=TCP 连接到 localhost 端口 3306 时,我可以远程和本地获取所有数据库。不使用 -protocol 我只获得本地数据库列表。 【参考方案1】:

这个问题的经典答案是使用127.0.0.1主机的IP主机名 而不是“特殊名称”localhost。来自documentation:

[...] Unix 上到 localhost 的连接默认使用 Unix 套接字文件

后来:

在 Unix 上,MySQL 程序对主机名 localhost 进行特殊处理,与其他基于网络的程序相比,这种方式可能与您所期望的不同。对于到 localhost 的连接,MySQL 程序尝试使用 Unix 套接字文件连接到本地服务器。即使给出了 --port 或 -P 选项来指定端口号,也会发生这种情况。要确保客户端与本地服务器建立 TCP/IP 连接,请使用 --host 或 -h 指定主机名值 127.0.0.1,或本地服务器的 IP 地址或名称。


但是,这个简单的技巧在您的情况下似乎不起作用,因此您必须以某种方式强制使用 TCP 套接字。正如您自己解释的那样,在命令行上调用mysql 时,您使用--protocol tcp 选项。

正如here 所解释的,来自 SQLAlchemy,您可以使用connect_args 关键字参数将相关选项(如果有)作为 URL 选项传递给您的驱动程序。

例如使用 PyMySQL,在我为此目的设置的测试系统(MariaDB 10.0.12、SQLAlchemy 0.9.8 和 PyMySQL 0.6.2)上,我得到了以下结果:

>>> 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:passwd@127.0.0.1/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',)]

正如您所注意到的,两者都将使用 TCP 连接(我知道这是因为主机名后面的端口号)。另一方面:

>>> 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:passwd@127.0.0.1/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',)]

主机名后没有端口:这是一个 UNIX 套接字。

【讨论】:

不错。这也适用于我使用本地(非root)mysql安装,我需要设置一个非标准的portunix_socket 不幸的是,MySQLdb 似乎变得更加严格。现在你得到一个 TypeError,因为 SQLAlchemy 将端口作为字符串传递,而 MySQLdb 需要一个 int。 ://【参考方案2】:

在我的设置(我正在使用 mysql-python)中,仅在 MySQL SQLAlchemy url 中使用 127.0.0.1 而不是 localhost 即可。我完全用于该场景的完整网址(带有本地端口 3307 的隧道)是:

mysql:/user:passwd@127.0.0.1:3307/

我正在使用 SQLAlchemy 1.0.5,但我想这并不重要......

【讨论】:

确实,127.0.0.1 是 localhost,@BK435,但它不适用于 localhost 而不是 127.0.0.1。这似乎是由于 musql 如何处理“localhost”,坚持通过文件系统套接字而不是 TCP 套接字进行连接。这就是问题中提到 mysql 的选项 --protocol=tcp 的原因。但似乎 127.0.0.1 可以解决问题,并且比当前答案更简单。【参考方案3】:

这对我有用:

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

将凭据添加到 mysql 数据库的位置如下:

CREATE USER '<username>' IDENTIFIED BY '<password>';
GRANT ALL PRIVILEGES ON *.* TO '<username>' WITH GRANT OPTION;
FLUSH PRIVILEGES;

【讨论】:

微小的补充,可能还需要端口。 "mysql+pymysql://:@:/" 使用 sqlalchemy 时不需要导入 pymysql。【参考方案4】:

我试过这个连接xampp服务器的mysql db

from sqlalchemy import create_engine

engine = create_engine("mysql+pymysql://usrnme:passwd@hstnme/dbname")

【讨论】:

以上是关于如何使用 Python+SQLAlchemy 远程连接 MySQL 数据库?的主要内容,如果未能解决你的问题,请参考以下文章

使用 pymssql 的 Python/Flask/sqlAlchemy 环境中的 Adaptive Server 连接失败错误

Python/SQLAlchemy:如何将巨大的红移表保存到 CSV?

如何使用 Python 在 AWS Lambda 上运行 SQLAlchemy

如何在 python 中使用 SQLalchemy 在数据库中有新条目时运行脚本?

如何在 sqlalchemy 查询过滤器中使用用户定义的 python 函数?

如何避免在 SQLAlchemy - python 的多对多关系表中添加重复项?