Python使用oracledb连接Oracle数据库的代码示例与TNS配置
本文详细介绍在Python中使用官方新一代驱动python-oracledb连接Oracle数据库的完整方法,涵盖Thin与Thick模式区别、tnsnames.ora配置文件编写、config_dir与TNS_ADMIN寻址路径指定、基础查询与高并发连接池代码示例及常见报错排查。
python-oracledb 是 Oracle 官方推出的新一代 Python 数据库驱动库,也是长期被广泛使用的 cx_Oracle 的官方正统继任者。与旧驱动最大的区别在于,python-oracledb 默认运行在纯 Python 实现的 Thin 模式下,无需在宿主机上下载或配置臃肿的 Oracle Instant Client 动态链接库即可直连数据库。在生产环境与企业多系统部署中,直接硬编码 IP、端口和服务名容易引发耦合过紧、迁移不便等问题;借助透明网络底座(Transparent Network Substrate,简称 TNS)以及 tnsnames.ora 别名配置文件,能将网络地址细节与代码完全解耦。本文将依次讲解驱动安装、双模式特性、TNS 配置语法、寻址路径指定、完整 Python 代码示例以及连接池与故障诊断。
一、认识 python-oracledb 及其连接模式
在编写连接代码之前,读者需要理解 python-oracledb 提供的两种不同运作模式:Thin 模式(轻量模式)与 Thick 模式(兼容模式)。明确两者的运行机理,有助于在架构设计和环境选型时做出正确的决策。
-
Thin 模式(纯 Python 默认模式):驱动内置了 Oracle Net 协议的纯 Python 解析与通信引擎。代码执行
import oracledb后默认即为此模式。该模式完全不依赖 C 共享库(如libclntsh.so、oci.dll),支持直连 Oracle Database 12.1 及更高版本,特别适合 Docker 容器化打包、轻量 Web 微服务和无编译环境的自动化脚本。 -
Thick 模式(C 库兼容模式):当在执行任何数据库连接动作前显式调用
oracledb.init_oracle_client()时,驱动会切换至 Thick 模式。此模式基于 ODPI-C 桥接本地安装的 Oracle Instant Client 动态链接库。该模式主要用于向下兼容已处于维护末期的 Oracle Database 11.2,或者需要使用高级网络安全特性(如 Kerberos、RADIUS 认证、外部认证钱包等)的特殊场景。
无论是 Thin 模式还是 Thick 模式,python-oracledb 均完整支持读取和解析标准 tnsnames.ora 文件。在绝大多数日常开发与企业应用中,推荐优先采用 Thin 模式,以获得免客户端安装的极大便利。
二、环境准备与 TNS 配置文件编写
通过 TNS 连接数据库,本质上是让 Python 驱动在建立网络连接前,根据传入的“网络服务别名(Net Service Name)”,去本地磁盘中找到 tnsnames.ora 文件并提取对应的目标 IP、端口和服务名称。本节演示环境安装及配置文件的编写规范。
1. 安装驱动包
使用 pip 命令安装官方最新版本的 oracledb。需要确认系统中的 Python 版本在 3.8 及以上:
pip install oracledb
cx_Oracle,官方支持直接迁移。在代码顶部执行 import oracledb; oracledb.upgrade_mode(),即可保持原有 cx_Oracle 命名空间的代码无缝向下兼容。
2. 编写 tnsnames.ora 文件
选择一个固定的本地目录(例如 Linux/macOS 环境下的 /opt/oracle/network/admin,或 Windows 环境下的 C:\oracle
etworkdmin,亦或是项目目录下的 ./config 文件夹),创建名为 tnsnames.ora 的纯文本文件。
在文件中添加如下配置内容。这里我们配置两个典型别名:一个是常用的单实例别名 ORCL_PROD,另一个是具备故障转移(Failover)与负载均衡(Load Balance)能力的高可用别名 ORCL_RAC:
# 单实例生产数据库配置
ORCL_PROD =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.1.100)(PORT = 1521))
(CONNECT_DATA =
(SERVER = DEDICATED)
(SERVICE_NAME = orclpdb1.localdomain)
)
)
# RAC 高可用集群配置(带故障转移)
ORCL_RAC =
(DESCRIPTION =
(LOAD_BALANCE = on)
(FAILOVER = on)
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.1.101)(PORT = 1521))
(ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.1.102)(PORT = 1521))
)
(CONNECT_DATA =
(SERVER = DEDICATED)
(SERVICE_NAME = racdb.localdomain)
)
)
3. 配置语法关键项解析
ORCL_PROD:网络服务名别名,后续 Python 代码中的dsn参数将直接使用此名称。PROTOCOL = TCP:通信协议,一般为常规 TCP;如启用了 SSL/TLS 加密则为TCPS。HOST / PORT:数据库监听器所在的主机名或 IP,以及监听端口(默认 1521)。SERVICE_NAME:Oracle 推荐的服务名。注意区分SERVICE_NAME与旧式的SID,连接可插拔数据库(PDB)时必须使用服务名。
4. 确定配置文件的寻址路径
Python 驱动必须知道去哪里寻找 tnsnames.ora。python-oracledb 提供了两种推荐的标准寻址手段:
-
在代码参数中显式指定
config_dir:在调用oracledb.connect()或创建连接池create_pool()时,传入参数config_dir="/opt/oracle/network/admin"。这种方式对宿主环境没有侵入性,不同项目可以各自隔离配置。 -
设置操作系统环境变量
TNS_ADMIN:若不想在代码中写死目录,可在操作系统或部署脚本中设置环境变量,例如export TNS_ADMIN=/opt/oracle/network/admin。驱动在没有显式传递config_dir时,会自动检索该环境变量所指目录。
三、Python 连接代码示例与实战应用
完成配置后,本节通过具体的 Python 脚本演示如何使用 TNS 别名完成基础数据查询、Thick 模式切换以及高并发连接池的管理。
1. 基础连接与游标查询(默认 Thin 模式)
推荐始终使用 Python 的 with 上下文管理器来管理 Connection 和 Cursor。当代码块执行完毕或遇到异常跳出时,系统会自动关闭游标并安全释放数据库会话连接,杜绝连接泄露。
import os
import oracledb
# 数据库账号密码建议通过环境变量注入,避免代码硬编码
DB_USER = os.getenv("DB_USER", "hr")
DB_PASS = os.getenv("DB_PASS", "hr_secure_password")
# TNS 别名与配置文件所在目录
TNS_ALIAS = "ORCL_PROD"
TNS_DIR = "/opt/oracle/network/admin"
def run_query():
# 建立数据库连接:dsn 直接传入别名,config_dir 传入 tnsnames.ora 所在目录
with oracledb.connect(
user=DB_USER,
password=DB_PASS,
dsn=TNS_ALIAS,
config_dir=TNS_DIR
) as connection:
print(f"成功连接至数据库!Thin 模式状态: {connection.thin}")
print(f"数据库服务端版本: {connection.version}")
# 创建游标执行 SQL
with connection.cursor() as cursor:
sql = """
SELECT banner
FROM v$version
WHERE ROWNUM = 1
"""
cursor.execute(sql)
row = cursor.fetchone()
if row:
print(f"数据库 Banner 信息: {row[0]}")
if __name__ == "__main__":
run_query()
2. 厚客户端(Thick 模式)连接示例
当需要对接 Oracle 11g 等早期版本或使用本地 Wallet 证书时,需在程序启动前调用 init_oracle_client()。请注意:该方法在每个 Python 解释器进程的生命周期内只能被成功执行一次。
import oracledb
# 指定 Instant Client C 库目录与 TNS 配置文件目录
CLIENT_LIB_DIR = "/opt/oracle/instantclient_19_19"
TNS_CONFIG_DIR = "/opt/oracle/network/admin"
try:
# 显式初始化 Thick 模式,同时绑定 config_dir
oracledb.init_oracle_client(
lib_dir=CLIENT_LIB_DIR,
config_dir=TNS_CONFIG_DIR
)
print("成功初始化 Oracle Thick 模式客户端")
except Exception as e:
print(f"Thick 模式初始化失败: {e}")
# 初始化完成后,连接调用无需重复传 config_dir
with oracledb.connect(
user="hr",
password="hr_secure_password",
dsn="ORCL_PROD"
) as connection:
print(f"当前 Thin 模式标识: {connection.thin}") # 输出 False 即为 Thick 模式
with connection.cursor() as cursor:
cursor.execute("SELECT sys_context('USERENV', 'SESSION_USER') FROM dual")
print("当前登录会话用户:", cursor.fetchone()[0])
3. 连接描述形式对比:TNS 别名 vs Easy Connect
在实际工程中,除了 TNS 别名,还可以使用 Easy Connect 字符串或直接书写完整的 Connect Descriptor。各方式对比如下:
| 连接形式 | 典型语法示例 | 核心优势 | 主要不足与适用场景 |
|---|---|---|---|
| TNS 别名 (推荐) | dsn="ORCL_PROD" |
网络拓扑集中在配置文件中管理,支持复杂的 RAC 故障转移与负载均衡,代码与网络环境彻底解耦。 | 需在客户端维护 tnsnames.ora 文件,需指定 config_dir。适合生产环境与企业微服务。 |
| Easy Connect | dsn="192.168.1.100:1521/orclpdb1" |
无需任何额外的配置文件,一行代码直接包含地址与端口,开箱即测。 | 无法直接配置负载均衡组或复杂连接参数,IP 变更需要改动代码或环境变量。适合临时调试与单元测试。 |
| 完整描述符 | dsn="(DESCRIPTION=(ADDRESS=...))" |
可直接在代码内编写完整的高级参数,无需物理文件。 | 连接串冗长易错,修改维护成本高。适合无法落盘配置文件的受限环境。 |
4. 生产高并发场景:基于 TNS 的连接池(Connection Pool)
对于 Web API(如 FastAPI、Flask、Django)或高频处理任务,频繁建立与断开 TCP 连接和 Oracle 会话会带来巨大的系统开销。使用 oracledb.create_pool() 构建连接池是企业生产的标准实践。
import oracledb
# 初始化全局连接池
pool = oracledb.create_pool(
user="hr",
password="hr_secure_password",
dsn="ORCL_PROD",
config_dir="/opt/oracle/network/admin",
min=2, # 初始保活连接数
max=10, # 最大并发连接数
increment=1, # 扩容步长
getmode=oracledb.POOL_GETMODE_WAIT,
wait_timeout=5000 # 获取连接超时(毫秒)
)
def query_employee(emp_id: int):
# 从连接池借出连接,使用完成后自动归还连接池
with pool.acquire() as connection:
with connection.cursor() as cursor:
cursor.execute(
"SELECT first_name, last_name, salary FROM employees WHERE employee_id = :id",
id=emp_id
)
return cursor.fetchone()
# 模拟业务调用
if __name__ == "__main__":
result = query_employee(100)
print("员工查询结果:", result)
# 服务停止时关闭连接池
pool.close()
四、TNS 别名解析机制与通信链路深析
当执行 oracledb.connect(dsn="ORCL_PROD", config_dir="...") 时,底层的通信时序与状态流转如下图所示:
整个过程可划分为三个关键阶段:
-
本地寻址与语法解析:驱动定位到
config_dir目录,读取tnsnames.ora文件并对文本进行语法树解析,定位到ORCL_PROD节,解析出HOST、PORT以及SERVICE_NAME。 - TCP 网络握手与监听协商:驱动发起针对目标端口(如 1521)的 TCP 连接。握手成功后,发送 Oracle Net 协议的连接请求包(Connect Packet),携带所需请求的服务名称及版本元数据。
- 监听重定向与会话鉴权:Oracle 监听器接收到请求,核对该实例下服务名是否处于就绪(READY)状态。如果是专用连接模式,监听器创建或重定向到一个 Dedicated Server Process 专有进程,由该进程与 Python 客户端完成用户密码安全哈希握手,最终返回会话建立成功的响应。
五、常见报错原因与排查方案
在配置和使用 TNS 连接的过程中,读者最常遇到以下几类错误。掌握这些错误的核心成因能大幅提升故障定位效率。
1. ORA-12154 或 DPY-4001: 无法解析连接标识符 / 找不到配置文件
现象描述:执行连接时抛出 DPY-4001: cannot open file .../tnsnames.ora 或 ORA-12154: TNS:could not resolve the connect identifier specified。
排查步骤:
- 确认
config_dir传入的目录真实存在,且路径中不要包含文件名本身(只传目录路径,不要写成/path/to/tnsnames.ora)。 - 确认当前运行 Python 脚本的系统用户对该文件拥有读权限(Linux 下建议
chmod 644 tnsnames.ora)。 - 打开
tnsnames.ora,检查左右括号是否严格匹配。如果括号嵌套存在缺失,解析器会直接跳过该别名。 - 检查代码中
dsn="ORCL_PROD"的别名拼写,包括大小写与是否有不可见多余空格。
2. ORA-12541: TNS:no listener(无监听器)
现象描述:网络链路已通,但报错没有监听器响应。
排查步骤:
- 登录 Oracle 数据库服务器,执行
lsnrctl status检查监听器是否已启动。 - 在客户端机器上使用
nc -zv 192.168.1.100 1521或telnet 192.168.1.100 1521,验证中间是否存在防火墙或云安全组阻断了 TCP 1521 端口。
3. ORA-01017: invalid username/password; logon denied(用户名/密码错误)
现象描述:身份验证拒绝。
排查步骤:
- Oracle 11g 及以上版本默认对密码大小写敏感,确认传入的字符串与数据库实际创建时完全一致。
- 如果密码中包含特殊字符(如
@或双引号),在通过环境变量或终端传入时需防止被 Shell 转义破坏。
4. DPY-4000: Oracle Client library cannot be loaded
现象描述:提示无法加载 Oracle Client 动态链接库。
排查步骤:
- 这是由于代码调用了
oracledb.init_oracle_client()进入了 Thick 模式,但操作系统未安装 Oracle Instant Client,或lib_dir路径不正确。 - 若连接的数据库在 12.1 及以上且无需特殊认证,直接删除
init_oracle_client()这一行调用,保留默认的 Thin 模式即可完美运行。
六、技术要点总结与速查清单
最后,我们将使用 python-oracledb 结合 TNS 配置的核心知识点总结为一份开发快速检查清单:
- 模式定夺:绝大多数现代项目直接使用默认的 Thin 模式,享受免装 Instant Client 库的纯净体验;仅在对接老库或特殊加密时调用
init_oracle_client()。 - 别名维护:在
tnsnames.ora中集中维护ADDRESS与CONNECT_DATA,解耦 IP 端口变动。 - 路径指定:优先在代码连接处显式声明
config_dir参数,或者统一配置TNS_ADMIN系统环境变量。 - 资源生命周期:单次事务与批处理严格配合
with oracledb.connect(...)释放连接;长时间常驻服务与 Web 后端统一使用oracledb.create_pool()连接池复用连接。
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













