背景:之前一直分不清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 是什么?