據(jù)庫實戰(zhàn):從安裝部署到數(shù)據(jù)遷移的踩坑全記錄)
國產(chǎn)數(shù)據(jù)庫這幾年確實是技術圈繞不開的話題尤其是隨著行業(yè)對自主可控的要求越來越高國產(chǎn)數(shù)據(jù)庫從“能用”到“好用”的轉變非常明顯。我最近剛學完《國產(chǎn)數(shù)據(jù)庫技術》這門課課程里花了大量時間在達夢數(shù)據(jù)庫DM數(shù)據(jù)庫的實踐上。說實話沒動手之前我以為這就是個“換皮Oracle”真正用下來才發(fā)現(xiàn)它確實在兼容上下足了功夫但也有很多自己的脾氣和邏輯。這篇就把我這段時間從安裝部署到實操踩坑的心得整理一下給正在學或者準備上手DM數(shù)據(jù)庫的朋友做個參考。1. 達夢數(shù)據(jù)庫整體認知它到底是個什么定位1.1 國產(chǎn)數(shù)據(jù)庫賽道里的位置國產(chǎn)數(shù)據(jù)庫現(xiàn)在大體分幾派有從開源生態(tài)發(fā)展起來的比如基于PostgreSQL、MySQL內核二次開發(fā)的也有完全自主研發(fā)的。達夢屬于后者它走的是“自主研發(fā)高度兼容Oracle”的路線。這個定位很聰明因為國內大量企業(yè)級系統(tǒng)以前跑在Oracle上數(shù)據(jù)庫管理員和開發(fā)者的思維習慣都是Oracle那套如果能做到“上手即用”遷移成本就低很多。我在課程里第一次接觸DM8的時候第一感覺就是命令行工具叫disqlSQL語法風格、數(shù)據(jù)字典視圖比如DBA_TABLES、DBA_VIEWS、PL/SQL寫法都帶著濃厚的Oracle影子。但深入用下來你會發(fā)現(xiàn)在某些細節(jié)上它并不是完全照搬比如它的存儲結構、表空間管理模式、備份恢復機制都有自己的一套設計邏輯。1.2 DM數(shù)據(jù)庫的架構特點達夢的體系架構大體還是“實例數(shù)據(jù)庫”的模式一個實例對應一個數(shù)據(jù)庫實例由內存結構和后臺進程組成。內存結構里比較核心的是緩沖池Buffer Pool、日志緩沖區(qū)Log Buffer、字典緩存等這些概念如果你熟悉Oracle就一點都不陌生。一個值得單獨說的點是DM8的“模式Schema”和“用戶”的關系。在達夢里創(chuàng)建一個用戶時會自動創(chuàng)建一個同名的模式用戶在默認模式下創(chuàng)建的表、視圖都歸屬到這個模式。這一點和Oracle的行為一致但對于習慣MySQL里“庫”和“用戶”完全獨立的人來說需要轉換一下思路。這里有個實操中的小體會達夢的sysdba賬號類似于Oracle的sys擁有最高權限日常開發(fā)盡量不要用它去連業(yè)務庫建議單獨建業(yè)務賬號并授予必要的權限。我在做練習時一開始圖省事全程用sysdba結果后面想測試權限控制相關的功能時還得回頭重新建用戶和授權增加了不少工作量。1.3 兼容性策略兼容但不等于替代課程里有一句話我印象很深達夢追求的是“兼容”而非“替代”。也就是說它努力做到讓原來跑在Oracle上的應用能平滑遷移過來但不是所有Oracle高級特性都能直接一一對應。舉個例子Oracle里有物化視圖達夢8也支持Oracle有自治事務PRAGMA AUTONOMOUS_TRANSACTION達夢也有類似的實現(xiàn)。但有些冷門的內置函數(shù)、特定的包Package行為可能就需要手動調整。所以做遷移評估的時候不能只看業(yè)務能不能跑起來還要注意跑起來之后的性能是否達標、是否有隱藏的兼容性坑。我在課程實驗里用達夢自帶的遷移工具把一套模擬的學生選課系統(tǒng)從Oracle遷移到DM8表面上所有表結構和數(shù)據(jù)都遷過來了但實際跑存儲過程的時候報了好幾個錯誤都是因為某些Oracle特有的寫法和內置函數(shù)在達夢里沒有完全等價物。這也算是給所有做國產(chǎn)化遷移的人提個醒遷移工具能幫你搬運數(shù)據(jù)但代碼層面的適配工作一點都不能偷懶。2. 安裝部署實踐從零到能連上數(shù)據(jù)庫2.1 環(huán)境準備與安裝包選擇達夢數(shù)據(jù)庫有企業(yè)版、標準版還有一個開發(fā)版?zhèn)€人學習用。課程里用的是開發(fā)版功能和標準版基本一致只是授權和性能上有限制。下載安裝包之后解壓出來是一堆rpm包和腳本這和Oracle那種巨型安裝向導完全不同。我建議安裝的時候注意幾點用專門的用戶比如dmdba來安裝不要用root直接裝這是個好習慣可以避免權限混亂也符合生產(chǎn)環(huán)境的規(guī)范。安裝路徑規(guī)劃好不建議裝到系統(tǒng)盤根目錄因為數(shù)據(jù)庫的數(shù)據(jù)文件會越來越大。disql這個命令行工具默認字符集可能和你的終端不一致中文顯示亂碼的話需要調整NLS_LANG相關的環(huán)境變量。有個同學在安裝時沒注意磁盤空間數(shù)據(jù)目錄放到了根分區(qū)結果初始化實例到一半就報“磁盤空間不足”。這個看起來是很低級的錯誤但實際操作里確實不少人會踩到尤其是用虛擬機裝的時候默認分配的磁盤往往剛夠系統(tǒng)用根本沒有冗余給數(shù)據(jù)庫。2.2 初始化實例的“坑”安裝完數(shù)據(jù)庫軟件之后還需要初始化實例。達夢初始化實例的工具叫dminit它會在指定目錄下生成數(shù)據(jù)文件、控制文件、日志文件等然后才能啟動數(shù)據(jù)庫服務。初始化實例時幾個關鍵參數(shù)要特別留意PAGE_SIZE頁大小默認是8K如果業(yè)務里有大數(shù)據(jù)量的行可能需要設成16K或32K。這個參數(shù)一旦初始化后就不能改了必須提前想清楚。CHARSET字符集默認是GB18030如果你的應用主要是UTF-8建議在初始化時就指定為UTF-8。我自己第一次就沒改后來為了調整這個參數(shù)折騰了很久。CASE_SENSITIVE大小寫敏感這個參數(shù)決定了標識符大小寫是否敏感。默認是敏感意味著你建表時寫CREATE TABLE Test和CREATE TABLE test產(chǎn)生的是不同的表。對于從Oracle遷移過來的應用來說保持默認就好但如果你更習慣MySQL那種大小寫不敏感的方式這個參數(shù)要提前改。我在初始化的時候周圍好幾個同學都遇到一個情況實例初始化成功了但用disql連接時報“登錄密碼錯誤”。后來發(fā)現(xiàn)是初始化時設置的密碼里包含了特殊字符而終端環(huán)境的轉義把密碼搞變了。解決方法是初始化時暫時用簡單密碼等確認能登錄后再用ALTER USER改成復雜密碼。2.3 啟動與連接檢查實例初始化完成后啟動服務有兩種方式一種是注冊成系統(tǒng)服務用systemctl管理另一種是直接用dmserver命令前臺啟動適合調試場景。我建議學習階段用前臺方式啟動因為一旦數(shù)據(jù)庫啟動報錯前臺模式可以立刻看到日志輸出方便定位問題。跑生產(chǎn)的話當然要注冊成服務并且設置開機自啟還要配置好自動重啟策略。連接測試這一關除了用disql達夢還提供了圖形化管理工具DM管理工具DM Management Tools。這個工具類似Oracle的SQL Developer可以圖形化地管理用戶、表空間、表結構、執(zhí)行SQL腳本等。圖形化工具對于初學者很友好至少比在命令行里懵著敲命令強但實際工作中很多DBA還是習慣寫腳本畢竟批量操作的時候腳本效率高得多。有一個細節(jié)值得提一下達夢默認端口號是5236注意防火墻要放行否則遠程連接不上。我課程實踐里出現(xiàn)過自己本機可以連、但從另外一臺虛擬機連不上的情況排查了半天最后發(fā)現(xiàn)就是虛擬機的防火墻擋了5236端口。3. 核心功能實踐建庫建表到數(shù)據(jù)處理3.1 創(chuàng)建表空間和用戶達夢里創(chuàng)建表空間和用戶的順序和Oracle一致先建表空間再建用戶并指定用戶默認表空間。這里面有個關聯(lián)邏輯表空間是存儲數(shù)據(jù)的物理容器而用戶模式是邏輯對象歸屬。實操里的標準流程大概是-- 創(chuàng)建表空間 CREATE TABLESPACE app_data DATAFILE /dm/data/DMDB/app_data.dbf SIZE 256M AUTOEXTEND ON NEXT 16M MAXSIZE 4G; -- 創(chuàng)建用戶并指定默認表空間 CREATE USER app_user IDENTIFIED BY your_password DEFAULT TABLESPACE app_data; -- 授予基本權限 GRANT DBA TO app_user;這里要特別說明一下GRANT DBA TO app_user在學習環(huán)境里這么干圖省事沒問題但在生產(chǎn)環(huán)境絕對不能這樣做。DBA角色權限太大了等于把整個數(shù)據(jù)庫的鑰匙都給了業(yè)務賬號。正確做法是按需授權只授予業(yè)務實際需要的權限比如CREATE SESSION、CREATE TABLE、SELECT等。建表時達夢的數(shù)據(jù)類型和Oracle高度一致包括NUMBER、VARCHAR2、DATE、CLOB、BLOB等這讓我從Oracle切過來的時候幾乎沒有學習成本。但如果你是從MySQL過來的可能會覺得NUMBER用起來不如INT直觀其實本質上是一個東西就是定義方式不一樣。3.2 SQL兼容性常用語句的差異體驗我在實踐里建了一個課程項目用的選課系統(tǒng)庫里面包含了學生表、課程表、選課記錄表還寫了幾個統(tǒng)計查詢。達夢對標準SQL的支持很完整基本的JOIN、子查詢、聚合函數(shù)、排序、分頁都能用。有個分頁查詢的小差異特別值得注意。Oracle經(jīng)典的分頁寫法是ROWNUMMySQL是LIMIT而達夢8直接支持LIMIT關鍵字同時也支持Oracle的ROWNUM。這意味著你可以用兩種方式寫分頁非常靈活。但需要提醒的是LIMIT在達夢里不是所有場景都支持比如在某些子查詢或視圖定義里用LIMIT就可能報錯這時候就要退回ROWNUM寫法。再比如空值處理達夢默認行為和Oracle一樣是“NULL不參與比較”這一點和MySQL的默認行為不同。如果你之前寫慣了MySQL的查詢在達夢里遇到數(shù)據(jù)查不出來先別急著懷疑SQL寫錯了很有可能是NULL處理邏輯不一樣。還有一個實用小技巧達夢支持Dual表查詢SELECT SYSDATE FROM DUAL;這個Oracle特性被完整保留了下來。3.3 存儲過程和觸發(fā)器PL/SQL風格的延續(xù)達夢的存儲過程語法和Oracle的PL/SQL非常接近支持DECLARE、BEGIN、EXCEPTION、游標、存儲函數(shù)、觸發(fā)器等。課程里要求我們用存儲過程實現(xiàn)一個簡單的選課事務邏輯判斷課程容量如果沒滿就插入選課記錄并更新已選人數(shù)滿了就拋出異常。我貼一段當時寫的簡潔版存儲過程供大家感受語法風格CREATE OR REPLACE PROCEDURE sp_select_course( p_student_id IN VARCHAR2, p_course_id IN VARCHAR2 ) AS v_capacity NUMBER; v_selected NUMBER; BEGIN -- 查詢課程容量和已選人數(shù) SELECT capacity INTO v_capacity FROM course WHERE course_id p_course_id; SELECT selected_count INTO v_selected FROM course WHERE course_id p_course_id; IF v_selected v_capacity THEN RAISE_APPLICATION_ERROR(-20001, 課程容量已滿); END IF; -- 插入選課記錄更新已選人數(shù) INSERT INTO enroll_record(student_id, course_id) VALUES (p_student_id, p_course_id); UPDATE course SET selected_count selected_count 1 WHERE course_id p_course_id; COMMIT; EXCEPTION WHEN OTHERS THEN ROLLBACK; RAISE; END;這個寫法如果之前寫過Oracle的PL/SQL基本可以直接跑兼容度確實很高。但我在實際測試里發(fā)現(xiàn)達夢對RAISE_APPLICATION_ERROR的參數(shù)類型要求比較嚴格如果傳進去的字符串長度超出限制會報錯寫代碼時要注意控制錯誤提示信息的長度。觸發(fā)器方面達夢支持BEFORE、AFTER、INSTEAD OF等類型語法和Oracle也基本一致。另一個細節(jié)是達夢的自治事務用PRAGMA AUTONOMOUS_TRANSACTION聲明用于在觸發(fā)器里寫日志而不影響主事務的回滾。這和Oracle完全一樣在實際項目里非常好用。3.4 視圖與索引實踐視圖在達夢里用起來和Oracle一樣支持創(chuàng)建普通視圖和物化視圖。我建了一個統(tǒng)計視圖用于課程選課人數(shù)統(tǒng)計語句大致如下CREATE OR REPLACE VIEW v_course_stats AS SELECT c.course_id, c.course_name, COUNT(e.student_id) AS enroll_cnt FROM course c LEFT JOIN enroll_record e ON c.course_id e.course_id GROUP BY c.course_id, c.course_name;索引方面達夢支持B樹索引、位圖索引、全文索引等。在課程實踐里我給選課記錄表的student_id和course_id分別建了索引查詢性能提升還是很明顯的。經(jīng)驗是索引不是越多越好每個索引在加快查詢的同時都會拖慢寫入速度并且占用存儲空間。要針對實際查詢模式來設計索引用EXPLAIN看執(zhí)行計劃是最靠譜的判斷方式。擴展一個實踐心得達夢可以通過在SQL前面加EXPLAIN關鍵字來查看執(zhí)行計劃這一點和Oracle一致。執(zhí)行計劃能清楚地告訴你SQL有沒有走索引、有沒有做全表掃描是排查性能問題的最基本工具。我在調優(yōu)一個選課統(tǒng)計查詢時就是通過執(zhí)行計劃發(fā)現(xiàn)表連接順序不對調整了驅動表和被驅動表的順序后查詢時間從十幾秒降到了幾百毫秒。4. 數(shù)據(jù)管理實操備份恢復與遷移4.1 備份機制的理解與操作達夢的備份分為物理備份和邏輯備份。物理備份備份的是數(shù)據(jù)文件本身恢復時能把數(shù)據(jù)庫恢復到備份時刻的狀態(tài)適合災難恢復邏輯備份導出的是一系列SQL語句或記錄適合數(shù)據(jù)遷移、異構數(shù)據(jù)庫之間交換數(shù)據(jù)。我在實踐中重點測試了物理備份和恢復達夢也有類似Oracle的歸檔模式概念。開啟歸檔模式后數(shù)據(jù)庫會保存所有事務日志這樣在備份數(shù)據(jù)文件的基礎上再應用日志可以把數(shù)據(jù)庫恢復到任意時間點。實際操作里一個值得記錄的點達夢在OPEN狀態(tài)下做聯(lián)機備份默認是備份數(shù)據(jù)庫的完整數(shù)據(jù)文件加上歸檔日志這樣才能保證恢復時數(shù)據(jù)一致性。如果只是簡單備份了數(shù)據(jù)文件而沒有備份歸檔日志恢復到某個時間點時可能因為日志缺失而失敗。我測試恢復時印證了一個原則備份是一個完整流程里的其中一環(huán)備份后的驗證也就是定期做恢復演練同樣重要。課堂上我試了一次把數(shù)據(jù)庫恢復到另一臺機器上就因為兩邊安裝路徑不一致導致恢復失敗后來通過修改dm.ini里的配置項才解決。這提醒我們生產(chǎn)環(huán)境里做恢復演練不能只在同一臺機器上做跨機恢復必須提前驗證環(huán)境差異。4.2 數(shù)據(jù)遷移工具的使用達夢提供了兩個主要遷移工具一個是圖形化的數(shù)據(jù)遷移工具DM數(shù)據(jù)遷移工具DTS一個是命令行工具dmfldr用于快速加載數(shù)據(jù)。我課程里先用了DTS把Oracle里的模擬數(shù)據(jù)遷到DM8整個過程界面引導清晰選擇源庫、目標庫、映射表結構、開始遷移一氣呵成。用DTS遷移時有幾點實際經(jīng)驗值得分享遷移前要把源端的只讀權限賬號準備好避免使用高權限賬號操作生產(chǎn)庫。大表遷移時最好分批提交不然一個事務里數(shù)據(jù)量太大會消耗大量回滾段。字符集的坑非常容易出現(xiàn)。源Oracle庫是AL32UTF8目標DM8設的GB18030的話遷移完成后中文亂碼是必然的。所以在初始化DM庫實例時就要確認好字符集方向不能遷移到一半再改。還有一個天然坑DTS并不能把Oracle里所有的存儲過程、包、函數(shù)100%自動轉換。我遷移完存儲過程后用disql執(zhí)行報了一些語法錯誤。這些不能指望工具自動處理只能手動修改函數(shù)名或語法。4.3 導入導出實踐與數(shù)據(jù)一致性驗證除了物理備份達夢的邏輯備份用dexp導出和dimp導入工具完成。類似的用法是# 導出某個用戶的全部對象 dexp app_user/app_user_password DIRECTORY/dm/backup FILEapp_user.dmp LOGapp_user_exp.log # 導入到目標庫 dimp app_user/app_user_password DIRECTORY/dm/backup FILEapp_user.dmp LOGapp_user_imp.log這里要注意導入時如果目標庫里已經(jīng)有同名對象通常需要先DROP或者使用忽略錯誤的參數(shù)否則會報“對象已存在”。我在實踐時為了省事直接用了忽略錯誤的模式結果有些表雖然導入報告成功但數(shù)據(jù)量對不上一查發(fā)現(xiàn)是部分約束沖突導致數(shù)據(jù)沒有完全導入。所以導入完成后的數(shù)據(jù)校驗環(huán)節(jié)不能省略一定要通過COUNT(*)或者對比關鍵表的記錄數(shù)來做驗證。導入導出還有個經(jīng)驗大字段CLOB/BLOB多的表導出文件會非常大傳輸和導入都比較耗時。最好按照業(yè)務模塊分用戶、分表導出而不是一股腦全庫導出這樣既能減小單文件大小也方便單獨恢復某個模塊的數(shù)據(jù)。5. 常見問題與排查技巧記錄這一塊是學習過程中最有價值的沉淀。我把這段時間遇到的各種報錯和解決辦法整理成了一張速查表方便以后查閱。問題現(xiàn)象可能原因解決辦法disql登錄報“登錄密碼錯誤”密碼含特殊字符被終端轉義用簡單密碼初始化登錄后ALTER USER改密碼中文顯示亂碼字符集不一致初始化實例時設UTF-8/GB18030修改客戶端NLS_LANG遠程連不上數(shù)據(jù)庫防火墻未放行5236端口開放端口核對監(jiān)聽IP是否為0.0.0.0LIMIT分頁在視圖中報錯部分場景不支持LIMIT改用ROWNUM實現(xiàn)分頁大表查詢特別慢未走索引或執(zhí)行計劃有誤EXPLAIN 查看計劃優(yōu)化索引設計存儲過程報ORA-00942錯誤對象名大小寫敏感或不存在確認大小寫注意模式名schema前綴恢復備份到新機器失敗路徑不一致或控制文件找不到修改dm.ini里的路徑參數(shù)保持目錄結構一致導入數(shù)據(jù)后記錄數(shù)對不上導入模式忽略了約束沖突導數(shù)據(jù)前先清理目標表或分批導入后驗證COUNT排查問題時我發(fā)現(xiàn)達夢的日志體系分為事件日志和SQL日志默認情況下SQL日志可能沒完全開啟。當遇到奇怪的SQL問題而找不到線索時先檢查是否開啟了相關調試日志這會給你節(jié)省大量排查時間。另一個經(jīng)常被初學者忽略的點是達夢的授權機制。開發(fā)版有連接數(shù)限制當多個終端同時連接時可能會報“超出許可連接數(shù)”這是正?,F(xiàn)象不是數(shù)據(jù)庫故障。宿舍幾個人一起連著同一個數(shù)據(jù)庫實驗庫練習的時候這個問題沒少見。控制好連接復用或者輪流操作就可以避免這個報錯。還要補充一個關于大小寫的細致問題。我在某次實驗中建了一張表叫Student因為大小寫敏感參數(shù)開著后面寫SQL的時候用了student小寫直接報“表或視圖不存在”。索引、列名、同義詞的大小寫也都要保持一致這在使用ORM框架時尤其要注意框架生成的SQL往往默認小寫很容易踩坑。6. 個人實操總結與擴展思考6.1 學習路線上的體會學DM數(shù)據(jù)庫給我的最大感受是它特別適合已經(jīng)有一定關系型數(shù)據(jù)庫基礎的人快速上手。因為語法像Oracle管理理念也向Oracle靠攏所以只要你不是零基礎幾個小時就能完成建庫、建用戶、建表、導入數(shù)據(jù)這一整套操作。但對于只接觸過MySQL的人有幾點需要額外適應表空間和用戶的關系需要理清既不是MySQL的“庫”也不是完全孤立的“模式”。大小寫敏感問題要提前確認不然SQL里到處是引號和大小寫錯誤。數(shù)據(jù)字典視圖從information_schema那套切換到達夢的DBA_*視圖體系查詢元數(shù)據(jù)的方式變得完全不同。我在實驗里把之前用MySQL寫過的一個簡單的圖書管理系統(tǒng)改造到了達夢上最初花了不少時間調試大小寫和數(shù)據(jù)類型差異但調通之后整個改造過程反而變得很順暢。這讓我意識到達夢的Oracle兼容策略對國內大量存量系統(tǒng)來說確實是一條務實的路。6.2 后續(xù)可以繼續(xù)挖掘的方向學完課程之后我覺得有幾個方向值得繼續(xù)深入達夢的集群部署和高可用方案比如數(shù)據(jù)守護、讀寫分離集群。單機學起來容易生產(chǎn)環(huán)境都是要上集群的而這方面我的實踐還非常有限。性能調優(yōu)。包括參數(shù)調整緩沖區(qū)大小、并發(fā)線程數(shù)等、SQL改寫、索引優(yōu)化這些和實際業(yè)務性能強相關也是DBA核心價值所在。異構數(shù)據(jù)庫遷移的完整流程。課程里只做了一個模擬庫的遷移真實場景里業(yè)務流程、存儲過程、定時任務、應用代碼的改造成本遠比想象復雜。我個人體會是國產(chǎn)數(shù)據(jù)庫的學習不能只在文檔里看一定要親手裝一遍、建一遍、備份一遍、恢復一遍、報錯一次再排查一遍這些經(jīng)歷里學到的東西才是真正能帶到工作中去的。有一點心得想特別分享給剛開始接觸達夢的朋友不要被“國產(chǎn)”兩個字帶著預設成見把它當成一個普通的、有自己特性的數(shù)據(jù)庫產(chǎn)品去學習和使用。它的生態(tài)和工具鏈雖然不像國際老牌數(shù)據(jù)庫那么龐大但在文檔、社區(qū)、培訓材料這些方面現(xiàn)在其實已經(jīng)相當完整了。如果你所在的公司未來有國產(chǎn)化替代的計劃現(xiàn)在動手學習達夢絕對是個劃算的時間投入。