db_name和db_unique_name的区别

在一般的数据库里我们看到倒是没有什么特殊的差异的,不过在多实例多数据系统里,这两个倒是经常被提及。不小心就把两个看成一个东东了。其实不然。

这里个人吧对其的理解归纳如下。

DB_NAME 数据库名称,也就是数据库的名字标示。这里,数据库里可能有多个实例,比如RAC里的多节点,这多个节点是不同的实例,但是却有相同的名字,他们的 DB_NAME是相同的但是Instance_name是不同的。DB_NAME会保持在数据文件头里,所以更改DB_NAME不能仅仅修改parameter,还需要用nid 来进行更改,并且更改后还需要手工做些工作,是其生效。

DB_UNIQUE_NAME这在另一个HA的应用,Dataguard会经常提及的,和DB_NAME不一样的作用,在DG里,要求物理DG,主从库都有一样的DB_NAME,虽然他们和RAC不一样,并不是同一个库。这里是数据库的唯一名字。但是他们的DB_UNIQUE_NAME是不一样的,用以进行不同的标示。DB_UNQUIE_NAME的会影响到Service_names,也会影响到动态监听的时候的service_name
比如如下片段
Service "zxdbdg1" has 1 instance(s).
Instance "zxdb", status BLOCKED, has 1 handler(s) for this service...
Service "zxdbdg1_XPT" has 1 instance(s).
Instance "zxdb", status BLOCKED, has 1 handler(s) for this service...
The command completed successfully
这里的zxdbdg1就是dg中的从库。从库的db_name和主库保持一样为zxdb,DB_UNIQUE_NAME不同。在动态监听后,注册为
zxdbdg1的service,启动的instance_name还是zxdb

Instance_name简单讲就是ORACLE_SID,oracle里通过ORSCLE_SID来管理不同的数据库实例。

另,上面的动态监听信息里出现了Instance "zxdb", status BLOCKED

这里是因为我的从库数据库不是open状态。

 

 1、db_name 数据库名
SQL> connect xys/manager as sysdba
已连接。
SQL> show user
USER 为 "SYS"
SQL> show parameter db_name

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
db_name string test1

db_name是数据库的名字,oracle本身可能是比较有用的,对我们而言没有什么太多的用处,db_name记录在controlfile,datafile_header,redo中,要想修改db_name是比较麻烦的有两种办法:1、重建controlfile,之后要求必须以reseglogs方式打开数据库;2、通过nid。另外在建库时db_name被限制为最长8个字符,尽管10g在创建时没有错误提示了,但是看看库里最多能存下几个字符就明白了,为什么即使输入超过8个字符不会报错,但是最终还是被截断了,之前我就为一个企业处理过截断db_name而引起的一些问题。

SQL> desc v$database;
名称                                      是否为空? 类型
----------------------------------------- -------- ------------------------

DBID                                               NUMBER
NAME                                               VARCHAR2(9)

SQL>
因此在建库时指定恰当的db_name还是非常重要的。db_name还有一个非常重要的作用就是动态注册监听,不管是否指定了service_name,或者说service_name的值是什么,pmon都会使用db_name动态注册监听的。

SQL> host lsnrctl status

LSNRCTL for 32-bit Windows: Version 10.2.0.1.0 - Production on 03-12月-2007 10:1
9:36

Copyright (c) 1991, 2005, Oracle.  All rights reserved.

正在连接到 (ADDRESS=(PROTOCOL=tcp)(HOST=)(PORT=1521))
LISTENER 的 STATUS
------------------------
别名                      LISTENER
版本                      TNSLSNR for 32-bit Windows: Version 10.2.0.1.0 - Produ
ction
启动日期                  03-12月-2007 09:29:47
正常运行时间              0 天 0 小时 49 分 50 秒
跟踪级别                  off
安全性                    ON: Local OS Authentication
SNMP                      OFF
监听程序日志文件          e:\oracle\product\10.2.0\db_1\network\log\listener.log

监听端点概要...
  (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=xys)(PORT=1521)))
服务摘要..
服务 "TEST2.COM" 包含 1 个例程。
  例程 "inst_test", 状态 READY, 包含此服务的 1 个处理程序...
服务 "TEST3.COM" 包含 1 个例程。
  例程 "inst_test", 状态 READY, 包含此服务的 1 个处理程序...
服务 "test1.COM" 包含 1 个例程。
  例程 "inst_test", 状态 READY, 包含此服务的 1 个处理程序...
服务 "test1_XPT.COM" 包含 1 个例程。
  例程 "inst_test", 状态 READY, 包含此服务的 1 个处理程序...
命令执行成功

SQL> show parameter service_names

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
service_names                        string      TEST2, TEST3

我们发现service_names的值是TEST2, TEST3,但是lsnrctl status显示的结果中包含了“

服务 "test1.COM" 包含 1 个例程。
  例程 "inst_test", 状态 READY, 包含此服务的 1 个处理程序...”

2、instnace_name 实例名

SQL> show parameter instance_name

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
instance_name string inst_test
instance_name除了动态注册监听用到之外,到目前为止我没有发现其他用处,也许oracle用它来区分各个实例?不过相信仅仅通过instance_name也不能完全区分,至少也的用到sid吧,看看上面显示出来的动态注册监听中的信息,其中inst_test就是instance_name

3、SID:System Identifier

The SID identifies the instance's shared memory on a host, but may not uniquely distinguish this instance
from other instances

doc上把sid解释为在host上用sid来标示实例的共享内存的,可见sid主要是和os打交道的。

sid可以通过如下语句在库中查询:

SQL> select instance_name from v$instance;

INSTANCE_NAME
----------------
tsid

尽管v$instance中字段 instance_name 看起来是实例名,但是实际上存储的是sid,在win下sid不能重复,不管oracle_home是否相同,相同当然不行,主要是不同也不行,这里的不同是针对unix/linux而言的,在unix/linux下只要不同版本的oracle安装在不同的oracle_home下就可以创建相同sid的实例,但是win下不可以,这不是由oracle决定的,主要是受到windows服务的限制,在服务中不能存在服务名相同的oracle服务,服务名是由如下格式组成的:OracleServiceSID,因为服务名中包括了sid,所以sid如果相同了,服务名就相同了,这是windows所不允许的。因此在win下无法创建相同sid的不同实例。

4、service_names 服务名

服务名是复数,大家看好了,意味着service_names 可以是多个值,这里的服务名除了在动态注册的监听中被用到之外,没有发现其它用处,还有其它用处大家可以补充,dataguard中建议大家在primary,standby上使用相同的service_names,这样可能便于尽可能的实现透明切换,前提是如果没有配置静态静听的话,当然如果配置了静态注册的监听在primary,standby上也务必保持在listener中要求输入的服务名相同,还是那句话,尽可能的实现透明切换。下面查询可以显示service_names:

SQL> show parameter service_names

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
service_names string TEST2, TEST3

这里我指定了2个值test2,test3,再来看看动态注册的监听是如何使用服务名的,监听的部分状态信息如下:

服务 "TEST2.COM" 包含 1 个例程。
例程 "inst_test", 状态 READY, 包含此服务的 1 个处理程序...
服务 "TEST3.COM" 包含 1 个例程。
例程 "inst_test", 状态 READY, 包含此服务的 1 个处理程序...

这里我们看到显示出来的服务名有后缀com,是因为我设置了db_domain

5、db_domain 数据库域名

SQL> show parameter db_domain

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
db_domain string COM

doc上说它被"."分割,包括句点最多128个字符,没改过这么长的,不知道,没有验证过,谁想验证就验证一下,db_domain 的作用主要是用在分布式数据库中,分布式事务的各个数据库应该有db_domain ,但是要求他们是否相同,doc上没说,我也不知道,之前单位开发有分布式环境,但是当时没有注意过,不过高级复制中要同步的对象所在的数据库是无论如何也要设置db_domain 的,是否要求相同也不得而知了,我在配置复制的时候把db_domain 设置为相同的了。介绍db_domain 的另一个用途就是在同一个os域中如果要创建同名db_name的数据库时建议最好让具有相同db_name的数据库具有不同的db_domain,以保证在同一个域中global_name是唯一的。doc上也是这样建议的:Oracle recommends that you specify DB_DOMAIN as a unique string for all databases in a domain

当指定了db_domain的时候,在创建db link时会自动在db_link的后面加上db_domain(doc:

If you omit the domains from the name of a database link, Oracle expands the name by qualifying the database with the domain of your local database as it currently exists in the data dictionary, and then stores the link name in the data dictionary. The characters valid in a database domain name are: alphanumeric characters, underscore (_), and number sign (#).

),9i好像记得是这样的,但是10g我验证了一下不是:

SQL> create database link dbl_test using 'orcl';

数据库链接已创建。

SQL> select db_link from dba_db_links;

DB_LINK
----------------------------------------------------------------------------

DBL
DBL_TEST
ORCL

还有一点需要主要的是:You must set this parameter for every instance, and multiple instances must have the same value in Real Application Clusters

6、global_name 全局数据库名

global_name 是由db_name.db_domain构成的,doc如下:
Understanding How Global Database Names Are Formed
A global database name is formed from two components: a database name and a domain. The database name and the domain name are determined by the following initialization parameters at database creation:
Component Parameter Requirements Example
Database name DB_NAME Must be eight characters or less. sales
Domain containing the database DB_DOMAIN Must follow standard Internet conventions. Levels in domain names must be separated by dots and the order of domain names is from leaf to root, left to right. us.acme.com
但是通过验证发现oracle并没有把db_name.db_domain和global_name 同步起来,不知道为什么?global_name oracle是通过提供了一个view,sys.global_name,该试图是源于props$的,可以查看创建view的脚本,最终我们访问的是一个public synonym global_name:

SQL> select * from global_name;

GLOBAL_NAME
--------------------------------------------

test1

按照doc的意思,我上面看到的查询结果应该是test1.com才对,这是我的疑问,好久了?

不过我们也可以修改global_name:

SQL> alter database rename global_name to test1.com;

数据库已更改。

SQL> select * from global_name;

GLOBAL_NAME
--------------------------------------------------------------------------------

TEST1.COM

SQL> alter database rename global_name to test123.com;

数据库已更改。

SQL> select * from global_name;

GLOBAL_NAME
--------------------------------------------------------------------------------

TEST123.COM

SQL>

需要注意的是一旦加上了域就不能通过上面的命令去掉了,如:

SQL> alter database rename global_name to test123.com;

数据库已更改。

SQL> select * from global_name;

GLOBAL_NAME
--------------------------------------------------------------------------------

TEST123.COM

SQL> alter database rename global_name to test1;

数据库已更改。

SQL> select * from global_name;

GLOBAL_NAME
--------------------------------------------------------------------------------

TEST1.COM

SQL> alter database rename global_name to test123;

数据库已更改。

SQL> select * from global_name;

GLOBAL_NAME
--------------------------------------------------------------------------------

TEST123.COM

SQL>

不过可以直接update global_name 或者props$来去掉后缀:

SQL> update global_name set global_name='test1';

已更新 1 行。

SQL> commit;

提交完成。

SQL> select * from global_name;

GLOBAL_NAME
---------------------------------------------------------------

test1

SQL>

global_name 的作用主要也是用在Distributed Database中,我只在高级复制中用过global_name

详细的内容也可以参考下面的连接:
[url=http://download.oracle.com/docs/cd/B19306_01/server.102/b14231/ds_admin.htm#sthref4096]http://download.oracle.com/docs/ ... dmin.htm#sthref4096[/url]


7、global_names 是一个布尔值,为什么要提它,是应为global_names和global_name看起来很相似,global_names的作用是创建db link时是否强制使用远程数据库的global_name,如果global_names=true,则db link name必须要求是remote database的global_name,否则创建之后db link 不能连同,测试如下,缺省值是false
SQL> show parameter global_names

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
global_names boolean TRUE
SQL> select count(*) from t_emp@dbl;
select count(*) from t_emp@dbl
*
第 1 行出现错误:
ORA-02085: 数据库链接 DBL 连接到 ORCL

SQL> col db_link format a10
SQL> col host format a10
SQL> col owner format a10
SQL> col username format a10
SQL> select * from dba_db_links;

OWNER DB_LINK USERNAME HOST CREATED
---------- ---------- ---------- ---------- --------------
SYS DBL TEST orcl 01-12月-07
SYS ORCL TEST orcl 01-12月-07

SQL> select count(*) from t_emp@orcl;

COUNT(*)
----------
4

SQL>
8、在通过netmanager配置静态监听注册时,需要输入的全局数据库名(GLOBAL_DBNAME )到底应该输入什么?而通过netmanager配置网络服务命名(tns)是需要输入的服务名(SERVICE_NAME )又是什么?

listener.ora内容如下:

SID_LIST_LISTENER =
  (SID_LIST =
    (SID_DESC =
      (GLOBAL_DBNAME = test)
      (ORACLE_HOME = E:\oracle\product\10.2.0\db_1)
      (SID_NAME = tsid)
    )
  )

LISTENER =
  (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = xys)(PORT = 1521))
  )

--================================

tnsnames.ora内容如下:

TEST =
  (DESCRIPTION =
    (ADDRESS_LIST =
      (ADDRESS = (PROTOCOL = TCP)(HOST = xys)(PORT = 1521))
    )
    (CONNECT_DATA =
      (SERVICE_NAME = test)
    )
  )

--================================

这里明确的告诉大家,配置静态监听注册时,需要输入的全局数据库名(GLOBAL_DBNAME )输入什么都可以,只要保证listerner.ora中的GLOBAL_DBNAME和tnsnames.ora中的SERVICE_NAME保持一致就可以,下面通过试验看看效果:

值得主要的是GLOBAL_DBNAME = test,而此时数据库的db_name和global_name以及service_names分别如下显示:

SQL> show parameter db_name

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
db_name                              string      test1
SQL> select *from global_name;

GLOBAL_NAME
--------------------------------------------------------------------------------

test1

SQL> alter database rename global_name to abcd.yu;

数据库已更改。

SQL> select *from global_name;

GLOBAL_NAME
--------------------------------------------------------------------------------

ABCD.YU

SQL>
SQL> show parameter service_names

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
service_names                        string      TEST2, TEST3
SQL>

而我在配置listener和tnsnames时提供的test和db_name,globla_name,service_name没有任何关系,然后看看tnsping的效果:

C:\>tnsping test
已使用 TNSNAMES 适配器来解析别名
Attempting to contact (DESCRIPTION = (ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP)
(HOST = xys)(PORT = 1521))) (CONNECT_DATA = (SERVICE_NAME = test)))
OK (30 毫秒)

C:\>

9、db_unique_name

DB_UNIQUE_NAME是10g的参数,在配置dataguard环境时必须为处于dg环境中每个db设置一个唯一值,在没有DB_UNIQUE_NAME参数之前,在同一台机器上搭建dg时必须使用参数LOCK_NAME_SPACE在standby参数文件中,10g

有了参数db_unique_name,LOCK_NAME_SPACE已经被废弃!

<think>好的,我现在需要帮助用户解决如何正确配置SQLAlchemy来执行PostgreSQL数据库查询并处理异常的问题。首先,我得回顾一下用户提供的引用内容,看看里面有什么有用的信息。 用户提到了三个引用,其中引用[1]提到使用db_session.execute()执行原生SQL查询不会因为会话过早关闭导致属性错误,因为返回的是结果集而不是ORM对象。引用[2]展示了一个连接MySQL的例子,但用户现在需要的是PostgreSQL,所以需要调整连接字符串。引用[3]讨论了不同情况下选择合适的事务管理方式,比如上下文管理器、装饰器或者直接使用会话。 首先,配置数据库连接。SQLAlchemy连接PostgreSQL需要使用不同的连接字符串,格式通常是postgresql://用户:密码@主机:端口/数据库名。我需要确保用户正确替换这些参数。另外,要确认是否安装了必要的驱动,比如psycopg2,这可能需要在依赖中加上。 接下来,创建引擎和会话工厂。这里需要导入create_engine和sessionmaker,并设置连接池参数,比如pool_size和pool_recycle,避免连接超时的问题。这部分在引用[2]的MySQL例子中有类似的结构,但需要调整连接字符串。 然后,定义模型类。用户可能需要使用declarative_base()来定义ORM模型,确保表结构正确映射到PostgreSQL数据库。这部分引用[2]中的模型定义可以作为参考,但要根据用户的实际表结构进行调整。 处理查询和异常是关键部分。用户需要执行查询并处理可能的异常,比如数据库连接错误、查询语法错误、重复键等。这里需要使用try-except块来捕捉SQLAlchemy的异常和Psycopg2的特定错误。引用[1]提到使用db_session.execute()执行原生SQL,这可能更适合复杂查询,而ORM方式更适合简单的CRUD操作。需要根据用户的需求建议合适的方法。 事务管理方面,引用[3]建议根据操作复杂度选择不同的方式。对于简单操作,使用上下文管理器自动提交或回滚;复杂事务可能需要手动控制提交和回滚。需要确保在异常发生时回滚事务,避免部分提交。 此外,关闭会话和引擎是必要的,避免资源泄漏。在finally块中关闭会话,最后断开引擎连接。引用[1]提到的会话管理需要注意的地方,比如懒加载,可能在这里不适用,但关闭会话是良好的实践。 测试部分,用户可能需要验证连接是否成功,执行一个简单的查询比如SELECT 1,这可以帮助确认配置是否正确。异常处理示例中,插入数据时可能遇到重复主键,这时候捕捉IntegrityError并回滚事务,同时记录错误信息。 最后,生成相关问题需要考虑用户可能的后续疑问,比如连接池优化、异步支持、ORM与原生SQL的选择、事务隔离级别以及数据库迁移工具。这些问题扩展了用户可能需要的进一步帮助,符合用户要求的相关问题部分。 总结步骤:配置连接字符串→创建引擎和会话→定义模型→处理查询和异常→事务管理→关闭资源→测试验证。需要确保每个步骤都考虑到PostgreSQL的特定需求和异常处理。</think>### 分步解答:配置SQLAlchemy连接PostgreSQL并处理异常 #### 一、配置数据库连接 1. **安装依赖** ```bash pip install sqlalchemy psycopg2-binary # PostgreSQL驱动 ``` 2. **配置连接字符串** ```python from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker # PostgreSQL连接格式:postgresql://用户:密码@主机:端口/数据库名 DATABASE_URL = "postgresql://user:password@localhost:5432/mydb" engine = create_engine( DATABASE_URL, pool_size=5, # 连接池大小 pool_recycle=3600, # 自动回收空闲连接(秒) echo=True # 输出SQL日志(调试用) ) SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) ``` #### 二、定义模型类(ORM) ```python from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, Integer, String Base = declarative_base() class User(Base): __tablename__ = "users" id = Column(Integer, primary_key=True) name = Column(String(50), unique=True) ``` #### 三、执行查询与异常处理 ```python from sqlalchemy.exc import SQLAlchemyError, IntegrityError from psycopg2 import OperationalError def query_data(): db = SessionLocal() try: # 示例1:原生SQL查询 result = db.execute("SELECT * FROM users WHERE id = :id", {"id": 1}) print(result.fetchall()) # 示例2:ORM查询 user = db.query(User).filter(User.name == "Alice").first() print(user.id if user else "Not found") db.commit() # 显式提交事务 except IntegrityError as e: db.rollback() print(f"唯一性约束冲突: {str(e)}") except OperationalError as e: print(f"数据库连接失败: {str(e)}") except SQLAlchemyError as e: db.rollback() print(f"SQL错误: {str(e)}") finally: db.close() # 确保会话关闭 ``` #### 四、事务管理最佳实践 ```python # 方式1:使用上下文管理器(推荐简单操作) with SessionLocal() as db: try: db.add(User(name="Bob")) db.commit() except: db.rollback() raise # 方式2:装饰器模式(适合重复操作) def with_db(func): def wrapper(*args, **kwargs): db = SessionLocal() try: result = func(db, *args, **kwargs) db.commit() return result except: db.rollback() raise finally: db.close() return wrapper @with_db def create_user(db, name): db.add(User(name=name)) ``` #### 五、完整连接测试脚本 ```python def test_connection(): try: with engine.connect() as conn: conn.execute("SELECT 1") # 简单连通性测试 print("Database connected successfully") except OperationalError as e: print(f"连接失败: {str(e)}") ``` #### 六、典型异常场景处理 ```python def insert_user(name): db = SessionLocal() try: new_user = User(name=name) db.add(new_user) db.commit() return new_user.id except IntegrityError: db.rollback() print(f"用户名 {name} 已存在") except SQLAlchemyError as e: db.rollback() print(f"数据库错误: {str(e)}") finally: db.close() ``` ### 关键点说明 1. **连接池配置**:`pool_recycle`参数防止数据库服务端断开空闲连接[^2] 2. **结果集处理**:原生查询返回`ResultProxy`对象,需调用`fetchall()`/`fetchone()`[^1] 3. **事务边界**:所有写操作必须显式调用`commit()`,异常时执行`rollback()`[^3] 4. **错误类型**: - `OperationalError`: 连接/网络问题 - `IntegrityError`: 违反唯一性约束等 - `SQLAlchemyError`: 所有SQLAlchemy异常的基类
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值