2017年7月8日 星期六

表錯情


約莫半年前,我開始做些網通業在AWS Markteplace 營銷模式的相關研究。面這張心智圖就是當時用了一個晚上紀錄學習的點滴的工具。完成的當下心情是很滿足的,感覺又進了一大步,心中滿是自信在隔天的會議中我應該可以有不小的貢獻意見。

沒想到事與願違。

會議開始後,大家討論的方向慢慢地偏離原本設定的主軸。雖然我想藉由研究的結果幫大家「導回」正途,卻只見大家一臉疑惑的表情,顯然聽不太懂我想表達的事情。那時候我才赫然發現兩件事:第一,他們聽不懂我的研究,因為我用了他們不懂的技術語言。第二,他們還不懂我已經熟稔的AWS Marketplace,所以我表達的內容就算再完整,若不是以他們關切的方式去整理與組織結論,最後也只是落得鴨子聽雷、有聽沒有懂的難堪。

所以,搞清楚聽眾的程度與他們關切的議題,再來組織腦海中所描繪的圖像,才是最有效率的開會方式。

會後心情雖有些落寞,但獲得比失去(發言)的多,就結果而言是相當正面的。

替自己加油囉!

2017年6月27日 星期二

被飆習慣了

從Architect 轉職做PM也已經一年多了。

過程辛酸艱苦不說,自己原有的專長還被以PM的角色來說,視為毫無價值。

只能說自己選的路,只能吞下去了。


今天做完系統升級後,被N先生臭罵了一頓,怪我怎麼沒把升級時間改為週末來做,難道
以我的經歷還不知道這樣會影響到服務嗎?

我沈默不語。

早在兩年前就做過相同的事,過程中雖有其他介接服務跟著無法執行正常營運,但重點都不是一般上班日作維運的問題。現在怪我,摸摸鼻子也只能認了。

維護通知早在一個月前就開始寄出,所有配合單位也沒異議。現在突然飆我,好像我犯了天條般的白痴一樣 ;但真正的問題點在於維運廠商不肯配合週末加班,才演變成這樣的局面。要說有錯,就是我沒有把N先生搬出來罵廠商,才讓場面變得千錯萬錯都是我的錯。

能怎麼辦?

吞下去,努力學,我已經可以看見我有能力掌控一切的未來了。等羽翼豐厚後,再來決定去留都還來得及。因人去職實在不是理智的行為。  :)

2015年8月5日 星期三

我又搬回來了

哎,上禮拜才知道IT Home的部落格服務已經謝謝再聯絡。所有的文章突然就這樣不見了。想想算了,雖然有不少文章在那邊,但人家已經關站了,怪自己沒注意到就是。

所以,我又搬回來了。 Orz...

2011年4月10日 星期日

紫糯米搬家了

原先開這個站的用意是要寫些關於資料庫的心得,與另外一個在ITHome純分享程式開發類別的網誌有所區隔。但最後方便管理起見,本網誌將即日起搬到 David' Blog,請舊雨新知移動尊駕到新網誌,小弟會繼續努力耕耘的...

利用IGNORE_DUP_KEY過濾重複的資料

要從SQL SERVER中某table過濾出重複的資料後,塞入另外一個table裡的方法很多。這幾天工作的關係,發現其實也可利用 ignore_dup_key 這個 relation_index_option來取巧。下面我建立個簡單的範例說明:


--------------------------------------------------------------------------------

--建立uniqindex , 並指定ignore_dup_key
--------------------------------------------------------------------------------

--1. 建立target table,儲存無重複的資料
if exists (select name from sys.tables t where t.name = 'category')
 drop table category
go
--2. 刪除既有的index
create table category(cat_desc varchar(50), cat_code varchar(10))
if exists(select name from sys.indexes i where i.name = 'idx_cat')
 drop index idx_cat on cayrgory
go

--3. 建立unique index,並指定ignore_dup_key
create unique index idx_cat on category(cat_desc) with ignore_dup_key
go

--4. 建立1,000測試資料,其中5,000筆重複
declare @tb1 table(c1 varchar(50), c2 varchar(10))
declare @i int
declare @loop_start_date datetime, @loop_end_date datetime
declare @uniq_start_date datetime, @uniq_end_date datetime

set @i = 1
set @loop_start_date = GETDATE() --設定迴圈起始時間

while @i <= 10000
begin
 if @i % 2 = 0
  insert @tb1 values('test' + cast((@i-1) as varchar(50)), cast((@i-1) as varchar(10)))
 else
  insert @tb1 values('test' +cast(@i as varchar(50)), cast(@i as varchar(10)))


 set @i = @i + 1
end
set @loop_end_date = GETDATE() --設定迴圈結束時間

set @uniq_start_date = GETDATE() --設定insert 至target table的起始時間
insert category(cat_desc, cat_code) select * from @tb1
set @uniq_end_date = GETDATE() --設定insert 至target table的結束時間

select 'inserting loop takes...'
select DATEDIFF(SECOND, @loop_start_date, @loop_end_date)
select 'inserting into uniq index with ignore_dup_key takes...'
select DATEDIFF(SECOND, @uniq_start_date, @uniq_end_date)

select * from @tb1 order by c1
select * from category order by cat_desc
go


一開始我先建立一個target table,以儲存待會測試資料中已經過濾出來的無重複資料。在target table上我們建立一個unique index,並以WITH  IGNORE_DUP_KEY指定relational_index_option的值;接著宣告一個 table type的暫存資料表,並以迴圈塞入10,000筆資料,其中有5,000筆重複。最後我們直接將測試資料表insert 到target table中。這裡就是重點了。以往要是在建立索引時沒有加上 with ignore_dup_key這個選項,則insert到重複的資料時,就會得到一個錯誤的訊息,整個insert的transaction就會終止並且rollback:

訊息 2601,層級 14,狀態 1,行 8
無法以唯一索引 'idx_cat' 在物件 'dbo.category' 中插入重複的索引鍵資料列。

為了能略過該筆重複的資料所造成的錯誤並使insert動的繼續進行,create unique index時指定 with ignore_dup_key 即可辦到。以後如果懶得寫一堆過濾語法,或許可以偷個懶用用這個方法。

2011年3月23日 星期三

select 是否會使用 undo segment?

根據 Ask Tom的文章,我做了些實驗,得到的結論如文章中所描述:
  • 單純的select 並不會使用到undo segment;
  • 對於一個有一筆紀錄以上的Table來說,只要select statement 尾巴多加了 for update,就會用到undo segment。
我先建立一個測試表格T(char(1)),並insert了一筆資料,隨後進行以下的測試:

SQL> select used_ublk from v$transaction;                                                              

沒有任何資料列被選取

SQL> truncate table t;

表格被截斷.

SQL> select used_ublk from v$transaction;

沒有任何資料列被選取

SQL> insert into t values('1');

已建立 1 個資料列.

SQL> commit;

確認完成.

SQL> select used_ublk from v$transaction;

沒有任何資料列被選取

SQL> select * from t for update;

T1
--
1

SQL> select used_ublk from v$transaction;

 USED_UBLK
----------
         1

SQL> commit;

確認完成.

SQL> select used_ublk from v$transaction;

沒有任何資料列被選取

SQL>
請注意以上紅色部分,不管你有沒有異動T Table內的資料,只要一旦下了 for update,undo space就會備準備出來儲存現有的資料列版本,而且更恐怖的是Table Lock也同時跟著發生。一旦有其它 transaction 要進行異動,那真的是死在那邊等你解了。所以千萬小心、在意 for update用完要趕快commit以釋放undo space跟table lock,否則大禍臨頭(DBA找出兇手的時候)可就糗了...

2011年3月17日 星期四

SQL Writer 自發性的啟動?

SQL Writer 預設和SQL Server一起安裝,主要目的在於和VSS互動,讓備份軟體可在SQL Server運行時,執行備份或復原鎖定的data files。

這個服務預設是Disabled的,但我試著把SQL 2005 sp2升級到sp4,卻發現升級精靈檢查鎖定檔案時,SQL Writer服務卻出現了!奇怪,難道我又誤入了什麼嗎? 查了一下文章,才發現SQL SQL 2005 上了 sp2後就會自動的啟動這項服務。原文如下:

The SQL Writer Service provides added functionality for backup and restore of SQL Server 2005 through the Volume Shadow Copy Service framework.
This SQL Writer Service is automatically installed but not enabled by default. It must be explicitly enabled to run on the server machine and must be running at the time that the Volume Shadow Copy Service (VSS) application requests a backup or restore. Use the Microsoft Windows Services applet to configure the service. The SQL Writer Service installs on all operating systems but is only needed on server systems. For Microsoft Windows XP, use the MSDE Writer.

SQL Server 2005 Service Pack 2 configures SQL Writer to start automatically.

真是差點以為自己見鬼了...(筆記)

2011年3月8日 星期二

關於log file sync兩三事

今天Top Activity突然發現有趣的wait class:commit。看起來像下面橘色的那一小塊(請點圖放大):
由於平常沒什麼特別注意,今日特別興起繼續往下追蹤,發現原來是 「log file sync」的wait event:

查了一下文章,原來是app可能大量用了batch transaction的寫法,但是log buffer或者redo log file所在的硬碟效率有待加強的時候,就容易發生。嗯,這個要開始注意了..

2011年2月23日 星期三

取得Oracle 物件的DDL

因工作關係,寫了取得Table、Procedure、Function、Trigger等物件的DDL的函式,並搭配C#使用;我把文章寫在這裡,分享囉。

2011年1月26日 星期三

替新建立的Control File建立Backup Repository

要Restore一個一模一樣的測試環境,備份檔來源紀錄會有兩個地方,一個來自於Source DB的Control file,另外一個則來自於RMAN Repository。假設,你想還原的是一個已經超過Retention Policy的備份檔,而且你想要用新建立的Control File來建立備份記錄,那該怎麼辦呢? 方法比想像中的簡單許多。只要利用RMAN的Catalog Start With指令,將備份檔整理到Control file就搞定了。舉例:

C:\> sqplus / as sysdba
SQL> startup mount;
SQL> exit;
C:\> rman target /
RMAN> catalog start with 'd:\oracle\orabak\';                                            

這時候Control file就會有放在d:\oracle\orabak\的備份檔記錄了。

2011年1月20日 星期四

有趣的行話:Bit bucket

Bit bucket 在 Wiki 裡頭解釋的挺有趣的,它所舉的生活化例子,相當於我們用中文說"資料莫名奇妙的消失在「黑洞」或「百慕達三角洲」"的味道。

這名詞記下來給大家笑一下。

2011年1月19日 星期三

ORA-01019 on Windows 7

同事今天安裝Windows 7給User使用,其中有個應用程式在安裝完成後,執行時卻遇到了ORA-01019錯誤,由錯誤訊息看起來無法判斷是哪方面的問題造成的,不過經驗上判斷這應該跟權限問題比較有關。我試著先用tnsping是沒問題的,但如果用sqlplus 登入卻會出現以下錯誤:

SP2-1503: Unable to initialize Oracle call interface
SP2-0152: ORACLE may not be functioning properly


很有趣但又沒有用的的訊息。既然如此,換個方式來google一下SP2-1503呢?撲!答案找到了,原來是「控制台\系統管理工具\本機安全性設定\本機原則\使用者權限指派\建立通用物件(Create Global Object)」加入有需要權限的使用者即可。譬如說你希望所有網域使用者都能正常的登入Oracle,就將Domain Users群組加入就搞定。


註:XP稱Create Global Object為「建立全域物件」,個人覺得真是自找麻煩的變更

2011年1月11日 星期二

回復誤蓋的control file

實作restore的時候,不小心把source database的control file覆蓋到別的測試區的control file,等回神定眼一看,大錯已經造成。不幸中的大幸是測試區有做 3 個 multiplex control file,這次蓋到的只有兩個。立刻,心神慌亂的shutdown測試區instance(windows 平台直接關掉service),接著馬上砍掉錯誤的兩個control file,把測試區那碩果僅存的control以另存新檔的方式做出兩個control file,並命名成正確的control file name(ex, copy control03.ctl control01.ctl)。最後,restart database即成功恢復運作。

好在搞掛的是測試區,換作正式區,我可無法解釋這5分鐘的downtime怎麼來的啊...@@

2010年11月23日 星期二

僅變更來源表格中相符欄位的紀錄:UPDATE VS. MERGE INTO

從 B Table 將資料更新到 A Table的方式,大都會這樣寫:

UPDATE A 
   SET A.CODE = (SELECT DISTINCT (CODE)
                     FROM B
                    WHERE A.CODE_1 = B.CODE_1
                      AND A.CODE_2 = B.B_CODE_2)

但有個缺點是如果B與A若有沒有相符的紀錄,即在A表格中不符合 A.A_CODE = B.A_CODE & A.B_CODE = B.B_CODE 條件的紀錄,就會被更新成NULL;為了避免這種情況,我們可以加上NVL()函式跳過不相符的紀錄被更新:


UPDATE A 
   SET A.CODE = NVL((SELECT DISTINCT (CODE)
                     FROM B
                    WHERE A.CODE_1 = B.CODE_1
                      AND A.CODE_2 = B.CODE_2), A.CODE)

Oracle 在 9i 之後的版本新增一個 MERGE 的功能,在語意以及速度方面上更勝一籌:

  MERGE INTO A
  USING  B
  ON (A.CODE_1 = B.CODE_1 AND A.CODE_2 = B.CODE_2)
  WHEN MATCHED THEN
    UPDATE SET A.CODE = B.CODE;

語意上就更接近口語化,程式碼也容易變得簡潔。且不止於此,既然有 MATCHED,也就會有 NOT MATCHED,多增加了分支判斷的功能。假設我們想把不符條件的資料記錄到另外一個Table中,就可這樣寫:

MERGE INTO A
  USING  B
  ON (A.CODE_1 = B.CODE_1 AND A.CODE_2 = B.CODE_2)
  WHEN MATCHED THEN
    UPDATE SET A.CODE = B.CODE;
  WHEN NOT MATCHED THEN
INSERT INTO FAIL_REC VALUES (A.CODE_1, A,CODE_2);

分享囉~

2010年11月16日 星期二

Oracle複寫到SQL 2005時,卡在DELETE ARTICLE的終極解法

前篇文章提到當利用SQL 2005複寫機制,將Oracle複寫到SQL端時,一段時間後就會卡死在DELTE FROM Articlexxx的SQL Statement,除了速度緩慢之外,也連帶的影響其他工作效能。最後跟同事試了幾種方式,經由同事的實驗確認了以下方式會取得最佳狀態:
  • 建立好複寫機制後,替每個Table做好統計值分析;再確認執行計畫是否有使用Index即可(使用Range Scan而非Full Table Access)。這個步驟有點麻煩;我們的問題是當複寫的時候,DELETE FROM Articlexxx 會因為執行計畫不佳而Hang在哪裏。但每個Article都可能會有這樣的問題,所以我們以Oracle端部署時所用的複寫帳號登入後(假設是repadm),透過以下SQL Statement準備各Article的DELETE Article SQL Statement:

     SELECT 'DELETE FROM ' || TABLE_NAME  ||
           
    '
     WHERE EXISTS ' ||
              (SELECT p.POLL_POLLID ' ||

                 'FROM HREPL_POLL p ' || 
               'WHERE CHARTOROWID(l.ROWID) = p.Poll_ROWID ' || 

               '       AND p.Poll_PollID = :Pollid)'
       FROM USER_TABLES
     WHERE TABLE_NAME LIKE 'HREPL_ARTICLE%'


    之後,我們先塞點假資料給HREPL_POLL以及各個Article以模擬大量資料(剛開始沒資料,執行計畫就會用Full Table Access的方式規劃),然後分別開始做統計分析;這時候取得統計值就會接近實際狀況。
  • 準備好統計資訊後,利用 outline 的方式鎖住上述每一個DELETE Article SQL Statement的執行計畫,避免大量DML後Oracle對該DELETE Statement的執行計畫跑掉。
  • 以後每建立一個新的發行項目(Article),就要執行上述步驟(修改一下SQL Statement取得該新發行項目的Delete Articlexxx SQL Statement)以取得並鎖定最佳的執行計畫。
目前跑了一週,已經沒再發生Hang住的問題,這方法提供參考囉。

2010年10月26日 星期二

Workaround for hanging SQL Statement "DELETE Article" in replication

在處理Oracle複寫到SQL Server的問題之中,最令人難解跟抓狂的,莫過於LogReader在Oracle端執行類似以下的語法時,卻莫名的耗費大量時間,導致複寫機制整個停住:



DELETE FROM HREPL_ARTICLE326LOG_1 l
 WHERE EXISTS (SELECT p.POLL_POLLID
          FROM HREPL_POLL p
         WHERE CHARTOROWID(l.ROWID) = p.Poll_ROWID
           AND p.Poll_PollID = :Pollid)


這個問題目前尚未有答案,只知道一旦這個罕見的情況發生,整個複寫機制形同宣告死亡;如果這種情況在六日發生,禮拜一你來公司才發現的話,最好也檢查一下Oracle的UNDO Tablespace,因為它將產生大量的UNDO資料,很有機會將UNDO Tablespace搞爆;前兩次發生時我只能癡癡的拆掉重建 replication的機制(觀念沿襲於windows不穩定就FORMAT掉 XD),但今天總算試出一個workaround,解法如下:

  1. Kill掉Oracle上的Logread.exe session
  2. 找出所有Oracle端的article
  3. 刪除所有article的資料(強烈建議用truncate table)
  4. 打開複寫監控器觀察
過個一兩分鐘,複寫機制應該就可以復活了。


後記:

好事果然不能說出來,快樂指持續了一個下午就結束,隔幾天的DELETE ARTICLE還是會死掉。想破頭來探究原因,同事提出是否有可能統計值已經失真的問題,並且利用anaylze的方式證明似乎可以讓DELETE ARTICLE變快;那好,死馬當活馬醫,排程所有ARTICLE以及 HREPL_POOL都在適當時間做統計值分析後(改用DBMS_STATS.GATHER_TABLE_STATS處理才不會lock table,而參數ESTIMATE_PERCENT需設定成100強迫上工),的確可以明顯改善這問題。看來這種大量刪除跟新增的複寫機制,Oracle可要另外加工才能處理得好啊。@@....

Oracle複寫到SQL Server的概略圖

下圖為透過SQL Server複寫機制,將Oracle複寫到SQL Server的架構,概略說明日後補上:

相關在Oracle中建立的Table物件請參考 這裡,流程概念請參考 這裡。

2010年10月18日 星期一

錯誤:18456,SQL 2005 "myUserAccount" 登入失敗

早上一來同事就大喊無法登入SQL Server測試機,快速檢查了一下,原來是登入機制有設定「強制設定密碼原則」,但因為測試機的關係隨手就把它關掉。再次嘗試登入後還是出現錯誤,這次的錯誤代碼為「18456」。Google了一下,發現 這裡 有解法,但它更具價值的地方在於文章下方的表格,已列出錯誤狀態碼各自代表的錯誤描述,好讓你更快速的判斷造成「18456」的根本錯誤原因。最後把幾個instance的登入者密碼改改就好啦,分享囉~



後記:


改了登入者密碼後,同事過沒多久又開始鬼叫無法alter procedure。找了半天才發現,原來是sp裡面有使用server link,連結到已經有修改過密碼的target。這只要再修改「遠端伺服器\屬性\安全性\」中的遠端登入密碼即可。

2010年10月14日 星期四

Workaround for failure of Orakill

還記得 Orakill也有失靈的時候 提到的,當你用Alter system kill session或者Orakill這兩個大絕都砍不掉死在裡面的session的時候,通常建議只有兩種,不是要你下定決心上patch外,就是要你直接閉著眼睛restart instance,雖然可以解決砍不掉的問題,但downtime時間令人難以忍受。今天恰巧又撞上這個難解之題,但這次手上有了Process Explore,對於Windows平台的Oracle來講可說是一大福音。Process Explore可以找出每個Process含有的Thread清單,這對於我們找出需要砍掉的session是很有幫助的。做法很簡單,首先利用下面SQL找出session的spid(thread handle):

select spid, osuser, s.program
  from 
v$process p, v$session s
where p.addr=s.paddr
   and s.sid=785    /*785是你session的session id */

然後打開Process Explore,將Process以Tree的方式展開,找到Oracke.exe後點選它,你就可以在下方看見Thread List;每個Thread後面都會有一個數字,那個就是Thread Handle ID,請確實的對它按右鍵(因為它會亂飄,挺好動的),選擇「Close Handle」,即可大功告成。一下子你就可以看到Oracle Database Control裡的Top Activities清爽多了。

2010年10月13日 星期三

升級你的RMAN CATALOG

今天撞上一個挺有趣的情況。日前我升級了RMAN 專屬的DataBase,但沒有跟著login到RMAN Catalog進行升級。今天一登入後就出現了下列的訊息:


圖1、未升級RMAN CATALOG前,登入RMAN的提示訊息

google了一下才知道原來rman catalog也要在Database升級後跟進才可正確的使用。升級方式很簡單,先以catalog owner的身分登入RMAN後,連打兩次 upgrade catalog即可,畫面參考如下:

圖2、升級RMAN CATALOG

第一次打upgrade catalog 會確認你是CATALOG的OWNER,之後再打一次upgrade catalog即可升級成功。