自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

慢SQL,壓垮團隊的最后一根稻草

開發(fā) 開發(fā)工具
今天只討論一種應(yīng)用模式,就是最普遍的,前端實時調(diào)用后端web服務(wù),服務(wù)端經(jīng)過DB的增刪改查作出響應(yīng)的應(yīng)用。至于離線數(shù)據(jù)分析,在線規(guī)則引擎模板執(zhí)行,流式計算等不在本次討論范疇。

先說結(jié)論,我支持將邏輯寫在 Java 等應(yīng)用系統(tǒng)中。

[[225954]]

背景:

今天只討論一種應(yīng)用模式,就是最普遍的,前端實時調(diào)用后端web服務(wù),服務(wù)端經(jīng)過DB的增刪改查作出響應(yīng)的應(yīng)用。至于離線數(shù)據(jù)分析,在線規(guī)則引擎模板執(zhí)行,流式計算等不在本次討論范疇。

重SQL開發(fā)的場景

先看一個例子吧。用經(jīng)典的 Controller Service DAO 開發(fā)模式描述。

需求:

查詢出每個學(xué)生所在的城市名以及分?jǐn)?shù)展示到前端。

重SQL模式

  1. class Controller{ 
  2.   Service service; 
  3.   Map<String,String> get(Map<String,Object> param){ 
  4.     return service.get(param); 
  5.   } 
  6.  
  7. class Service{ 
  8.   DAO dao; 
  9.   Map<String,String> get(Map<String,Object> param){ 
  10.     return dao.get(param); 
  11.   } 
  12. class DAO{ 
  13.   SQLTemplate template; 
  14.   Map<String,String> get(Map<String,Object> param){ 
  15.     String sql = "select city_name,student_name,score from student,score,city where city.city_code=student.city_code and score.student_id=student.student_id" ; 
  16.     return template.execute(sql,param); 
  17.   } 

重Java模式

  1. class View
  2.   String studentName; 
  3.   String cityName; 
  4.   String score; 
  5.  
  6. class Requent{ 
  7.  
  8. class Controller{ 
  9.   Service service; 
  10.   List<View> get(Requent request){ 
  11.     return service.get(param); 
  12.   } 
  13.  
  14. class Service{ 
  15.   StudentDAO studentDAO; 
  16.   ScoreDAO scoreDAO; 
  17.   CityDAO cityDAO; 
  18.   List<View> get(Requent param){ 
  19.     Student studentRequest = new Student(); 
  20.     //查詢學(xué)生 
  21.     List<Student> students = studentDAO.select(studentRequest); 
  22.     List<View> result = new ArrayList(students.size()); 
  23.      
  24.     for(Student student : students){ 
  25.       View view = new View(); 
  26.       view.setStudentName(student.getStudentName()); 
  27.      
  28.        //拼接城市名 
  29.       City cityRequest  = new City(); 
  30.       cityRequest.setStudentId(student.getStudentId()); 
  31.       City city = cityDAO.select(cityRequest); 
  32.       view.setCityName(city); 
  33.        
  34.       //拼接分?jǐn)?shù) 
  35.       Score scoreRequest = new Score(); 
  36.         scoreRequest.setStudentId(student.getStudentId()); 
  37.       Score score = scoreDAO.select(scoreRequest); 
  38.       view.setScode(score.getScore()); 
  39.        
  40.       result.add(view); 
  41.     } 
  42.     return result; 
  43.   } 
  44.  
  45. class StudentDAO{ 
  46.   SQLTemplate template; 
  47.   Student select(Student param){ 
  48.     String sql = "select * from Student where param = ..."
  49.     template.select(sql,param); 
  50.   }  
  51.  
  52. class ScoreDAO{ 
  53.   SQLTemplate template; 
  54.    Score select(Score param){ 
  55.     String sql = "select * from Score where param = ..."
  56.     template.select(sql,param); 
  57.   } 
  58.  
  59. class CityDAO{ 
  60.   SQLTemplate template; 
  61.   City select(City param){ 
  62.   String sql = "select * from City where param = ..."
  63.     template.select(sql,param); 
  64.   } 

可以看到,使用重SQL的模式來進(jìn)行開發(fā)確實很快很快,只需要把SQL開發(fā)出來基本就完事了,但是看著用重 Java 的模式開發(fā),需要寫一堆的代碼,這么看來好像是 SQL 勝利一籌。

好,PD突然說了,我要把城市名為 “大蕉” 的,分?jǐn)?shù)乘于2展示出來。握草,這個怎么搞??

重SQL模式

  1. class DAO{ 
  2.   SQLTemplate template; 
  3.   Map<String,String> get(Map<String,Object> param){ 
  4.     String sql = "select city_name,student_name,CASE WHEN city.city_name='大蕉' THEN 2*score.score ELSE score END score from student,score,city where city.city_code=student.city_code and score.student_id=student.student_id " ; 
  5.     return template.execute(sql,param); 
  6.   } 

好了。。這個SQL已經(jīng)變得很復(fù)雜了基本沒法看了。。

重 Java 模式

  1. Service.class    
  2.  
  3.      //拼接分?jǐn)?shù) 
  4.       Score scoreRequest = new Score(); 
  5.         scoreRequest.setStudentId(student.getStudentId()); 
  6.       Score score = scoreDAO.select(scoreRequest); 
  7.        
  8.       if("大蕉".equals(city.getCityName()){ 
  9.         view.setScode(score.getScore() * 2); 
  10.       }else
  11.             view.setScode(score.getScore()); 
  12.       } 
  13.        

咦好像改動也不多嘛。

這時候PD又來了我要把城市名為 “大蕉” ,并且城市Code小于10086的,分?jǐn)?shù)乘于2展示出來。握草,完蛋了,之前全是SQL,這個需求要怎么搞??繼續(xù)疊加上去繼續(xù) CASE WHEN?

還沒想清楚呢,突然 DBA 電話飛過來了,兄dei你的SQL太慢了,現(xiàn)在把整個庫拖垮了,你是不是沒有加索引?

我:索引加了啊。。。難道是沒走到?那是先解決慢SQL還是先開發(fā)需求呢?拆庫是不可能了,邏輯這么死鬼復(fù)雜拆庫完全沒法跑啊,加CPU加內(nèi)存啊 DBA大佬?。?!

[DBA日報] 慢SQL 180+,已解決10。

又上了一個版本

[DBA日報] 慢SQL 200+,已解決15。

又上了一個版本

[DBA日報] 慢SQL 250+,已解決30。

慢慢的,開發(fā)和運營和DBA每天都疲勞于監(jiān)控這些SQL。。。。

前言

觀察了一下,傳統(tǒng)企業(yè)以及絕大部分轉(zhuǎn)型中的企業(yè)的 Java 應(yīng)用中,很神奇的是,他們的開發(fā)人員包括我自己以前,大家都非常非常希望使用一個 SQL 來完成所有的邏輯的編寫,非常多企業(yè)更是把數(shù)據(jù)庫的存儲過程和數(shù)據(jù)庫自定義函數(shù)來完成。

這些關(guān)于邏輯應(yīng)該寫在哪里的爭論從來沒有停止過,不僅僅發(fā)生在后端和數(shù)據(jù)庫端,連前后端都經(jīng)常會發(fā)生這種爭論,現(xiàn)在只討論后端和數(shù)據(jù)庫端的糾結(jié)。

我將從這五個方面分別對比一下兩種模式的異同。

  • 出現(xiàn)場景
  • 開發(fā)效率
  • 缺陷排查
  • 架構(gòu)升級
  • 系統(tǒng)維護

出現(xiàn)場景

SQL

我們絕大多數(shù)的歷史代碼都是用存儲過程來實現(xiàn)的啊,如果有新需求不往上面做的話,很難兼容原來的邏輯啊啊。

前面的人呢是這樣寫的,我來了看大家都這樣寫就這樣寫了。

Java

新應(yīng)用嘛,我想怎么樣寫就怎樣寫。

監(jiān)控和埋點寫起來簡單吖,排查問題可方便了。

前面的人呢是這樣寫的,我來了看大家都這樣寫就這樣寫了。

開發(fā)效率

SQL

這樣寫起來很快啊,而且寫 Java 代碼多難受啊,寫 SQL 我自己在數(shù)據(jù)庫開發(fā)環(huán)境跑一下結(jié)果正確我就直接丟到代碼中提交了,多爽啊。

老實說,這樣子確實會提高開發(fā)的效率,因為不用寫那么多查庫聚合的操作,一切都在 SQL 中搞定了。另一方面來看,這確實會讓 Java 代碼看起來很雞肋,好像只是把數(shù)據(jù)從 web 層到數(shù)據(jù)層的一個管道而已,一切 if else 能寫在 SQL 中的都寫在 SQL 中了。

但是新需求來或者需求變更的時候,我經(jīng)常要重新寫SQL,如果變動不多我可能要改動到原來的 SQL,但是我又不敢改,所以只好 copy 重新寫一個,改 SQL 的風(fēng)險好大,一報錯又要重啟好難受。

Java

一次要寫N個類,有點煩。

新需求來或者需求變更的時候,如果邏輯比較復(fù)雜,我直接抽成方法或者改成一些設(shè)計模式,維護起來效率還是可以接受的。

缺陷排查

SQL

開發(fā)排查問題的時候,除了看日志,直接把SQL和參數(shù)丟到 PL/SQL 或者 其他工具里跑一下,基本就能知道數(shù)據(jù)問題出現(xiàn)在哪了。測試同學(xué)在進(jìn)行測試的時候,如果發(fā)現(xiàn)有不對的東西,直接跟開發(fā)同學(xué)一樣的思路,把SQL 跑一下,問題基本就定位得七七八八了。

但是呢,一旦遇到跑 SQL 無法一眼看出問題的 bug 或者 SQL 實在是太長太長了的的時候,就蒙圈了。我曾經(jīng)就維護了一個幾千行的存儲過程,一旦發(fā)生問題,排查問題的過程巨艱難。但是呢直接用一個數(shù)據(jù)庫一個功能搞定所有功能未嘗不是一件很爽的事情,因為關(guān)系型數(shù)據(jù)庫實在是實在是太太太穩(wěn)定了,一次編寫***運行。

Java

看日志看監(jiān)控。

根據(jù)報錯的代碼位置 check 一下代碼邏輯。

一些入?yún)⒎种庋?check 不出來,只能遠(yuǎn)程 debug 慢慢看數(shù)據(jù)流向。

測試的同學(xué)基本無法幫忙 check 缺陷,只能靠程序的表現(xiàn)來判斷。

架構(gòu)升級

SQL

SQL 慢沒關(guān)系,它穩(wěn)定啊,慢就把機器垂直擴展一下好啦,加cpu,加內(nèi)存,換SSD,加加加絕對可以解決事情的。

SQL 有各種索引和優(yōu)化策略,說不定跑起來比我們自己寫邏輯還快呢。

加加加,加內(nèi)存加cpu垂直升級。也沒有其他招數(shù)了,除了前置緩存,但是如果查詢都很個性化SQL很復(fù)雜,前置緩存也基本沒啥亂用。。。

如果你的邏輯全部寫在 SQL 中,那完蛋了,你這個表基本就沒法分表了,因為你的業(yè)務(wù)邏輯跟數(shù)據(jù)庫的數(shù)據(jù)完整性是強耦合的,需要一切數(shù)據(jù)基本都在一個數(shù)據(jù)庫中,這是一件很難受很難受的事情,不信你去問問那些所有業(yè)務(wù)邏輯全寫在 SQL 中的小伙。

數(shù)據(jù)庫中非常復(fù)雜的表關(guān)聯(lián)會極大程度拖慢數(shù)據(jù)庫處理每條 SQL 的平均時間,極大程度拖慢數(shù)據(jù)庫 RT,降低了數(shù)據(jù)庫的 RT ,如果邏輯都寫在 SQL 中,那么只能進(jìn)行垂直升級。因為一旦進(jìn)行水平擴展,那么多機器的非常復(fù)雜的分布式表關(guān)聯(lián),RT 基本不是一個高并發(fā)的業(yè)務(wù)應(yīng)用的能容忍的。

Java

如果是數(shù)據(jù)庫瓶頸,加數(shù)據(jù)庫機器,分庫分表一下,應(yīng)用層基本不用改,在DAO層進(jìn)行路由一下。

如果是服務(wù)器cpu瓶頸,多加幾臺機器就好了。

如果還有瓶頸,增加一下查詢緩存。

在應(yīng)用快速發(fā)展的過程中一般都會分庫分表的拆分或者自動水平擴展,這時候其實只需要數(shù)據(jù)庫層面做好自己的數(shù)據(jù)遷移和同步就好了,對于業(yè)務(wù)層來說是完全無感知的。即使業(yè)務(wù)非常非常復(fù)雜,需要拆應(yīng)用,其實也非常簡單,只需要把對應(yīng)的 DAO 層的操作拆分出去,換成 RPC 或者其他方式的調(diào)用就好了。

系統(tǒng)維護

SQL

舊SQL完全不敢動,來一個需求加一個 SQL。

慢SQL日益增加,應(yīng)對疲乏。

Java

SQL寫完一次基本不用動,來一個需求加一個方法聚合一下數(shù)據(jù)操作即可。

應(yīng)用維護比較簡單,只要監(jiān)控做好了,定位到問題基本都能很快解決。

邏輯越來越復(fù)雜,沒有好的開發(fā)框架的話,代碼維護起來也是挺要命,因為完全不知道跑哪個分支去了。但是現(xiàn)在已經(jīng)有很多優(yōu)秀的開源框架來更好地維護代碼了,比如 Spring 的全家桶。

怎么破

舊的重 SQL 邏輯暫時不要動,新的邏輯都基于 Java 模式開發(fā),先保證慢 SQL 不增加,舊的 SQL 穩(wěn)定運行,畢竟業(yè)務(wù)穩(wěn)定是***要素。

如果業(yè)務(wù)初期需要非常非??焖匍_發(fā),那么使用重 SQL 模式也是可以理解的,但是還是要抽時間重構(gòu)成 Java 模式。

結(jié)論

我支持將邏輯寫在 Java 等應(yīng)用系統(tǒng)中。其實原因在上面基本描述完了,***就是復(fù)雜 SQL 的表關(guān)聯(lián)其實跟個人的能力有非常大的關(guān)系,如果一個 SQL 寫得不好,那是極慢極慢的非常容易把整個數(shù)據(jù)庫拖慢的。第二就是維護這些 SQL 也是一件很難受的事情,因為你完全不知道這個 SQL 背后的數(shù)據(jù)流轉(zhuǎn)是怎樣的,你只能根據(jù)自己的猜測去查看 SQL 中的 bug,Java 應(yīng)用好歹還能 debug 一下還有打點看看數(shù)據(jù)不是?如果邏輯寫在 Java 中那么其實你的 DAO 層只需要編寫一次,但是可以***使用,基本不會在這一層浪費很多的時間(用過 ibatis 的都知道改了 SQL 需要重啟應(yīng)用吧?)。第三就是邏輯都寫在 SQL ,中對于分庫分表和應(yīng)用拆分來說是一件非常難受的事情,真的難受。

昨天寫的被吐槽了,回爐重造了,重新看看。

責(zé)任編輯:武曉燕 來源: 51CTO專欄
相關(guān)推薦

2022-08-15 07:32:03

SQL語句數(shù)據(jù)庫

2014-01-10 10:53:29

移動廣告平臺進(jìn)化分發(fā)

2011-07-28 09:09:23

Java

2020-05-08 09:37:32

網(wǎng)線網(wǎng)絡(luò)網(wǎng)速

2025-04-03 00:03:00

數(shù)據(jù)內(nèi)存網(wǎng)絡(luò)

2011-07-22 10:40:04

思科裁員

2015-03-23 11:56:58

2017-02-07 09:15:54

光纖傳輸介質(zhì)通信網(wǎng)絡(luò)

2016-12-01 09:30:03

運維網(wǎng)絡(luò)網(wǎng)線

2009-03-12 10:03:00

雙絞線連接網(wǎng)絡(luò)

2021-03-23 08:21:06

GolangPython字符

2016-05-18 14:50:57

運維PortfastAPI

2020-07-16 11:16:57

云計算SD-WAN運營

2010-09-10 16:17:27

2022-12-13 10:28:53

2017-08-14 16:36:23

ASActivity內(nèi)存

2021-04-06 08:20:24

二叉搜索樹數(shù)據(jù)結(jié)構(gòu)算法

2017-12-28 11:25:51

2019-09-02 10:38:30

網(wǎng)線攻擊MVP

2016-11-18 13:58:33

點贊
收藏

51CTO技術(shù)棧公眾號