背景:之前一直分不清plsql和程序中配置文件url之间的连接,想当然的认为service_name 和jdburl后面的实例相对应,直到出错的这一天,通过这篇博客,彻底扫除了盲点。
1 问题
1.1 问题描述
plsql的tnsnames.org中的数据源是这么配置的
应用程序中的配置文件是这么配置的:
启动程序,报错:
Caused by: oracle.net.ns.NetException: Listener refused the connection with the following error:
ORA-12505, TNS:listener does not currently know of SID given in connect descriptor
这里我只是单纯的将 “”:“” 后面的名称和service_name 对应上去,完全是想当然的做法。
1.2 问题解决
这个问题同事首先给我的解决方案是,将url后面的sjjdb 改为sjjdb1,这个不是数据库的实例名吗,我虽然不相信,但是改完后确实能够正常启动程序,百思不得其解啊。
于是,我在网上找到了查看数据库sid_name 的方法
例如:jdbc:oracle:thin:@192.168.0.58:1521:ORCL
最后的ORCL就是sid_name
附:
察看数据库中当前的sid的方法:
SQL> select INSTANCE_NAME from v$instance;
INSTANCE_NAME
----------------
hasl
然后,我就在plsql中执行了一遍命令,发现:
我擦,我的sid_name 居然是sjjdb2,我去修改程序中的配置文件为connection.url=jdbc:oracle:thin:@168.33.131.19:1521:sjjdb2
非常6,程序跑起来了。这时候我在想,sid_name和service_name之间到底是个什么关系呢?
于是就有了第二章的终极解决方案,茅塞顿开的感觉!
2 彻底明白sid_name和service_name之间的区别
2.1 顿悟
ps:RAC,全称real application clusters,译为“实时应用集群”, 是Oracle新版数据库中采用的一项新技术,是高可用性的一种,也是Oracle数据库支持网格计算环境的核心技术。
先来讲一个小故事,2015年6月份,有个客户迁移了数据库,由单实例数据库变成了RAC。Java应用程序出现了无法连接数据库的情况,但是PL/SQL能连接上数据库(我就是的)。
由于项目比较庞大,虽然在半夜切换的,但是也不能接受长时间的业务停顿。当时,我对Oracle技术也只是略知皮毛。在咨询过公司研发后,他们给我的建议是:
参考PL/SQL的连接参数,将spring中jdbc连接的url由jdbc:oracle:thin:@10.2.0.2:1521:orcl改为jdbc:oracle:thin:@(DESCRIPTION =(ADDRESS_LIST =(ADDRESS =(PROTOCOL = TCP)(HOST = 10.2.0.2)(PORT = 1521)))(CONNECT_DATA =(SERVICE_NAME = orcl))),结果问题解决了,当时我挺佩服公司研发的。
现在看来,这个并不是最佳的解决方案,下面通过讲解SID和SERVICE_NAME的区别,我将给出更佳的解决方案。
在讲解SID和SERVICE_NAME之前,先说一下实例。实例是操作系统中访问数据库所需要的一系列的进程和内存的集合。即使没有任何数据文件,实例也可以启动。但是要想访问数据库,必须把数据库文件加载进实例中。
实例和数据库的区别可以简单概括为:实例是临时的,它只在相关的进程和内存集合存在时存在,而数据库是永久的,只要文件存在它就存在。一个实例只能对应一个数据库,但是一个数据库可以由多个实例对应(如RAC)。RAC就是多个实例同时打开一个数据库文件的系统,在结构上是多台机器,每台机器运行一个实例,每个实例都打开同一个数据库 (这个是用磁盘共享技术实现的),这些实例之间需要同步高速缓存,这样保证多个实例是完全一致的,不会相互冲突乃至覆盖。
SID即INSTANCE_NAME是用来唯一标示实例的。SERVICE_NAME是oracle8i新引进的,8i之前,一个数据库只能由一个实例对应,但是随着高性能的需求,并行技术的使用,一个数据库可以由多个实例对应了,比较典型的应用如RAC。为了充分利用所有实例,并且令客户端连接配置简单,ORACLE提出了SERVICE_NAME的概念。该参数直接对应数据库,而不是某个实例。
了解了SID和SERVICE_NAME之后,我突然觉得前面故事中使用的解决方案并不是最佳的解决方案,因为SERVICE_NAME的出现就是为了应对并发技术,简化客户端连接配置。通过SERVICE_NAME应该能找到更好的解决方案。通过查找JDBC帮助得知JDBC连接ORACLE的方法由三种:
格式一:jdbc:oracle:thin:@//<host>:<port>/<service_name>
格式二:jdbc:oracle:thin:@<host>:<port>:<SID>
格式三:jdbc:oracle:thin:@<TNSName>
不难看出,故事中使用RAC之前,JDBC是使用格式二连接的,使用RAC后实例增多了,SID已经不唯一,格式二已经无法完全利用所有资源。研发参考PL/SQL的连接方法刚好碰巧使用了格式三。(这不是直接将数据源搞上去吗,low啊)因为java应用服务器跟数据库服务器是分离的,应用服务器上没有oracle的服务端以及客户端。虽然解决方案中没有使用TNSName,但是使用了TNSName的连接描述,效果是一样的。既然JDBC有三种连接方案,我们不妨再看一下第一种方案。
再加上前面的讲解内容,我想大家已经都知道故事中的问题该怎么修改了,只要改成jdbc:oracle:thin:@//10.2.0.2:1521/orcl就可以了,而且这种格式也支持单实例数据库。这也是ORACLE在8i之后增加SERVICE_NAME的初衷。
通过这个故事,我们可以看到,只有充分了解了ORACLE的知识,才能更好的使用ORACLE技术。
so,最后的的问题解决方案就是通过加 /解决的:
connection.url=jdbc:oracle:thin:@168.33.131.19:1521/sjjdb
2.2 引申
oracle中的service_name 是什么?