㈠ 誰能講講sql硬軟解析的區別
Oracle SQL的硬解析和軟解析
我們都知道在Oracle中每條SQL語句在執行之前都需要經過解析,這裡面又分為軟解析和硬解析。在Oracle中存在兩種類型的SQL語句,一類為DDL語句(數據定義語言),他們是從來不會共享使用的,也就是每次執行都需要進行硬解析。還有一類就是DML語句(數據操縱語言),他們會根據情況選擇要麼進行硬解析,要麼進行軟解析。
DML:INSERT,UPDATE,DELETE,SELECT
DDL:CREATE,DROP,ALTER
一.SQL解析過程
Oracle對此SQL將進行幾個步驟的處理過程:
1、語法檢查(syntaxcheck):檢查此sql的拼寫是否語法。
2、語義檢查(semanticcheck):諸如檢查sql語句中的訪問對象是否存在及該用戶是否具備相應的許可權。
3、對sql語句進行解析(prase):利用內部演算法對sql進行解析,生成解析樹(parsetree)及執行計劃(executionplan)。
4、執行sql,返回結果(executeandreturn)
二.解析過程詳解
2.1語法檢測
判斷一條SQL語句的語法是否符合SQL的規范,比如執行:
SQL>selet*fromemp;
我們就可以看出由於Select關鍵字少了一個「c」,這條語句就無法通過語法檢驗的步驟了。
2.2語義檢查
語法正確的SQL語句在解析的第二個步驟就是判斷該SQL語句所訪問的表及列是否准確?用戶是否有許可權訪問或更改相應的表或列?比如如下語句:
SQL>select*fromemp;
select*fromemp
*
ERRORatline1:
ORA-00942:tableorviewdoesnotexist
由於查詢用戶沒有可供訪問的emp對象,因此該SQL語句無法通過語義檢查。
2.3解析(Parse)
2.3.1Parse主要分為三種:
1、HardParse(硬解析)
2、SoftParse(軟解析)
3、SoftSoftParse(好像有些資料中並沒有將這個算在其中)
HardParse:就是上面提到的對提交的Sql完全重新從頭進行解析(當在SharedPool中找不到時候將會進行此操作),總共有一下5個執行步驟:
1:語法分析
2:許可權與對象檢查
3:在共享池中檢查是否有完全相同的之前完全解析好的.如果存在,直接跳過4和5,運行Sql,此時算softparse.
4:選擇執行計劃
5:產生執行計劃
註:創建解析樹、生成執行計劃對於sql的執行來說是開銷昂貴的動作,所以,應當極力避免硬解析,盡量使用軟解析。這就是在很多項目中,倡導開發設計人員對功能相同的代碼要努力保持代碼的一致性,以及要在程序中多使用綁定變數的原因。
SoftParse:就如果是在SharedPool中找到了與之完全相同的Sql解析好的結果後會跳過HardParse中的後面的兩個步驟。
SoftSoftParse:實際上是當設置了session_cursor_cache這個參數之後,Cursor被直接Cache在當前Session的PGA中的,在解析的時候只需要對其語法分析、許可權對象分析之後就可以轉到PGA中查找了,如果發現完全相同的Cursor,就可以直接去取結果了,也就就是實現了SoftSoftParse.
2.3.2解析的步驟可以分為兩個步驟:
1)驗證SQL語句是否完全一致。
在這個步驟中,Oracle將會對傳遞進來的SQL語句使用HASH函數運算得出HASH值,再與共享池中現有語句的HASH值進行比較看是否一一對應。現有資料庫中SQL語句的HASH值我們可以通過訪問vsqlarea、v$sqltext等數據字典中的HASH_VALUE列查詢得出。
如果SQL語句的HASH值一致,那麼ORACLE事實上還需要對SQL語句的語義進行再次檢測,以決定是否一致。那麼為什麼Oracle需要再次對語句文本進行檢測呢?不是SQL語句的HASH值已經對應上了?事實上就算是SQL語句的HASH值已經對應上了,並不能說明這兩條SQL語句就已經可以共享了。
例如:假如用戶SYS有自己的一張表EMP,他要執行查詢語句:select*fromemp;用戶SYSTEM也有一張EMP表,同樣要查詢select*fromemp;這樣他們兩條語句在文本上是一模一樣的,他們的HASH值也會一樣,但是由於涉及到查詢的相關表不一樣,他們事實上是無法共享的.
SQL>conn/assysdba
已連接。
SQL>showuser
USER為"SYS"
SQL>createtableemp(xint);
表已創建。
SQL>select*fromemp;
未選定行
SQL>connsystem/admin;
已連接。
SQL>createtableemp(xint);
表已創建。
SQL>select*fromemp;
未選定行
SQL>selectaddress,hash_value,executions,sql_textfromv$sqlwhereupper(sql_text)like'SELECT*FROMEMP%';
ADDRESSHASH_VALUEEXECUTIONSSQL_TEXT
--------------------------------------------------------------------------------
2769AE6417457007751select*fromemp
2769AE6417457007751select*fromemp
2rowsselected.
從結果可以看到這2個查詢的語句文本和HASH值都是一樣的,但是由於查詢的對象不同,是無法共享的,不同情況的語句還是需要硬解析的。因此在檢查共享池共同SQL語句的時候,是需要根據具體情況而定的。
可以進一步查詢v$sql_shared_cursor以得知SQL為何不能共享的原因:
SQL>selectaddress,auth_check_mismatch,translation_mismatch,optimizer_mismatch
fromv$sql_shared_cursorwhereaddressin(
selectaddressfromv$sqlwhereupper(sql_text)like'SELECT*FROMEMP%')
ADDRESSATO
-------------------------
2769AE64NNN
2769AE64YYN
TRANSLATION_MISMATCH表示SQL游標涉及到的數據對象是不同的;
AUTH_CHECK_MISMATCH表示對同樣一條SQL語句轉換是不匹配的。
optimizer_mismatch表示會話的優化器環境是不同的。
2)驗證SQL語句執行環境是否相同
比如同樣一條SQL語句,一個查詢會話加了/*+first_rows*/的HINT,另外一個用戶加/*+all_rows*/的HINT,他們就會產生不同的執行計劃,盡管他們是查詢同樣的數據。
通過如上檢查以後,如果SQL語句是一致的,那麼就會重用原有SQL語句的執行計劃和優化方案,也就是我們通常所說的軟解析。如果SQL語句沒有找到同樣的副本,那麼就需要進行硬解析了。
Oracle根據提交的SQL語句再查詢相應的數據對象是否有統計信息。如果有統計信息的話,那麼CBO將會使用這些統計信息產生所有可能的執行計劃(可能多達成千上萬個)和相應的Cost,最終選擇Cost最低的那個執行計劃。如果查詢的數據對象無統計信息,則按RBO的默認規則選擇相應的執行計劃。這個步驟也是解析中最耗費資源的,因此我們應該極力避免硬解析的產生。至此,解析的步驟已經全部完成,Oracle將會根據解析產生的執行計劃執行SQL語句和提取相應的數據。
2.4執行sql,返回結果(executeandreturn)
三.綁定變數
使用了BindVar能提高性能主要是因為這樣做可以盡量避免不必要的硬分析(HardParse)而節約了時間,同時節約了大量的CPU資源。
當一個Client提交一條Sql給Oracle後,Oracle首先會對其進行解析(Parse),然後將解析結果提交給優化器(Optimiser)來進行優化而取得Oracle認為的最優的QueryPlan,然後再按照這個最優的Plan來執行這個Sql語句(當然在這之中如果只需要軟解析的話會少部分步驟)。
但是,當Oracle接到Client提交的Sql後會首先在共享池(SharedPool)裡面去查找是否有之前已經解析好的與剛接到的這一個Sql完全相同的Sql(注意這里說的是完全相同,既要求語句上的字元級別的完全相同,又要求涉及的對象也必須完全相同)。當發現有相同的以後解析器就不再對新的Sql在此解析而直接用之前解析好的結果了。這里就節約了解析時間以及解析時候消耗的CPU資源。尤其是在OLTP中運行著的大量的短小Sql,效果就會比較明顯了。因為一條兩條Sql的時間可能不會有多少感覺,但是當量大了以後就會有比較明顯的感覺了。
㈡ c#如何使用緩存提高程序效率
緩存的技術應用應該是非廣泛的。而它的作用也是為了提高系統或者網站的執行效率。下面是四種常見的緩存技術:
一.OutputCaching
由於IIS的一些特性,默認情況下OutputCache是打開的,但是要對某些請求進行緩存,還需要開發者進行定製,而且默認情況下,Output Cache 會被緩存到硬碟上,我們可以通過修改DiskCacheable的屬性來設置其是否緩存,還可以通過Web config里配置緩存文件的大小。
<%@ OutputCacheDuration="3600" VaryByParam="state" DiskCacheable="true" %>
一般用硬碟緩存是考慮到頁面送顯的數據比較大,相對內存緩存來說,它的容量大,但是訪問速度慢點,如果把周期設太短,使用硬碟緩存的效率就不大好。對於Output Cache的定製,有兩種方法,一種是基於底層的API技術,一種是基於高層的@OutputCaching:
1.基於高層的@OutputCaching
A.由參數改變緩存內容:有些時候我們需要根據用戶的請求來生成頁面,但是用戶的請求只有有限的幾種組合,這個時候就可以根據用戶請求來生成幾種緩存頁面,來進行緩存。
<%@ OutputCache Duration ="60" VaryByParam = "state" %>
<asp:SqlDataSourceID="SqlDataSource1" runat="server">
<SelectParameters>
<asp:QueryStringParameter Name="state"QueryStringField="state" DefaultValue="CA" />
</SelectParameters>
</asp:SqlDataSource>
B.回調緩存:可以針對每個請求在頁面中插入動態的部分,以彌補單獨使用靜態緩存的不足:
動態的部分用Substitution控制項,Substitution控制項是一個容器
<asp:SubstitutionID="Substitution1" runat="server" MethodName =""/>
MethodName 裡面放入要調用的方法內容。
2. 使用API定製緩存:
通過設置System.Web.HttpCachePolicy屬性來進行配置
<%@ OutputCache Duration="60"VaryByParam="none" %>
就可以寫成
Response.Cache.SetExpires(DateTime.Now.AddSeconds(60));
Response.Cache.SetCacheability(HttpCacheability.Public);
二.FragmentCaching
作為Output的緩存的附加功能,還提供一種緩存技術,專門用於緩存用戶控制項。在用戶控制項中設置:
<%@ OutputCache Duration="60"VaryByParam="none" %>
但在引用用戶控制項的頁面不設置緩存。這樣的話,頁面中除了用戶控制項是靜態的,其他都是動態的。
緩存用戶空間同樣還可以使用控制項作為參數來源。通過指定控制項作為緩存控制項的數據來源,可以達到緩存控制項數據的目的,和上面一樣。
三.DataCaching
Asp.net提供了一種非常快捷的方法進行資料庫緩存,用戶可以非常簡單方便的對頁面變數進行緩存。並以此提高程序效率。一個頁面變數的緩存生命周期與應用程序的緩存生命周期相同
實現是把數據放在Cache中,如:
source = new DataView(ds);
Cache("MyCache") = source;
MyCache這個變數其實就是一個XML文件。
四.SQL Caching
通過配置資料庫連接池,只有當資料庫數據被改變的時候,緩存才會改變。
開個DOS窗口:
C:\>dir aspnet_regsql.exe/s ——這個文件是專門注冊SQL連接池的,它對SQLSever 7.0以上都有專門的支持,我們通過寫一些專門的語句來配置這個注冊連接池,可以把連接池和本地的應用程序(Asp.net伺服器,即IIS)做一個連接。連接池只能監視有限的幾個庫,不然連接池的負載太大。使用SQL Caching:
先注冊,如: aspnet_regsql.exe-S".\SQLExpress"-E-d"pubs"-ed
aspnet_regsql.exe-S".\SQLExpress"-E-d"pubs"-et-t"authors"
其中:- S".\SQLExpress" 表示要使用的SQL Server實例為".\SQLExpress"。-E 表示使用當前windows憑證進行身份驗證。-d"pubs"表示用於應用程序服務的資料庫名稱叫"pubs"。-ed表示為SQL 緩存依賴項啟用資料庫。-et 表示為SQL 緩存依賴項啟用表。-t"authors"表的名稱為"authors"。
然後頁面上:
<%@ OutputCacheDuration="99999999" VaryByParam="none"SqlDependency="Pubs.Authors" %>
就OK了。
㈢ ORACLE 資料庫緩沖區緩存與共享池SQL查詢和PL/SQL函數結果緩存的區別
1、資料庫緩沖區DB_BUFFER只能緩存訪問過的BLOCK,部分解決了物理讀的問題,查詢仍然需要大量的邏輯讀。
2、SQL緩存結果集/*+RESULT_CACHE*/它緩存的是查詢的結果。不在需要大量的邏輯讀,不在需要任何的復雜計算,而是直接將已經緩存的結果返回。
3、對於採用了RESULT_CACHE的函數,Oracle自動將函數的返回結果緩存,下次執行的時候,不會實際執行函數,而是直接返回結果。由於緩存的結果存儲在SGA中,因此所有的會話可以共享這個結果。
㈣ C語言,OCI多線程建立session的問題,需要一個多線程連接的示例代碼
。。
你子線程式控制制同步了么? 斷錯誤一般是內存操作出錯 和oci 或者pthread的關系不大!
void* OracleProcess(GPS_DATA GpsRec) // 資料庫數據處理
{
interval = 0;
struct HashItem* pHash;
pHash = inithashtable(MAX_REC<<2);
char sql[384] = {0};
char temp[256] = {0};
char tName[10] = {0}; // 表名字
int i,k;
int j = TotalRec >> RATE;
double distance;
for(i=0; i < j; i++)
{
sprintf(temp,"%s%f%f%f%d",gps_last[i].tid,gps_last[i].lon,gps_last[i].lat,gps_last[i].speed,gps_last[i].udate);
InsertHash(temp, pHash, MAX_REC<<2); // 插入最後GPS信息到hash
memset(temp,0x00,256);
}
for(i = 0; i < TotalRec; i++)
{
for(k=0; k<j; k++) // 查詢車機是否在冊
if(strcmp(GpsRec[i].tid,tid[k]) == 0)
break;
if(k < j)
{
if(GpsRec[i].udate != 0.00)
{
distance = InfoUpdate(GpsRec,i); // 最新GPS數據更新
sprintf(temp,"%s%f%f%f%d",GpsRec[i].tid,GpsRec[i].lon,GpsRec[i].lat,GpsRec[i].speed,GpsRec[i].udate);
if(GetHashTablePos(temp, pHash, MAX_REC<<2) == -1) // 查找hash是否存在
{
if (distance > 0.0001)
{
sprintf(tName,"GPS_%d_Y",tf[k]);
InsertHash(temp, pHash, MAX_REC<<2); // 插入
sprintf(sql,"insert into %s (id,tm_id,lon,lat, speed, utc_time, udate,mileage,DIRECTION,DISTANCE) values (seq_gps.nextVal,'%s','%f','%f','%f','%d','%d','%f','%d','%f','%d')",
tName,GpsRec[i].tid,GpsRec[i].lon,GpsRec[i].lat,GpsRec[i].speed,GpsRec[i].utime,GpsRec[i].udate,GpsRec[i].mileage,GpsRec[i].dir,distance,interval);
printf("%s\n",sql);
oci_excu(oracle_env,(text *)sql,0); // 插入數據
memset(tName,0x00,10);
}
}
memset(sql,0x00,384);
memset(temp,0x00,256);
}
}
}
memset(GpsRec,0x00,sizeof(GpsRec));
free(pHash);
pthread_exit(NULL);
}
void TcpProcess(int tfd) // 處理TCP連接上的事務
{
struct timeval ntime;
int index = 0,times,ret;
int rlen = 0,rflag = 0;
char recvbuf[513] = {0};
bzero(recvbuf,513);
while(1)
{
ret = rlen = read(tfd,recvbuf,512);
if(rlen <= 0)
break;
if((rlen%32) == 0) // 32長度為標准TCP信息
{
times = 0;
ret >>= 5;
while(ret--)
{
if(tflag[tfd] == tfd) // 已經存在的socket
{
LOVENIX *info = (LOVENIX *)malloc(sizeof(LOVENIX));
memset(info,0x00,sizeof(LOVENIX));
if(recvbuf[times] == 0x58 || recvbuf[times] == 0x59)
ProtocolAnalysisLovenixTcp(&recvbuf[times],info);
else if(recvbuf[times] == 0x24)
ProtocolAnalysisLovenixUdp(&recvbuf[times],info);
sprintf(info->tid,"%s",seq[tfd]); // 合成車輛ID
DataProcess(info); // 處理GPS數據
free(info);
gettimeofday(&ntime, NULL);
cntime[tfd] = ntime.tv_sec; // 更新時間
times += 32;
}
}
}
else if(rlen > 32)
{
if(!rflag)
{
if((index = RegLovenix(tfd,recvbuf)) > -1)
{
sprintf(seq[tfd],"%s",tid[index]); // 將對應的socket設備ID保存
gettimeofday(&ntime, NULL);
sfd[tfd] = tfd;
cntime[tfd] = ntime.tv_sec;
tflag[tfd] = tfd;
rflag = 1;
}
}
}
if(rlen < 512); // 已經讀完
break;
memset(recvbuf,0x00,rlen);
}
}
void *TcpServer(void *arg)
{
int port = (unsigned int) arg;
int efd,i;
struct timeval ntime;
int listener, nfds, n, listen_opt = 1, lisnum;
struct sockaddr_in my_addr, their_addr;
socklen_t len = sizeof(their_addr);
lisnum = MAXLISTEN;
for(i=0; i<MAX_REC; i++)
{
sfd[i] = 0;
tflag[i] = 0;
}
if ((listener = socket(PF_INET, SOCK_STREAM, 0)) == -1) // 開啟 socket 監聽
{
lprintf(lfd, FATAL, "TCP Socket error!\n");
exit(1);
}
else
lprintf(lfd, INFO, "TCP socket creat susscess!\n");
setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, (void *) &listen_opt,(int) sizeof(listen_opt)); // 設置埠多重邦定
setnonblocking(listener);
bzero(&my_addr, sizeof(my_addr));
my_addr.sin_family = PF_INET;
my_addr.sin_port = htons(port);
my_addr.sin_addr.s_addr = INADDR_ANY;
if (bind(listener, (struct sockaddr *) &my_addr, sizeof(struct sockaddr)) == -1)
{
lprintf(lfd, FATAL, "TCP bind error!\n");
exit(1);
}
else
lprintf(lfd, INFO, "TCP bind susscess!\n");
if (listen(listener, lisnum) == -1)
{
lprintf(lfd, FATAL, "TCP listen error!\n");
exit(1);
}
else
lprintf(lfd, INFO, "TCP listen susscess!\n");
kdpfd = epoll_create(MAXEPOLLSIZE); // 創建 epoll句柄,把監聽socket加入到epoll集合里
ev.events = EPOLLIN | EPOLLET; // 注冊epoll 事件
ev.data.fd = listener;
if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, listener, &ev) < 0)
lprintf(lfd, FATAL, "EPOLL_CTL_ADD error!\n");
while (1)
{
sem_wait(&sem_tcp); // 等待 sem_TCP
sem_wait(&sem_tp); // 將tp值減一
nfds = epoll_wait(kdpfd, events, MAXEPOLLSIZE, 1); // 等待有事件發生
if (nfds == -1)
lprintf(lfd, FATAL,"EPOLL_WAIT error!\n");
for (n = 0; n < nfds; ++n) // 處理epoll所有事件
{
if (events[n].data.fd == listener) // 如果是連接事件
{
if ((efd = accept(listener, (struct sockaddr *) &their_addr,&len)) < 0)
{
lprintf(lfd, FATAL, "accept error!\n");
continue;
}
else
lprintf(lfd, INFO, "Client from :%s\tSocket ID:%d\n", inet_ntoa(their_addr.sin_addr) ,efd);
setnonblocking(efd); // 設置新連接為非阻塞模式
ev.events = EPOLLIN | EPOLLET; // 注冊新連接
ev.data.fd = efd;
if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, efd, &ev) < 0) // 將新連接加入EPOLL的監聽隊列
lprintf(lfd, FATAL, "EPOLL_CTL_ADD error!\n");
else
{
gettimeofday(&ntime, NULL);
cntime[efd] = ntime.tv_sec;
sfd[efd] = efd;
}
}
else if (events[n].events & EPOLLIN)
tpool_add_work(pool, TcpProcess, (void*)events[n].data.fd); // 讀取分析TCP信息
else
{
close(events[n].data.fd);
epoll_ctl(kdpfd, EPOLL_CTL_DEL, events[n].data.fd, &ev);
}
}
sem_post(&sem_cm);
sem_post(&sem_udp);
}
close(listener);
}
int DataProcess(LOVENIX *info) // 處理GPS數據
{
if(sflag == 0 && (CacheRec != TotalRec)) // 緩存1可用且沒有滿
{
gps_cache[CacheRec].lat = info->lat;
gps_cache[CacheRec].mileage = info->mileage;
gps_cache[CacheRec].lon = info->lon;
gps_cache[CacheRec].speed = atod(info->speed, strlen(info->speed))*0.514444444*3.6;
gps_cache[CacheRec].udate = atoi(info->udate);
gps_cache[CacheRec].utime = atoi(info->utime);
gps_cache[CacheRec].dir = atoi(info->dir);
sprintf(gps_cache[CacheRec].tid ,"%s",info->tid);
CacheRec++;
// printf("CacheRec %d\tTotalRec %d \t sflag:%d\n",CacheRec,TotalRec,sflag);
if(CacheRec == TotalRec)
{
sflag = 1;
pthread_attr_init(&attr); // 初始化屬性值,均設為默認值
pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); // 設置線程為分離屬性
if (pthread_create(&thread, &attr,(void*) OracleProcess,(void*)gps_cache)) // 創建數據處理線程
lprintf(lfd, FATAL, "oracle pthread_creat error!\n");
CacheRec = 0;
}
}
else if(sflag == 1 && (Cache1Rec != TotalRec)) // 緩存2可用且沒有滿
{
gps_cache1[Cache1Rec].mileage = info->mileage;
gps_cache1[Cache1Rec].lat = info->lat;
gps_cache1[Cache1Rec].lon = info->lon;
gps_cache1[Cache1Rec].speed = atod(info->speed, strlen(info->speed))*0.514444444*3.6;
gps_cache1[Cache1Rec].udate = atoi(info->udate);
gps_cache1[Cache1Rec].utime = atoi(info->utime);
gps_cache1[Cache1Rec].dir = atoi(info->dir);
sprintf(gps_cache1[Cache1Rec].tid ,"%s",info->tid);
Cache1Rec++;
if(Cache1Rec == TotalRec)
{
sflag = 0;
pthread_attr_init(&attr); // 初始化屬性值,均設為默認值
pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); // 設置線程為分離屬性
if (pthread_create(&thread, &attr,(void*) OracleProcess,(void*)gps_cache1)) // 創建數據處理線程
lprintf(lfd, FATAL, "oracle pthread_creat error!\n");
Cache1Rec = 0;
}
}
else
{
lprintf(lfd, FATAL, "No cache to use!\n");
return (0);
}
return (1);
}