顯示具有 SQL 標籤的文章。 顯示所有文章
顯示具有 SQL 標籤的文章。 顯示所有文章

2019/5/17

SQL server 裡面用SQL達成遞迴

直接給程式
;with bomRecursive(MD001,MD003,Level,semiMaterial) as (
select  MD001,MD003,1,a.MD001 from BOMMD a
union all
select a.MD001,b.MD003,b.Level+1,b.semiMaterial
from  bomRecursive b inner join BOMMD a on a.MD003=b.MD001

)
select * from bomRecursive where MD001='1001120597'
order by Level desc ,MD003
option (MAXRECURSION 10)

解釋:(個人理解的方式)
bomRecursive<==遞迴用的函式名稱
 select  MD001,MD003,1,a.MD001 from BOMMD a<==第一階查詢
其中1代表第一階
 select a.MD001,b.MD003,b.Level+1,b.semiMaterial<==遞迴查詢查詢
b.level代表查詢到第幾階 ,要注意因為使用union,所以SQL欄位型態、欄位數量要一致

from  bomRecursive b inner join BOMMD a on a.MD003=b.MD001<==bomRecursive代表回傳的資料表值函式、可以join其他資料


 option (MAXRECURSION 10)<==代表最多查詢遞迴幾階,可以省略,預設1000

2017/1/9

取得 SQL Server 資料庫正在執行的 T-SQL 指令與詳細資訊--執行時間超過多久的查詢


來源:
http://blog.miniasp.com/post/2010/10/13/How-to-get-current-executing-statements-in-SQL-Server.aspx
我們若用 sp_who2 這個系統預儲程序可以查出所有連線的狀況,也可以看到該連線被卡住 (Blocked) 的狀況,不過 Command 這個欄位卻只有查詢的摘要,看不出完整的查詢命令為何:
SELECT      r.scheduler_id as 排程器識別碼,
            status         as 要求的狀態,
            r.session_id   as SPID,
            r.blocking_session_id as BlkBy,
            substring(
    ltrim(q.text),
    r.statement_start_offset/2+1,
    (CASE
                 WHEN r.statement_end_offset = -1
                 THEN LEN(CONVERT(nvarchar(MAX), q.text)) * 2
                 ELSE r.statement_end_offset
                 END - r.statement_start_offset)/2)
                 AS [正在執行的 T-SQL 命令],
            r.cpu_time      as [CPU Time(ms)],
            r.start_time    as [開始時間],
            r.total_elapsed_time as [執行總時間],
            r.reads              as [讀取數],
            r.writes             as [寫入數],
            r.logical_reads      as [邏輯讀取數],
            -- q.text, /* 完整的 T-SQL 指令碼 */
            d.name               as [資料庫名稱]
FROM        sys.dm_exec_requests r 
   CROSS APPLY sys.dm_exec_sql_text(sql_handle) AS q
   LEFT JOIN sys.databases d ON (r.database_id=d.database_id)
WHERE       r.session_id > 50 AND r.session_id <> @@SPID
ORDER BY    r.total_elapsed_time desc
 補充:
http://blog.sina.com.cn/s/blog_5408b1c80100fxv8.html
此文章說,可以查詢執行超過多久,還需要驗證
而在我管理的SQL Server2000系統裡, 執行時間超過30分鐘的進程都時常會出現。
          
  從那篇文章裡學到可以從[master].[dbo].[sysprocesses]裡獲取,阻塞並且等待時間是1800秒(30分鐘)的進程信息:
          
  select * from
          [master].[dbo].[sysprocesses] 
  where blocked > 0 and waittime >1800  
          

2015/10/21

MySQL各類引擎對比



FROM:
http://blog.roga.tw/2008/11/mysql-%E8%B3%87%E6%96%99%E5%BA%AB%E5%84%B2%E5%AD%98%E5%BC%95%E6%93%8E%E7%9A%84%E9%81%B8%E7%94%A8/
http://ssorc.tw/663
http://twpug.net/docs/mysql-5.1/pluggable-storage.html
簡而言之:
項目 MyISAM InnoDB Memory
空間限制 64TB 記憶體
transaction x x
大量 Insert 速度
設置外來鍵 x x
鎖定層級 資料表 資料列 資料表
二元樹索引 不知
雜湊索引 x
全文搜尋索引 x x
資料壓縮 x x
資料快取 x
索引快取
記憶體佔用
磁碟佔用 x

MyISAM 來說,最大的好處是成本低,而且可以 create views
InnoDB 也是有缺點像是不支援 FULLTEXT 的索引,且記憶體佔用多、磁碟空間耗用大
下述儲存引擎是最常用的:
· MyISAM:預設的MySQL插件式儲存引擎,它是在Web、數據倉儲和其他應用環境下最常使用的儲存引擎之一。注意,通過更改STORAGE_ENGINE配置變數,能夠方便地更改MySQL伺服器的預設儲存引擎。
· InnoDB:用於事務處理應用程式,具有眾多特性,包括ACID事務支援。
· BDB:可替代InnoDB的事務引擎,支援COMMIT、ROLLBACK和其他事務特性。
· Memory:將所有數據保存在RAM中,在需要快速搜尋引用和其他類似數據的環境下,可提供極快的訪問。
· Merge:允許MySQL DBA或開發人員將一系列等同的MyISAM資料表以邏輯方式組合在一起,並作為1個對象引用它們。對於諸如數據倉儲等VLDB環境十分適合。
· Archive:為大量很少引用的歷史、歸檔、或安全審計訊息的儲存和檢索提供了完美的解決方案。
· Federated:能夠將多個分離的MySQL伺服器連結起來,從多個物理伺服器建立一個邏輯資料庫。十分適合於分佈式環境或數據集市環境。
· Cluster/NDB:MySQL的叢集式資料庫引擎,尤其適合於具有高性能搜尋要求的應用程式,這類搜尋需求還要求具有最高的正常工作時間和可用性。
· Other:其他儲存引擎包括CSV(引用由逗號隔開的用作資料庫資料表的檔案),Blackhole(用於臨時禁止對資料庫的應用程式輸入),以及Example引擎(可為快速建立定製的插件式儲存引擎提供幫助)。
請記住,對於整個伺服器或方案,您並不一定要使用相同的儲存引擎,您可以為方案中的每個資料表使用不同的儲存引擎,這點很重要。

2015/4/20

Monting SQL server 2012 Express in cacti using wmic


建立了cacti ,使用WMIC來監控SQL server 2012 Express狀態
發現一直給我


[wmi/wmic.c:212:main()] ERROR: Retrieve result data.
NTSTATUS: NT code 0x80041010 - NT code 0x80041010
一直尋找,終於在這邊找到一部份原因:

https://support.microsoft.com/en-us/kb/820847/en-us

o confirm that this problem is occurring, you can use the WbemTest.exe tool that is provided with Microsoft Windows Server 2003. To use the WbemTest.exe tool, follow these steps:
  1. Click Start, click Run, type Wbemtest, and then click OK.
  2. In Windows Management Instrumentation Tester, click Connect.
  3. In the Namespace box, type root\cimv2, and the click Connect.
  4. Click Enum Classes.
  5. In the Enter superclass name box, type Win32_Perf, click Recursive, and then click OK.
  6. In Query Results, you will not see results for the counters that are not transferred to WMI.
For example, the counter object for Exchange Server 2003 routing is OBJECT_SDSMTP_ROUTING_009_NAME=SMTP Routing. If this counter does not exist, you will not see the following lines in Query Results:
  • Win32_PerfFormattedData_SDSMTPROUTING_SMTPRouting
  • Win32_PerfRawData_SDSMTPROUTING_SMTPRouting

歸納如下:
1.我升級到標準版,一樣監控不到,原因是:WMI的emun class名字沒變(意思就是:不要搞升級,重新安裝)
2.真的要監控express版本,WQL:"SELECT * FROM Win32_PerfRawData_MSSQLSQLEXPRESS_MSSQLSQLEXPRESSTransactions",
正式版本的
WQL:"SELECT * FROM Win32_PerfFormattedData_MSSQLSERVER_SQLServerDatabases"

所以:自己努力改CACTI的template吧



2015/4/9

SQL查詢TABLE內連續出現3次的指令



From:

http://www.blogjava.net/changedi/archive/2015/01/29/422554.html

節錄:
discuss區找到了一個很讚的解法,通過定義變量,很巧妙的解了這個擴展的問題,原作者kent-huang

select DISTINCT num
FROM (
  select
    num,
    case when @record = num then @count:=@count+1
         when @record <> @record:=num then @count:=1
    end as n
  from Logs ,(
    select
       @count:=0,
       @record:=(SELECT num from Logs limit 0,1)
  ) r
) a
where a.n>=3

簡單分析一下,作者通過定義兩個變量recordcount來控制記錄和對應的rank值,首先通過一個select @count:=0,@record:=(SELECT num from Logs limit 0,1)語句來初始化這兩個變量count=0record=表裡第一條記錄的num。接下來通過普通查詢,將Logs表裡每一條記錄查出來,和record對比,如果相同,則count自增1,如果不同,那麼新的record被賦值,同時count1,很漂亮的自定義變量用sql實現了我們直覺上需要用邏輯代碼來完成的功能。而且這個代碼的一大優勢是不需要用到Id字段~~非常棒

2015/1/16

about sql injection


實例講解 SQL 注入攻擊

 http://blog.jobbole.com/83092/


一位客戶讓我們針對只有他們企業員工和顧客能使用的企業內網進行滲透測試。這是安全評估的一個部分,所以儘管我們之前沒有使用過SQL注入來滲透網絡,但對其概念也相當熟悉了。最後我們在這項任務中大獲成功,現在來回顧一下這個過程的每一步,將它記錄為一個案例。
「SQL注入」是一種利用未過濾/未審核用戶輸入的攻擊方法(「緩存溢出」和這個不同),意思就是讓應用運行本不應該運行的SQL代碼。如果應用毫無防備地創建了SQL字符串並且運行了它們,就會造成一些出人意料的結果。
我們記錄下了在多次錯誤的轉折後經歷的曲折過程,而一個更有經驗的人會有這不同的 — 甚至更好的 — 方法。但事實上我們成功以後才明白,我們並沒有完全被誤導。
其他的SQL文章包含了更多的細節,但是這篇文章不僅展示了漏洞利用的過程,還講述了發現漏洞的原理。

目標內網

展現在我們眼前的是一個完整定製網站,我們之前沒見過這個網站,也無權查看它的源代碼:這是一次「黑盒」攻擊。『刺探』結果顯示這台服務器運行在微 軟的IIS6上,並且是ASP.NET架構。這就暗示我們數據庫是微軟的SQL server:我們相信我們的技巧可以應用在任何web應用上,無論它使用的是哪種SQL 服務器。
登陸頁有傳統的用戶-密碼表單,但多了一個 「把我的密碼郵給我」的鏈接;後來,這個地方被證實是整個系統陷落的關鍵。
當鍵入郵件地址時,系統假定郵件存在,就會在用戶數據庫裡查詢郵件地址,然後郵寄一些內容給這個地址。但我的郵件地址無法找到,所以它什麼也不會發給我。
對於任何SQL化的表單而言,第一步測試,是輸入一個帶有單引號的數據:目的是看看他們是否對構造SQL的字符串進行了過濾。當把單引號作為郵件地址提交以後,我們得到了500錯誤(服務器錯誤),這意味著「有害」輸入實際上是被直接用於SQL語句了。就是這了!
我猜測SQL代碼可能是這樣:
1
2
3
SELECT fieldlist
  FROM table
 WHERE field = '$EMAIL';
$EMAIL 是用戶從表單提交的地址,並且這段查詢在字符串末端$EMAIL上提供了引號。我們不知道字段或表的確切名字,但是我們瞭解他們的本質,這有助於我們做正確的猜測。
當我們鍵入steve@unixwiz.net『 -注意這個末端的引號 – 下面是這個SQL字段的構成:
1
2
3
SELECT fieldlist
  FROM table
 WHERE field = 'steve@unixwiz.net'';
當這段SQL開始執行,SQL解析器就會發現多餘的引號然後中斷執行,並給出語法錯誤的提示。這個錯誤如何清楚的表述給用戶,基於應用內部的錯誤恢 復規程,但一般來說都不會提示「郵件地址不存在」。這個錯誤響應成了死亡之門,它告訴別人用戶輸入沒有被正確的處理,這就為應用破解留下了可乘之機。
這個數據呈現在WHERE的從句中,讓我們以符合SQL規範的方式改變輸入試試,看看會發生什麼。鍵入anything' OR 『x'=『x, 結果如下:
1
2
3
SELECT fieldlist
  FROM table
 WHERE field = 'anything' OR 'x'='x';
因為應用不會思考輸入 – 僅僅構造字符串 - 我們使用單引號把WHERE從句的單一組成變成了雙組成,』x'=『x從句是恆成立的,無論第一個從句是什麼。(有一種更好的方式來確保「始終為真」,我們隨後會接觸到)。

但與每次只返回單一數據的「真實」查詢不同,上面這個構造必須返回這個成員數據庫的所有數據。要想知道在這種情況下應用會做什麼,唯一的方法就是嘗試,嘗試,再嘗試。我們得到了這個:

你的登錄信息已經被郵寄到了 random.person@example.com.

我們猜測這個地址是查詢到的第一條記錄。這個傢伙真的會在這個郵箱裡收到他忘記的密碼,想必他會很吃驚也會引起他的警覺。
我們現在知道可以根據自己的需要來篡改查詢語句了,儘管對於那些看不到的部分還不夠瞭解,但是我們注意到了在多次嘗試後得到了三條不同的響應:
  • 「你的登錄信息已經被郵寄到了郵箱」
  • 「我們不能識別你的郵件地址」
  • 服務器錯誤
前兩個響應是有效的SQL,最後一個響應是無效的SQL:當猜測查詢語句結構的時候,這種區別非常有用。

模式字段映射

第一步是猜測字段名:我們合理的推測了查詢包含「email address」和「password」,可能也會有「US Mail address」或者「userid」或「phone number」這樣的字段。我們特別想執行 SHOW TABLE語句, 但我們並不知道表名,現在沒有比較明顯的辦法可以拿到表名。
我們進行了下一步。在每次測試中,我們會用我們已知的部分加上一些特殊的構造語句。我們已經知道這個SQL的執行結果是email地址的比對,因此我們來猜測email的字段名:
1
2
3
SELECT fieldlist
  FROM table
 WHERE field = 'x' AND email IS NULL; --';
目的是假定的查詢語句的字段名(email),來試試SQL是不是有效。我不關心匹配的郵件地址是啥(我們用了個偽名』x'),——』這個符號表示SQL註釋的起始。對於去除應用末尾提供的引號,這是一個很有效的方式,我們不用在乎我們屏蔽掉的是啥。
如果我們得到了服務器錯誤,意味著SQL有不恰當的地方,並且語法錯誤會被拋出:更有可能是字段名有錯。如果我們得到了任何有效的響應,我們就可以 猜測這個字段名是正確的。這就是我們得到「email unknown」或「password was sent」響應的過程。
我們也可以用AND連接詞代替OR:這是有意義的。在SQL的模式映射階段,我們不需要為猜一個特定的郵件地址而煩惱,我們也不想應用隨機的氾濫的 給用戶發「這是你的密碼」的郵件 - 這不太好,有可能引起懷疑。而使用AND連接郵件地址,就會變的無效,我們就可以確保查詢語句總是返回0行,永遠不會生成密碼提醒郵件。
提交上面的片段的確給了我們「郵件地址未知」的響應,現在我們知道郵件地址的確是存儲在email字段名裡。如果沒有生效,我們可以嘗試email_address或mail這樣的字段名。這個過程需要相當多的猜測。
接下來,我們猜測其他顯而易見的名字:password,user ID, name等等。每次只猜一個字段,只要響應不是「server failure」,那就意味著我們猜對了。
1
2
3
SELECT fieldlist
  FROM table
 WHERE email = 'x' AND userid IS NULL; --';
在這個過程中,我們找到了幾個正確的字段名:
  • email
  • passwd
  • login_id
  • full_name
無疑還有更多(有一個線索是表單中的字段名),一陣挖掘後沒有發現更多了。但是我們依然不知道這些字段名的表名,它們在哪找到的?
尋找數據庫表名
應用的內建查詢指令已經建立了表名,但是我們不知道是啥:有幾個方法可以找到表名。其中一個是依靠subselect(字查詢)。
一個獨立的查詢
1
SELECT COUNT(*) FROM tabname
返回表裡記錄的數量,如果表名無效,查詢就會失敗。我們可以建立自己的字符串來探測表名:
1
2
3
SELECT email, passwd, login_id, full_name
  FROM table
 WHERE email = 'x' AND 1=(SELECT COUNT(*) FROM tabname); --';
我們不關心到底有多少條記錄,只關心表名是不是正確。重複多次猜測以後,我們終於發現members是這個數據庫裡的有效表名。但它是用在這個查詢裡的麼?所以我們需要另一個測試,使用table.field:實際查詢的部分只工作在這個表中,而不是只要表存在就執行。
1
2
3
SELECT email, passwd, login_id, full_name
  FROM members
 WHERE email = 'x' AND members.email IS NULL; --';
當返回「Email unknown」時,就意味著我們的SQL注入成功了,並且我們正確的猜測出了表名。這對後面的工作很重要,但是我們暫時先試試其他的方法。
找用戶賬號
我們對members表的結構有了一個局部的概念,但是我們僅知道一個用戶名:任意用戶都可能得到「Here is your password」的郵件。回想起來,我們從未得到過信息本身,只有它發送的地址。我們得再弄幾個用戶名,這樣就能得到更多的數據。
首先,我們從公司網站開始找幾個人:「About us」或者「Contact」頁通常提供了公司成員列表。通常都包含郵件地址,即使它們沒有提供這個列表也沒關係,我們可以根據某些線索用我們的工具找到它們。
LIKE從句可以進行用戶查詢,允許我們在數據庫裡局部匹配用戶名或郵件地址,每次提交如果顯示「We sent your password」的信息並且郵件也真發了,就證明生效了。
警告:這麼做拿到了郵件地址,但也真的發了郵件給對方,這有可能引起懷疑,小心使用。
我們可以查詢email name或者full name(或者推測出來的其他信息),每次放入%通配符進行如下查詢:
1
2
3
SELECT email, passwd, login_id, full_name
  FROM members
 WHERE email = 'x' OR full_name LIKE '%Bob%';
記住儘管可能不只有一個「Bob」,但我們只能看到一條信息:建議精煉LIKE從句。
密碼暴力破解
可以肯定的是,我們能在登陸頁進行密碼的暴力破解,但是許多系統都針對此做了監測甚至防禦。可能有的手段有操作日誌,帳號鎖定,或者其他能阻礙我們行動的方式,但是因為存在未過濾的輸入,我們就能繞過更多的保護措施。
我們在構造的字符串裡包含進郵箱名和密碼來進行密碼測試。在我們的例子中,我們用了受害者bob@example.com 並嘗試了多組密碼。
1
2
3
SELECT email, passwd, login_id, full_name
  FROM members
 WHERE email = '<a href="mailto:bob@example.com">bob@example.com</a>' AND passwd = 'hello123';
這是一條很好使的SQL語句,我們不會得到服務器錯誤的提示,只要我們得到「your password has been mailed to you」的提示信息,就證明我們已經得到密碼了。這時候受害人可能會警覺起來,但誰關心他呢,我們已經得到密碼了。
這個過程可以使用perl腳本自動完成,然而,我們在寫腳本的過程中,發現了另一種方法來破解系統。

數據庫不是只讀的

迄今為止,我們沒做查詢數據庫之外的事,儘管SELECT是只讀的,但不代表SQL只能這樣。SQL使用分號表示結束,如果輸入沒有正確過濾,就沒有什麼能阻止我們在字符串後構造與查詢無關的指令。
The most drastic example is:
這劑猛藥是這樣的:
1
2
3
SELECT email, passwd, login_id, full_name
  FROM members
 WHERE email = 'x'; DROP TABLE members; --';  -- Boom!
第一部分我們準備了一個偽造的email地址——『X'——我們不關心查詢結果返回什麼:我們想要得到的只是我們自己構造的SQL指令。這次攻擊刪除了整個members表,這就不太好玩了。
這表明我們不僅僅可以切分SQL指令,而且也可以修改數據庫。這是被允許的。

添加新用戶

我們已經瞭解了members表的局部結構,添加一條新紀錄到表裡視乎是一個可行的方法:如果這成功了,我們就能簡單的用我們新插入的身份登陸到系統了。
不要太驚訝,這條SQL有點長,我們把它分行顯示以便於理解,但它依然是一條語句:
1
2
3
4
5
SELECT email, passwd, login_id, full_name
  FROM members
 WHERE email = 'x';
        INSERT INTO members ('email','passwd','login_id','full_name')
        VALUES ('steve@unixwiz.net','hello','steve','Steve Friedl');--';
即使我們得到了正確的字段名和表名,但在成功攻擊之前我們還有幾件事需要瞭解:
  1. 在web表單裡,我們可能沒有足夠的空間鍵入這麼多文本(儘管可以用腳本解決,但並不容易)。
  2. web應用可能沒有members表的INSERT權限。
  3. 母庸置疑,members表裡肯定還有其他字段,有一些可能需要初始值,否則會引起INSERT失敗。
  4. 即使我們插入了一條新紀錄,應用也可能不正常運行,因為我們無法提供值的字段名會自動插入NULL。
  5. 一個正確的「member」可能額不僅僅只需要members表裡的一條紀錄,而還要結合其他表的信息(如,訪問權限),因此只添加一個表可能不夠。
在這個案例裡,我們遇到了問題#4或#5,我們無法確定到底是哪個—— 因為用構造好的用戶名登陸進去的時候,返回了服務器錯誤的提示。儘管這就暗示了我們那些沒有構造的字段是必須的,但我們沒有辦法正確處理。
一個可行的辦法是猜測其他字段,但這是一個勞力費神的過程:儘管我們可以猜測其他「顯而易見」的字段,但要想得到整個應用的組織結構圖太難了。
我們最後嘗試了其他方式。
把密碼郵給我
我們意識到雖然我們無法添加新紀錄到members數據庫裡,但我們可以修改已經存在的,這被證明是可行的。
從上一步得知 bob@example.com 賬戶在這個系統裡,我們用SQL注入把數據庫中的這條記錄改成我們自己的email地址:
1
2
3
4
5
6
SELECT email, passwd, login_id, full_name
  FROM members
 WHERE email = 'x';
      UPDATE members
      SET email = <a href="mailto:'steve@unixwiz.net">'steve@unixwiz.net</a>'
      WHERE email = <a href="mailto:'bob@example.com">'bob@example.com</a>';
運行之後,我們自然得到了「we didn't know your email address」的提示,但這在預料之中,畢竟我們用了假的email地址。UPDATE操作不會通知應用,因此它悄然執行了。
之後,我們使用了「I lost my password」的功能,用我們剛剛更新的email地址,一分鐘後,我們收到了這封郵件:
1
2
3
4
5
6
7
From: <a href="mailto:system@example.com">system@example.com</a>
To: <a href="mailto:steve@unixwiz.net">steve@unixwiz.net</a>
Subject: Intranet login
 
This email is in response to your request for your Intranet log in information.
Your User ID is: bob
Your password is: hello
現在,我們要做的就是跟隨標準的登錄流程進入系統,這是一個高等級職員,有高級權限,比我們INSERT的用戶要好。
我們發現這個企業內部站點內容特別多,甚至包含了一個全用戶列表,我們可以合理的推出許多內網都有同樣的企業Windows網絡帳號,它們可能在所 有地方都使用同樣的密碼。我們很容易就能得到任意的內網密碼,並且我們找到了企業防火牆上的一個開放的PPTP協議的VPN端口,這讓登錄測試變得更簡 單。
我們又挑了幾個帳號測試都沒有成功,我們無法知道是否是「密碼錯誤」或者「企業內部帳號是否與Windows帳號名不同」。但是我們覺得自動化工具會讓這項工作更容易。

其他方法

在這次特定的滲透中,我們得到了足夠的權限,我們不需要更多了,但是還有其他方法。我們來試試我們現在想到的但不夠普遍的方法。
我們意識到不是所有的方法都與數據庫無關,我們可以來試試。
調用xp_cmdshell
微軟的SQLServer支持存儲過程xp_cmdshell有權限執行任意操作系統指令。如果這項功能允許web用戶使用,那webserver被滲透是無法避免的。
迄今為止,我們做的都被限制在了web應用和數據庫這個環境下,但是如果我們能執行任何操作系統指令,再厲害的服務器也禁不住滲透。xp_cmdshell通常只有極少數的管理員賬戶才能使用,但它也可能授權給了更低級的用戶。
繪製數據庫結構
在這個登錄後提供了豐富功能應用上,已經沒必要做更深的挖掘了,但在其他限制更多的環境下可能還不夠。
能夠系統的繪製出數據庫可見結構,包含表和它們的字段結構,可能沒有直接幫助。但是這為網站滲透提供了一條林萌大道。
從網站的其他方面收集更多有關數據結構的信息(例如,「留言板」頁?「幫助論壇」等?)。不過這對應用環境依賴強,而且還得靠你準確的猜測。
減輕危害
我們認為web應用開發者通常沒考慮到「有害輸入」,但安全人員應該考慮到(包括壞傢伙),因此這有3條方法可以使用。
輸入過濾
過濾輸入是非常重要的事,以確保輸入不包含危險代碼,無論是SQL服務器或HTM本身。首先想到的是剝掉「惡意字符」,像引號、分號或轉義符號,但這是一種不太好的方式。儘管找到一些危險字符很容易,但要把他們全找出來就難了。
web語言本身就充滿了特殊字符和奇怪的標記(包括那些表達同樣字符的替代字符),所以想要努力識別出所有的「惡意字符」不太可能成功。
換言之,與其「移除已知的惡意數據」,不如移除「良好數據之外的所有數據」:這種區別是很重要的。在我們的例子中,郵件地址僅能包含如下字符:
1
2
3
4
abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ
0123456789
@.-_+
允許不正確的字符輸入是沒有任何好處的,應該早點拒絕它們 - 可能會有一些錯誤信息 – 不僅可以阻止SQL注入,也可以捕獲一些輸入錯誤而不是把它們存入數據庫。
某個特殊的email地址會讓驗證程序陷入麻煩,因為每個人對於「有效」的定義不同。由於email地址中出現了一個你沒有考慮到的字符而被拒絕,那真是糗大了。
真正的權威是RFC 2822(比RFC822內容還多),它對於」允許使用的內容「做了一個規範的定義。這種更學術的規範希望可以接受&和*(還有更多)作為有效的email地址,但其它人 - 包括作者 – 都樂於用一個合理的子集來包含更多的email地址。
那些採用更限制方法的人應當充分意識到沒有包含這些地址會帶來的後果,特別是限制有了更好的技術(預編譯/執行,存儲過程)來避免這些「奇怪」的字符帶來的安全問題。
意識到「過濾輸入」並不意味著僅僅是「移除引號」,因為即使一個「正規」的字符也會帶來麻煩。在下面這個例子中,一個整型ID值被拿來和用戶的輸入作比較(數字型PIN):
1
2
3
SELECT fieldlist
  FROM table
 WHERE id = 23 OR 1=1;  -- Boom! Always matches!
在實踐中,無論如何這個方法都有諸多限制,因為能夠徹底排除所有危險字符的字段實在太少了。對於「日期」或者「email地址」或者「整型」,上面的辦法是有價值的,但對於真實的環境,我們不可避免地要使用其他方式來減輕危害。
輸入項編碼/轉義
現在可以過濾電話號碼和郵件地址了,但你不能通過同樣的方法處理「name」字段,要不然可能會排除掉Bill O'Reilly這樣的名字:對於這個字段,這裡的引號是合法的輸入。
有人就想到過濾到單引號的時候,再加上一個引號,這樣就沒問題了 – 但是這麼幹要出事啊!
預處理每個字符串來替換單引號:
1
2
3
SELECT fieldlist
  FROM customers
 WHERE name = 'Bill O''Reilly'-- works OK
這個方法很容易出問題,因為大部分數據庫都支持轉碼機制。像MySQL,允許輸入』來替代單引號,因此如果輸入 『; DROP TABLE users;  時,通過兩次引號來「保護」數據庫,那我們將得到:
1
2
3
SELECT fieldlist
  FROM customers
 WHERE name = '\''; DROP TABLE users; --';  -- Boom!
『』』 是 一個完整的SQL語句(只包含一個引號),通常,惡意SQL代碼就會緊跟其後。不光是反斜線符號的情況:像Unicode編碼,其他的編碼或者解析規則都 會無意中給程序員挖坑。完美的過濾的很困難的,這就是為什麼許多的數據庫藉口語言都提供函數給你使用。當同樣的內容給「string quoting」和「string parsing」處理過後,會好一些,也更安全一些。
比如MySQL的函數mysql_real_escape_string()和perl DBD 的 $dbh->quote($value)方法,這些方法都是必用的。
參數綁定 (預編譯語句)
儘管轉義是一個有用的機制,但我們任然處於「用戶輸入被當做SQL語句」這麼一個循環裡。更好的方法是:預編譯,本質上所有的數據庫編程接口都支持預編譯。技術上來說,SQL聲明語句是用問號給每個參數佔位創建的 – 然後在內部表中進行編譯。
預編譯查詢執行時是按照參數列表來的:
Perl中的例子
1
2
3
$sth = $dbh->prepare("SELECT email, userid FROM members WHERE email = ?;");
 
$sth->execute($email);
感謝Stefan Wagner幫我寫了個java實現:
不安全版
1
2
3
Statement s = connection.createStatement();
ResultSet rs = s.executeQuery("SELECT email FROM member WHERE name = "
                             + formField); // *boom*
安全版
1
2
3
4
PreparedStatement ps = connection.prepareStatement(
    "SELECT email FROM member WHERE name = ?");
ps.setString(1, formField);
ResultSet rs = ps.executeQuery();
$email 是從用戶表單獲得的,它作為#1(第一個問號標記的地方)位置的參數傳遞過來,在任何情況下這條SQL聲明都可以解 析。引號,分號,反斜槓,SQL指令記號 – 任何字符都不會產生特殊效果,因為它們「只是數據」而已。這不會對其他東西造成破壞,因此這個應用很大程度上防範了SQL注入攻擊。
如果預編譯查詢語句多次(只編譯一次)執行,也會帶來性能上的提升,但是與大量安全方面的巨大提升相比,這顯得微不足道。這可能是我們保證web應用安全最重要的一步。
限制數據庫權限和隔離用戶
在這個案例中,我們觀察到只有兩個交互動作不在登錄用戶的上下文環境中:「登錄」和「發密碼給我」。web應用應該對數據庫連接做權限的限制:對於members表只能讀,並且無法操作其他表。
作用是即使一次「成功的」SQL注入攻擊也只能得到非常有限的成功。噢,我們將不能做有授權的UPDATE請求,我們要求助於其他方法。
一旦web應用確定登錄表單傳遞來的認證是有效的,它就會切換會話到一個有更多權限的用戶上。
對任何web應用而言,不使用sa權限幾乎是根本不用說的事。
對數據庫的訪問採用存儲過程
如果數據庫支持存儲過程,請使用存儲過程來執行數據庫的訪問行為,這樣就不需要SQL了(假設存儲過程編程正確)。
把查詢,更新,刪除等動作規則封裝成一個單獨的過程,就可以針對基礎規則和所執行的商業規則來完成測試和歸檔(例如,如果客戶超過了信用卡限額,「添加新記錄」過程可能拒絕訂單)。
對於簡單的查詢這樣做可能僅僅能獲得很少的好處,不過一旦操作變複雜(或者被用在更多地方),給操作一個單獨的定義,功能將會變得更穩健也更容易維護。
注意:動態構建一個查詢的存儲過程是可以做到的:這麼做無法防止SQL注入 – 它只不過把預編譯/執行綁定到了一起,或者是把SQL語句和提供保護的變量綁定到了一起。
隔離web服務器
實施了以上所有的防禦措施,仍然可能有某些地方有遺漏,導致了服務器被滲透。設計者應該在假定壞蛋已經獲得了系統最高權限下來設計網絡設施,然後把它的攻擊對其他事情產生的影響限制在最小。
例如,把這台機器放置在極度限制出入的DMZ網絡「內部」,這麼做意味著即便取得了web服務器的完全控制也不能自動的獲得對其他一切的完全訪問權限。當然,這麼做不能阻止所有的入侵,不過它可以使入侵變的非常困難。
配置錯誤報告
一些框架的錯誤報告包含了開發的bug信息,這不應該公開給用戶。想像一下:如果完整的查詢被現實出來了,並且指出了語法錯誤點,那要攻擊該有多容易。
對於開發者來說這些信息是有用的,但是它應該禁止公開 – 如果可能 - 應該限制在內部用戶訪問。
注意:不是所有的數據庫都採用同樣的方式配置,並且不是所有的數據庫都支持同樣的SQL語法(「S」代表「結構化」,不是「標準的」)。例如,大多 數版本的MySQL都不支持子查詢,而且通常也不允許單行多條語句(multiple statements):當你滲透網絡時,實際上這些就是使問題複雜化的因素。

再強調一下,儘管我們選擇了「忘記密碼」鏈接來試試攻擊,但不是因為這個功能不安全。而是幾個易攻擊的點之一,不要把焦點聚集在「忘記密碼」上。
這個教學示例不準備全面覆蓋SQL注入的內容,甚至都不是一個教程:它僅僅是一篇我們花了幾小時做的滲透測試的記錄。我們看了其他的關於SQL注入文章的討論,但它們只給出了結果而沒有給出過程。
但是那些結果報告需要技術背景才能看懂,並且滲透細節也是有價值的。在沒有源代碼的情況下,滲透人員的黑盒測試能力也是有價值的。
感謝 David Litchfield 和 Randal Schwartz對本文的貢獻,還有Chris Mospaw的排版(© 2005 by Chris Mospaw, used with permission).

其他資源

關於作者: zer0Black@lxtalx

關注信息安全,網絡安全,目前為移動開發工程師,android和IOS兼有涉獵。半路出道,基礎薄弱,正努力補習計算機基礎中。近日習得「遍歷」學習法,正欲嘗試之。(新浪微博:@Zer0Black
查看zer0Black的更多文章 >>

2014/6/6

SQL性能小測試得出的驚人結果:60%失敗

這篇文章放了將近2個月才仔細閱讀
結果:我勉強算答對一題
保留起來。以後隨時複習

=============================================================

http://blog.jobbole.com/60800/

本文由 伯樂在線 - sunbiaobiao 翻譯自 MarkusWinand。歡迎加入技術翻譯小組。轉載請參見文章末尾處的要求。


2011年,我開展了「3分鐘測試你對SQL性能知道多少?」的測試活動。其中包含五個問題,它們是這樣的:每個問題有一個query/index查詢,問你這樣是否正確使用了索引。至今, 這個測試已經成了 Use The Index, Luke網站上的一個熱點。這個測試已經被回答了28,000次。
提醒一下:也許你不想被我劇透,你可以提前自己測試一下自己。
儘管這個測試是為了教育,我很好奇自己是否可以從中找到一些規律,我認為可以的。當你看這些結果時,要記住幾點,第一,這些測試因為很出人意料才惹 人眼球,也就是說,有的測試看著性能很高,其實性能不高。有的反之。只有一個問題答案符合你的第一印象。很有意思的是,這個測試並不知道參與者是誰,所有 人都可以參與,為了獲得一個好的分數,你也可以再來一遍。要曉得這個測試不是為了對索引進行科學研究。然而,我認為結果仍可以給人一些啟示。
下面我對每個問題展示兩個不同的統計圖。第一,每個問題平均沒正確回答多少次。第二,對於MySQL, Oracle, PostgreSQL 和 SQL Server統計數據有什麼不同。也就是說,是否MySQL 使用者會比PostgreSQL 使用者更懂索引呢?我很幸運獲得這樣的統計數據,原因是不同的數據庫提供商有自己獨特的語法定義。像MySQL和PostgreSQL 中的 LIMIT  到了SQL Server中就成了 TOP。因此參與者開始時要選擇一種數據庫,問題是針對所選數據庫的。
問題一:WHERE語句中的函數
從性能上來看,下面的SQL語句是好的實踐嗎?
查詢出所有2012年的行:
1
2
3
4
5
CREATE INDEX tbl_idx ON tbl (date_column);
 
SELECT text, date_column
  FROM tbl
 WHERE TO_CHAR(date_column, 'YYYY') = '2012';
這個例子 SQL語句使用了Oracle和PostgreSQL  的特有函數,在MYSQL中這個問題就使用YEAR(date_column),在SQL SERVER中則為datepart(yyyy, date_column)。當然我可以使用EXTRACT(YEAR date_column),但我覺得還是使用通用一點的語法好一點。
參與者有兩個選項:
  •  好的實踐 ,沒有大的性能改進可以採用了
  •  壞的實踐,有大的性能改進可以採用
答案是「壞實踐」,原因是雖然在date_column上有索引,但 是在date_column字段上加了函數以後,索引就失效了。你如果不信,你可以看看一些可以證明我的結論的腳本和最後的解釋說明。詳細的解釋都在 Use The Index, Luke網站的相關頁面上。
如果你不知道在字段上加函數時怎麼吧索引的功能給抹殺了,很多人都和你一樣。只有2/3的人給出了正確答案。算上有些人選了兩次,有些人是蒙的。這樣說來差不多只有一半的人答對,無疑是很少的。我用下面這張圖強調一下
q3_tofefetal_20140212
這是我平時工作中最常見的一個問題,當你在VARCHAR 類型的字段上使用UPPERTRIM等函數時同樣會碰到這個問題。請記住,當你對WHERE語句中使用的字段加上函數的時候,它的索引功能就失去了作用。
儘管這個結果很令人失望——只比隨便碰對的概率高17%,但這都沒讓我感到驚奇。讓我驚奇的是在不同數據庫使用者中結果的不同。
          q1_bydb_20140212
實施上 MYSQL使用者只得到了55%的分數——就像純粹蒙一樣低。PostgreSQL 使用者卻獲得了83%的分數。
也許產生這個結果的原因是MYSQL不支持function-based indexes而Oracle 和 PostgreSQL支持。Function-based 索引允許你使用索引表達式像TO_CHAR(date_column, 『YYYY'),雖然對這個測試來說,這樣做不是推薦的解決方案。但僅僅是這個特性的存在讓Oracle 和 PostgreSQL使用者對這個問題更有意識。SQL Server提供了類似的特性,雖然不能直接使用索引表達式,但是你可以創建所謂的computed column,這個列是可以被索引的。
雖然以上可以解釋為什麼MySQL 使用者的效率比較低,但這不是藉口。不管支持function-based indexes與否,那個 query/index句子總之效率很低。很有效果的改進是不在索引字段上使用函數:
1
2
3
4
SELECT text, date_column
  FROM tbl
 WHERE date_column >= TO_DATE('2012-01-01', 'YYYY-MM-DD')
   AND date_column <  TO_DATE('2013-01-01', 'YYYY-MM-DD');
索引字段不必改變。這種解決方案很靈活,因為它支持廣泛的類型——星期或月份。這是我推薦的解決方案。
我很好奇,我想知道那些正確回答問題的人怎麼在function-based索引上考慮復合索引。我最好把這種回答認為是正確了一半。
問題二:索引過之後的TOP-N查詢
從性能上來看是好的實踐還是壞的實踐?
按時間遠近排行:
1
2
3
4
5
6
7
CREATE INDEX tbl_idx ON tbl (a, date_column);
 
SELECT id, a, date_column
  FROM tbl
 WHERE a = ?
 ORDER BY date_column DESC
 LIMIT 1;
注意,那個問號是個佔位符。因為我經常推薦開發者使用綁定變量。
參與者有兩個選項:
  •  好的實踐 ,沒有大的性能改進可以採用了
  •  壞的實踐,有大的性能改進可以採用
這個問題看著有性能危險,但其實不是。一般看來order by一定會對數據排序,然而這個索引,使你沒有沒有必要對整個數據集排序,所以它就像查詢唯一索引鍵一樣快。
正確率接近與 「隨便蒙」 ,我認為人們對這個問題基本上沒有概念。
             q2_total_20140212
這個結果讓人難以接受,  我看到人們平時建立緩存表,恰恰為了避免我們介紹這種查詢,經常被計劃任務填滿。有趣的是這種日常任務經常引起性能問題,因為它需要在很小的時間間隔內確認緩存表中是否存在新的數據。然而,正確的索引應該是你的第一選擇。
q2_bydb_20140212
這裡,我要提一下Oracle 數據庫使用者要特別注意一下這個技巧。到12c 版本的Oracle數據庫仍然沒有提供像LIMIT or TOP等便利的語法糖。你可以使用ROWNUM的偽式的數據列。
1
2
3
4
5
6
7
8
SELECT *
  FROM (
        SELECT id, date_column
          FROM tbl
         WHERE a = :a
         ORDER BY date_column DESC
       )
 WHERE rownum <= 1;
這個多餘的複雜度讓Oracle使用者得到了錯誤的結果,比「隨便蒙」對的概率還低。 
對於這個問題回饋的另一個爭論是如果包含ID列將允許 index-only scan, 儘管這是正確的,但我不認為不這樣做就是一個「壞實踐」。因為查詢的只有一行。index-only scan可以避免單表訪問,很多情況下你可以使用它提高性能,但一般情況下我認為這是一種過早優化,這是只是我的觀點。但這個爭論可以讓我們看到 PostgreSQL 使用者獲得最好的分數。PostgreSQL直到9.2版本才有index-only scans。在2012年九月才發佈這個特性。因此PostgreSQL 沒有掉入認為只有index-only scan才能提高性能的陷阱。
問題三:索引列的順序
從性能上來看是好的實踐還是壞的實踐
兩個查詢語句:
1
2
3
4
5
6
7
8
9
10
CREATE INDEX tbl_idx ON tbl (a, b);
 
SELECT id, a, b
  FROM tbl
 WHERE a = ?
   AND b = ?;
 
SELECT id, a, b
  FROM tbl
 WHERE b = ?;
參與者有兩個選項:
  •  好的實踐 ,沒有大的性能改進可以採用了
  •  壞的實踐,有大的性能改進可以採用
答案是壞實踐,因為第二個查詢語句沒有正確地使用索引。把索引列的順序改為(b, a)可以使兩個查詢語句都能使用索引從而獲得很高的性能。在b上再加一個索引,從而無緣無故的帶來了很大的性能開銷。不幸地是我看到很多人都這麼做。
結果是令人失望的,但是我已經猜到了。比「隨便蒙」只高12.5% 。
q3_tofefetal_20140212
這也是一個我每天都遇到的問題,人們就是不知道復合索引是怎麼工作的。
q2_bydb_20140212
不同數據庫的使用者的回答很接近,可能是因為(不同數據庫)沒有很大語法區別和的特性影響回答的結果。Oracle的不為人知的Skip Scan特性有很小的影響。通常來講index-only  scan 的意識可能有影響,但這次它的影響是讓參與者更有可能回答對問題。
總之,統計表明,一些數據庫的使用者比另一下更瞭解索引。有趣的是PostgreSQL 使用者第三次獲得最高分。
問題四:模糊查詢
從性能上來看是好的實踐還是壞的實踐?
查詢一個句子:
1
2
3
4
5
CREATE INDEX tbl_idx ON tbl (text);
 
SELECT id, text
  FROM tbl
 WHERE text LIKE '%TERM%';
我這次給出了不一樣的答案:
  • 銀彈 ,總是運行的很快
  • 噩夢,有性能危險
正確答案是噩夢因為匹配符中使用了前綴通配符,反之如果使用匹配符「TERM%」就會更有效率。大部分人都能回答對這個問題。我可以說大部分人還是知道LIKE 不是用來全文搜索的。
q4_total_20140212
這個與眾不同的結果各種數據庫使用者的正確率相差無幾。
q4_byergfewgfdb_20140212
這一次PostgreSQL 使用者不是那麼牛逼了。我們仔細審視一下PostgreSQL 面對的問題就知道為什麼了。
1
2
3
4
5
CREATE INDEX tbl_idx ON tbl (text varchar_pattern_ops);
 
SELECT id, text
  FROM tbl
 WHERE text LIKE '%TERM%';
注意我們對索引字段的補充修飾(varchar_pattern_ops),在PostgreSQL中這個操作符類使的索引對後綴通配符無效。我加 上這個是想知道人們是否意識到在模糊查詢是前綴通配符會帶來問題。沒有操作符類,它不工作有兩個原因:(1)前綴通配符;(2)沒有操作符類,我認為這是 顯然的。
問題五a  Index-only  scan
第五個問題有點棘手,因為在這個測試開始時,PostgresSQL不支持 index-only scans。 因此我稍微調整,兩組的這個問題不一樣。 MySQL, Oracle and SQL Server中是關於index-only  scan。另一個是針對PostgresSQL 使用者出的關於索引列的順序問題。我把結果都展示在這裡。先看關於index-only scans:的問題。
從第一個到第二個查詢性能會怎麼改變?
從一百萬行中選出一百行:
1
2
3
4
5
6
CREATE INDEX tab_idx ON tbl (a, date_column);
 
SELECT date_column, count(*)
  FROM tbl
 WHERE a = 123
 GROUP BY date_column;
從一百萬行中選出十行
1
2
3
4
5
SELECT date_column, count(*)
  FROM tbl
 WHERE a = 123
   AND b = 42
 GROUP BY date_column;
這個問題有點不同,因為我給了四個答案:
  • 查詢性能大體相同
  • 依賴數據的不同
  • 查詢會變很慢(影響>10%)
  • 查詢會變很快(影響>10%)
在我出這個測試的時候,我十分曉得五五分的答案沒有什麼意義,要在讓參與者快速抓住要點並回答和給出準確答案之間做權衡。
簡單來說,正確答案是查詢會變的很慢,因為原來的查詢使用了index-only scan,這個查詢只使用了索引中的數據就能給出答案而不需要到實際的表中獲取數據。第二個查詢需要檢查數據列B,而數據列B不在索引中,因此數據庫要花 費多餘的開銷到拿出候選的行來判斷是否符合條件,它要從表中取出100行,這正是第一個查詢中要返回的數據行數。因為有group by操作,估計要取出更多的數據行,會使查詢變的很慢。
因為有多個選項,總體分數明顯下降,掉到了比「隨便蒙」低39% or 14%。
q2_bydb_20140212
我會說有39%的參與者知道正確答案這個結論是錯誤的,它們雖然給出了正確答案,但是我估計有25% 的人是蒙的。
分開各種數據庫使用者後,結果更是無聊。
q5_by3db_20140212
但是,我們仍然要看一下人們是怎麼回答的:
q5_byanswer_20140212
我非常吃驚,「大體相同」 和 「依賴具體的數據」這兩個選項都獲得了25%的選擇——它們可能都是猜的。這是否表明一半的參與者只是在胡亂猜。還是因為這是最後一個問題,很多人都想快 點做完看看答案,恩,很有可能。然而正確答案「會變的很慢」獲得了38.8%的選擇,導致只有10.9%的人選擇「會變的很快」選項。
我的本意是把人誤導選擇「會變的很快」,因為後者數據量更少——只有使用了index-only scan的情況下會變得不同,但是我假設我得到這個結果是因為人們通常會認為很明顯的答案肯定是錯的。這樣的話,我想驗證多少人會知道index- only scan的本意根本沒有得到證明。
問題5b:索引列順序和範圍操作符
這個問題只是給PostgreSQL 使用者的。
從性能上來看是好的實踐還是壞的實踐?
查詢狀態的X並且不超過五年的實體。
1
2
3
4
5
6
7
8
CREATE INDEX tbl_idx ON tbl (date_column, state);
 
SELECT id, date_column, state
  FROM tbl
 WHERE date_column >= CURRENT_DATE - INTERVAL '5' YEAR
   AND state = 'X';
 
(365 rows)
數據分佈如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
SELECT count(*)
  FROM tbl
 WHERE date_column >= CURRENT_DATE - INTERVAL '5' YEAR;
 
 count
-------
  1826
 
SELECT count(*)
  FROM tbl
 WHERE state = 'X';
 
 count
-------
 10000
參與者有兩個選項:
  •  好的實踐 ,沒有大的性能改進可以採用了。
  •  壞的實踐,有大的性能改進可以採用。
正確答案是「壞實踐」,因為索引的數據列的順序不對。通常的索引列排序是規律是,如果等號運算符放在左邊就經常有很高的性能,過濾之後,再使用範圍操作符也很有效率。然而,如果範圍操作符放在左邊,就會喪失索引的好處,之後的的索引列也不能高效率的使用。
像以上沒有修改的查詢語句,我們要在索引中找出1826個實體(它們都符合date_column 列的過濾),然後對它們進行state 列過濾。如果過濾順序改變一下,數據庫就使得兩次過濾都很有效,直接把要過濾的行數限制在了365 行內。
人們是這樣回答的:
 q2_bydb_20140212
等一下,竟然比隨便猜猜的正確概率還低,人們不僅對次沒有意識,而且大多數人都有了錯誤的理解。然而我得承認這個」大多數「是有水分的。當我運行這個例子時,快的不只是一倍,竟然加速了70%。
總體分數:多少人通過了測試?
單獨看每個例子很有趣,但是那不能讓你知道有多少人答對了5個題目,下面的圖可以告訴你。
correct_answers_given
最後,我想把這張圖歸結為一個數字:到底多少人通過了測試?
考慮到只有五個問題,並且每個問題只有兩個選項,公平的說,我想答對三個不足以說明你通過了測試,答對五個又明顯要求過高。答對四個通過測試,我覺得這樣界定是很明智的。使用這個定義,38.2%通過了測試。多說一句,隨便猜通過的概率為12.5%。

原文鏈接: MarkusWinand   翻譯: 伯樂在線 - sunbiaobiao
譯文鏈接: http://blog.jobbole.com/60800/
[ 轉載必須在正文中標註並保留原文鏈接、譯文鏈接和譯者等信息。]

2014/3/12

SQL Server內存遭遇操作系統進程壓榨案例


From :http://blog.jobbole.com/62432/


原文出處: Czperfectaction的博客  


場景:

最近一台DB服務器偶爾出現CPU報警,我的郵件報警閾(請讀yù)值設置的是15%,開始時沒當回事,以為是有什麼統計類的查詢,後來越來越頻繁。

探索:

我決定來查一下,究竟是什麼在作怪,我排查的順序如下:
1、首先打開Cacti監控,發現最近CPU均值在某天之後驟然上升,並且可以看到System\Processor Queue Length 和 sqlservr\%ProcessorTime 也在顯著的變化。
egwegweg101627022872955
2、從最容易入手的低效SQL開始,考慮是不是最近業務做了什麼修改?連接到該SQL實例,打開活動監視器,展開「最近耗費大量資源的查詢」,並 CPU時間倒序,在這裡並未發現有即時的耗費資源的查詢。據個人經驗,這裡的值如果是4位數,分鐘內執行次數3位數,一般的服務器CPU大概就10%以 上,如果cpu時間那裡是5位數,且分鐘內執行次數也很高,幾百次以上,那CPU一般就會不淡定了。圖片僅為演示
regergehg101637470523676
ergegg101637579375581
3、沒有耗資源的SQL,這是DBA最不願意看到的結果,因為也許,SQL Server受到了來自內部或者外部的壓力,使得自己花費了過多的時間去處理與操作系統的溝通去了。SQL Server常見的非查詢低效類的性能問題,絕大多數都來自於內存或者硬盤,而這兩者有的時候需要同時研究對比基線,才能確定誰是因,誰是果。在這裡,我 們首先查看SQL Server內存使用情況,當打開性能計數器時,我和我的小夥伴們都驚呆了……安裝了64G內存的數據庫,SQL Server的TargetMemory僅有500多兆!這其中StolenPage還佔用了200多兆,數據庫DataPage僅有200多兆的內存可 供使用,Oh,Shit!雖然我很不想用「去哪了」這三個字,但是「我的內存去哪了「?同時我們也注意到PageLifeExpectancy值只有 26(一個內存充足的服務器,這個值至少應該是上W的),而很早之前我們津津樂道的」Cache Hit Ration」卻仍然保持一個比較高的水準98! 這個案例告訴我們,緩存命中率這個性能計數器很多時候說明不了什麼問題。
geg54545y54ygrg101644419859009
4、OK,既然這樣,是誰佔用了本該屬於我親愛的SQL Server的內存呢?我們繼續,打開Wiindows任務管理,選定進程選項卡,點擊顯示所有用戶進程,發現svchost.exe佔用了絕大多數的60G內存!
regregerg101701562239210
5、那svchost.exe又是個什麼東西呢?我們下面就用到ProcessMonitor這個工具了,打開後自動加載所有Wiindows進程,按內存排序後,鼠標移至svchost.exe進程上,顯示為Remote Registry服務。
trhrth1hgegh01704346942165
6、查到這裡,事情已經有了一定的眉目,這個多半是windows內存洩露Bug,遂google關鍵詞: windows server 2008 r2 remote registry memory leak
找到如下鏈接:http://support.microsoft.com/kb/2699780/en-us
果然:Assume that you query performance counters on a remote computer by using an application on a computer that is running Windows 7 or Windows Server 2008 R2. In this situation, the memory usage of the Remote     Registry service on the local computer increases until the available memory is exhausted.

解決方法:

1、重啟服務器,安裝hotfix
2、因為重啟服務器會影響到業務,所以我在想重啟RemoteRegistry服務,應該也能暫時解決問題,這個bug應該是在某種固定情景下發生的。
隨後,在合適的時間,我重啟了這個服務,SQL Server的TargetMemory重新恢復到60多G,CPU也正常了,目前為止該問題未再發生。

後續跟進:

DBA的工作,說難也難,說容易也容易,發現問題,解決問題還不夠,我們還要意識到自己的欠缺,在本案例中,我之前並沒有建立起SQL Server內存的監控,所以沒有在第一時間就發現病情的嚴重性,好在該服務器並未承擔重要業務,否則後果不堪設想,說不定早就崩潰過了,後怕之處在於, 如果崩潰了,自然要重啟服務器,到那個時候,我們連第一現場都沒有,當leader問起來,我又該使勁撓頭了。
該事件之後,我建立起了SQL Server內存的監控,1天後,我從新的監控數據中,又發現了一台服務器出現相同的問題!我很慶幸,不是慶幸服務器沒宕機,而是慶幸我做對了。
附一張內存監控圖,可以看到服務重啟之後,SQL Server的Total Pages一直在上升,並逐漸穩定,Page life expectancy也在變得越來越大,CPU也能指示病症已消除,我很欣慰。
rgreger101724207207725
regfwegfwgf101738120387322

總結:

服務器在出現性能問題前,大部分是提前有一些徵兆的,尤其是內存洩露,因為內存是一點點被壓榨掉的,最後到達一個極限時,SQL Server就會突然Crash掉,然後只留給你一個dump,微軟就笑了。有經驗的大夫應該從日常的腰酸背痛中看出一些端倪,然後進一步分析,提前預知 重大疾病的發生,這就是DBA的價值。這個案例,告訴我,重視服務器異常的細節變化,才能做到防患於未然。

2013/12/17

查詢SQL Server目前連線(Connection)狀況

SQL server 2008沒有直覺的查詢方式,只能下指令
google一下,找到了下面方式

from:

http://segdoc.blogspot.com/2011/11/sql-serverconnection.html
=======================

查詢SQL Server目前連線(Connection)狀況

前言
我們的產品最近在某客戶家運作時,常固定某個時間就無法登入,目前發現是系統的料庫連線數超過,所造成。
又該資料庫不只我們產品在使用,為找出佔掉Connection的真兇,弄了這段SQL Statement來確認。

作法
--查詢目前連線數量 SELECT * FROM master..sysperfinfo where object_name = 'SQLServer:General Statistics' And counter_name = 'User Connections' --查詢目前連線數明細 Use Master SELECT c.session_id, c.connect_time,s.login_time, c.client_net_address, s.login_name,s.status FROM sys.dm_exec_connections c left join sys.dm_exec_sessions s on c.session_id = s.session_id

總結
Master 資料庫平常雖少用,但卻隱藏許多重要資料,在查問題時,真的是好用,值得花點時間去了解~

Reference

http://technet.microsoft.com/zh-tw/library/ms189806(SQL.100).aspx
http://technet.microsoft.com/zh-tw/library/ms181509(SQL.90).aspx
http://technet.microsoft.com/zh-tw/library/ms176013.aspx

2008/5/28

防護SQL的產品

https://www.sentrigo.com/sysreq.htm?loc=rs

這是朋友推薦要去看的網站
裡面會針對各種可能的SQL攻擊,先作防護

看起來,要找時間來好好的研讀了

手機QQ禁用NFC

一圖解千言萬語 應用程式權限:透過NFC起動