在大多數的開發項目中,尤爲是集成項目,都會有涉及到數據分析部分的工做,數據分析多數是各類圖表的展示和交互(所謂數據可視化),數據分析的展示速度直接影響着用戶的體驗,並且絕大多數管理系統(MES、PDM/PLM、ERP、SCM、OA、HR等等)的數據都存儲在數據庫中,數據庫相關的性能優化能夠較容易提升程序系統的總體性能、提高用戶的體驗,保障項目的順利驗收。對應用程序進行總體的性能優化須要全局考慮,好比:硬件選型、軟件架構、部署架構、程序開發等方面。本文主要側重介紹程序開發部分的數據庫層面的相關優化手段,但願能對你們有所幫助。java
不管是開發項目仍是集成項目最終的目的是項目的驗收,促進項目的回款,保障公司的資金流進一步的運做。可是若是功能程序的性能不過關,響應速度慢進而影響客戶的體驗,則直接影響着項目的驗收,從而阻礙了公司的正常運做。典型的優化途徑有:硬件選選型、系統軟件、應用程序三個途徑。sql
選擇什麼樣的服務器都會遇到一個相同的問題,那就是選擇什麼硬件配置的服務器。在平常的項目工做中會將服務器區分爲:應用服務器、數據庫服務器、文件服務器以及其餘服務器。數據庫
一般狀況下硬件配置越高,性能越好,可是綜合考慮(money!)硬件配置通常能知足展望將來3-5年性能要求便可。注意:雲服務器如今也是能夠考慮的選擇。apache
在操做系統的選擇上最多見的就是Linux以及Windows,考慮到服務器的性能、安全性一般咱們選擇Linux操做系統。雖然Server版本操做系統自己的性能已經相對穩定,可是咱們能夠優化對應操做系統的配置來進一步匹配對應項目的性能需求,而Linux系列的操做系統相對來講有更多的優化策略和空間,更重要的是運維尤爲遠程運維很方便。緩存
衡量一個程序的標準首當其衝的是程序的安全性,而後則是程序的性能,也就是程序的響應速度。對於程序的保密性要求並非全部行業均是嚴格要求的那麼對於程序的性能則是不區分行業均是更改的性能帶來更好的體驗。安全
應用程序的優化必殺技一般來講就是程序(軟件)自己支持水平擴展,不少書籍都有介紹,百度關鍵字:大型系統架構,能夠了解不少相關知識,水平擴展是另一個話題,這個話題也會涉及到不少方面,在本文中就不一一贅述。性能優化
系統程序的基礎環境調優對應用程序的優化也較爲明顯,好比:Java程序的JVM設置、PHP程序的子進程數配置、.NET程序的認證機制、運行庫設置等等。基礎環境調優也不是本文闡述重點。在下面咱們主要對軟件數據庫相關的優化方案中進行詳細介紹。服務器
雖然NoSQL也開始流行,可是更多場景下它只是數據庫的補充,數據庫自從誕生開始起就緊緊佔據着管理軟件後臺存儲的主場,並且從未離開。數據庫層面的性能優化屬於短平快調整就能見效、或者在開發中稍微注意就能夠大幅度提高性能的常規系統調優手段。架構
咱們一般須要從總體策略發的角度出發,將數據庫調優從彙總查詢、視圖方式、數據緩存等三個方面來進行。併發
在平常工做中若是所涉及的查詢語句較爲複雜,或者須要訪問第三方的數據庫,而在訪問第三方數據庫時經常會由於不一樣的數據庫中不一樣數據表的讀取頻率不一樣,進而影響性能。面對這種狀況咱們一般將須要查詢的內容彙總到中間表,而後直接從中間表進行數據查詢。
通常狀況下建立視圖是不會直接提升性能的,可是若是須要查詢的內容涉及到多個數據表之間的關聯且關聯關係較爲複查,查詢出的結果集被高頻的訪問。這時若是沒有建立視圖那麼每要查詢這個結果集就須要從新建立SQL,可是若是建立統一的視圖而且在建立視圖是已經進行SQL調優,方便你們的統一調用從而來提高數據庫的性能。
當前查詢結果的結果集是爲展示內容提供數據展示,不是交互性數據操做,不常常被改變是咱們能夠將數據的查詢結果集放入緩存中,這樣在讀取時在緩存中進行獲取,減小了對數據庫的訪問操做進一步的提高程序的響應速率。在程序應用中常見的緩存處理手段以下:
靜態緩存
靜態緩存一般爲建立一個靜態的HashMap 變量,在數據獲取是判斷Map中是否含有,若是有在Map變量中獲取,若是沒有則在數據庫中查詢而後放入緩存的Map變量中。
分佈式緩存
分佈式緩存一般是應用於集羣部署的場景,一般應用部署於不一樣的業務服務器,經過Redis 或者Mncached來進行分佈式緩存的管理。
在數據庫優化的方案中最多見也是性能優化的最關鍵的部分就是數據庫的SQL優化,本篇文章分別在查詢優化、更新優化、其餘說明三個方面來進行說明常見的SQL優化。
避免在客戶端返回大數據量
儘可能避免在客戶端返回大數據量,若數據量過大,應該考慮相應需求是否合理。若是必定要返回大數據量,考慮使用數據庫分頁來處理。
查詢避免使用*號
SELECT子句中避免使用*號數據庫在解析的過程當中,會將*依次轉換成全部的列名,這個工做是經過查詢數據字典完成的,這意味着將耗費更多的時間。如:
Select * from emp |
應該爲:
Select id,name,code from emp |
慎用DISTINCT
用EXISTS替換DISTINCT: 當提交一個包含一對多表信息(好比部門表和僱員表)的查詢時,避免在SELECT子句中使用DISTINCT. 通常能夠考慮用EXIST替換, EXISTS 使查詢更爲迅速,由於RDBMS核心模塊將在子查詢的條件一旦知足後,馬上返回結果. 例子:
(低效):
SELECT DISTINCT DEPT_NO,DEPT_NAME FROM DEPT D , EMP E WHERE D.DEPT_NO = E.DEPT_NO |
(高效):
SELECT DEPT_NO,DEPT_NAME FROM DEPT D WHERE EXISTS ( SELECT ‘X' FROM EMP E WHERE E.DEPT_NO = D.DEPT_NO); |
UNION和UNION-ALL
用UNION-ALL 替換UNION ( 若是有可能的話): 當SQL 語句須要UNION兩個查詢結果集合時,這兩個結果集合會以UNION-ALL的方式被合併, 而後在輸出最終結果前進行排序. 若是用UNION ALL替代UNION, 這樣排序就不是必要了. 效率就會所以獲得提升. 須要注意的是,UNION ALL 將重複輸出兩個結果集合中相同記錄. 所以仍是要從業務需求分析考慮使用UNION ALL的可行性。
條件子句的注意事項
建立索引
對where中的條件列建立索引,能夠加快查詢速度。對於表中的主鍵、外鍵、有對像或身份標識意義的字段視狀況添加索引。
避免null判斷
應儘可能避免在 where 子句中對字段進行 null 值判斷,不然將致使引擎放棄使用索引而進行全表掃描,如:
select name from system_users where id is null |
最好不要給數據庫留NULL,儘量的使用 NOT NULL填充數據庫。備註、描述、評論之類的能夠設置爲 NULL,其餘的,最好不要使用NULL。不要覺得 NULL 不須要空間,好比:char(100) 型,在字段創建時,空間就固定了, 無論是否插入值(NULL也包含在內),都是佔用 100個字符的空間的,若是是varchar這樣的變長字段, null 不佔用空間。
能夠在id上設置默認值0,確保表中id列沒有null值,而後這樣查詢:
select name from system_users where id = 0 |
避免不等於操做
儘可能避免在 where 子句中使用 != 或 <> 操做符,不然將引擎放棄使用索引而進行全表掃描。
避免in或not in
in 和 not in 也要慎用,不然會致使全表掃描,如:
select id from t where num in(1,2,3) |
對於連續的數值,能用 between 就不要用 in 了:
select id from t where num between 1 and 3 |
不少時候用 exists 代替 in 是一個好的選擇:
select num from a where num in(select num from b) |
用下面的語句替換:
select num from a where exists(select 1 from b where num=a.num) |
避免對字段進行函數操做
儘可能避免在where子句中對字段進行函數操做,這將致使引擎放棄使用索引而進行全表掃描。以下:
select id from t where substring(name,1,3) = ’abc’ |
查詢全部以abc開頭的名字的id
應改成:
select id from t where name like 'abc%' |
更新批量使用bach處理
在程序中儘可能避免大量的insert或者delete同時處理,若是遇到這種狀況須要使用bach進行批量統一處理。
避免大批量的insert和delete
由於這兩個操做是會鎖表的,表一鎖住了,別的操做都進不來了。因此,若是有一個大的處理,必定把其拆分,使用 LIMIT oracle(rownum),sqlserver(top)條件。
Update注意
若是隻更改一、2個字段,不要Update所有字段,不然頻繁調用會引發明顯的性能消耗,同時帶來大量日誌。
杜絕count(*)
select count(*) from table; |
這樣不帶任何條件的count會引發全表掃描,而且沒有任何業務意義,是必定要杜絕的。
在數據庫使用中儘可能減小長事務
在數據庫中若是涉及到主表、從表、附屬從表,這時若是同時操做三個數據表同時成功以及同時失敗,若是當前數據表的數據量較大,爲了下降數據庫的性能壓力,咱們能夠採用批處理方式分別批處理三個數據表來進行數據庫性能的提高。
減小分佈式事務的使用
通常的數據庫均是支持分佈式事務,當涉及到跨數據庫的不一樣數據表的操做時咱們可使用分佈式事務。但爲了提升性能損耗,儘可能減小這種強一致性需求,更多狀況下轉化爲最終一致性方式來知足業務需求,一般來講引入消息中間件是這種場景下的常規解決手段。
多用varchar和nvarchar
儘量的使用 varchar/nvarchar 代替 char/nchar ,由於首先變長字段存儲空間小,能夠節省存儲空間,其次對於查詢來講,在一個相對較小的字段內搜索效率顯然要高些。
減小大字段的使用
在數據庫中定義類型是儘可能避免使用大字段類型如:BLOB、TEXT、LONG以及Object等大對象的類型
不要在數據庫中存儲文件
在程序設計以及數據庫存儲是不要將圖片文件、其餘日誌文件的文件類型存儲於數據庫中,而是在數據庫中存儲文件索引的URL將文件存儲於文件服務器中。
在進行數據庫鏈接操做時,咱們能夠經過選擇合適的驅動、釋放鏈接池中的資源、選擇符合應用場景的接口,構造只讀結果集來進一步的優化JDBC的配置。下面咱們經過鏈接處理、匹配接口以及返回結果三個方面進行詳細的說明。
對於Java程序而言, Connention的優化一般使用數據鏈接池(dbcp、proxool、c3p0)來進行Connention對象的管理,這樣程序的靈活性強,便於移植。但要注意的是對象池裏中是沒有回收機制,而且對象池裏有容量限制,對於對象池裏的閒置對象儘早的釋放資源。
下面來簡單說明不用的鏈接池的對比:
Dbcp(DataBase connection pool):是apache上的一個 java鏈接池項目。
優勢:配置方便,能夠設置最大和最小鏈接,鏈接等待時間等,持續運行的穩定性,速度快。
缺點:沒有自動的去回收空閒鏈接的功能,大併發量的壓力下穩定性不高,不可以進行鏈接池監控。
Proxool:Proxool是一種Java數據庫鏈接池技術。是sourceforge下的一個開源項目。
優勢:能夠設置最大和最小鏈接,具有監控功能。
缺點:明顯的性能問題持,續運行的穩定性不高。
C3p0:是在Hibernate和Spring中默認支持該數據庫鏈接池,實現了數據源和jndi綁定,支持jdbc3規範和jdbc2的標準擴展。
優勢:支持高併發,異步操做,有自動回收空閒鏈接功能。
缺點:沒有Dbcp的速度快。
對於Statement對象的優化,咱們須要根據不一樣的應用場合選擇合適的Statement接口。如:
Statement:不帶參數,例如:查詢時,不須要到任何參數。
PreparedStatement: PreparedStatement能夠寫參數化查詢,比Statement能得到更好的性能,能夠阻止常見的SQL注入式攻擊,提升安全性。
CallableStatement:專門針對存儲過程,使用它能享受到全部存儲過程帶來的優點,但也包括存儲過程帶來的劣勢如Java程序可移植性查,依賴數據庫等。
優化結果集(ResultSet)查詢時候,返回的結果集有不一樣的類型。結果集分兩種類型:只讀和可更改。返回的結果集默認就是隻讀的。而在Oracle中咱們能夠設置手工加鎖語句(Select XXX forUpdate)。
明確指定主鍵,而且有此數據則鎖定若無則不鎖定
SELECT * FROM products WHERE id='3' FOR UPDATE; |
無主鍵或者主鍵不明確則進行表鎖定
SELECT * FROM products WHERE name='Mouse' FOR UPDATE; |
應用程序優化是一個系統工程,須要綜合考慮,更多時候要提早考慮,在系統架構層面來保障系統具備更多優化的能力。系統運維有一種消極的說法,系統能用就行,不要輕易去改變;但對於系統開發而言,每一次代碼重構都是一次系統調優以及加強調優能力的機會。Devops也慢慢開始盛行了,開發和運營愈來愈密切,甚至是一套班子兩種角色,你(們)如何選擇?我我的而言,傾向主動調優、擁抱變化,即使可能帶來一些風險。
不管是對公司的產品進行開發仍是在項目開發的過程當中,要在全局的角度出發總體考慮、制定規範、落實到每一項的工做中,從制度上保障系統性能調優的能力。筆者做爲數通暢聯公司的一名技術員工,今天將本身所學所用的常見的數據庫優化相關處理總結出來與你們分享。若是對本文檔相關的描述信息存在疑問歡迎加入數通暢聯官方技術羣(299719834)進行討論。