數(shù)據(jù)庫漏洞的種類繁多和危害性嚴(yán)重是數(shù)據(jù)庫系統(tǒng)受到攻擊的主要原因,通過研究數(shù)據(jù)庫漏洞分類,有助于人們對漏洞的深入理解并加以預(yù)防和避免。

美國Verizon就“核心數(shù)據(jù)是如何丟失的”做過一次全面的市場調(diào)查,結(jié)果發(fā)現(xiàn),75%的數(shù)據(jù)丟失情況是由于數(shù)據(jù)庫漏洞造成的,這說明數(shù)據(jù)庫的安全非常重要。
據(jù)CVE的數(shù)據(jù)安全漏洞統(tǒng)計(jì),Oracle、SQL Server、MySQL等主流數(shù)據(jù)庫的漏洞逐年上升,以O(shè)racle為例,當(dāng)前漏洞總數(shù)已經(jīng)超過了1200多個(gè)。
數(shù)據(jù)庫安全漏洞從來源上,大致可以分為四類:缺省安裝漏洞、人為使用上的漏洞、數(shù)據(jù)庫設(shè)計(jì)缺陷、數(shù)據(jù)庫產(chǎn)品的bug。
1、缺省安裝漏洞
● 數(shù)據(jù)庫安裝后的缺省用戶名和密碼
在主流數(shù)據(jù)庫中往往存在若干缺省數(shù)據(jù)庫用戶,并且缺省密碼都是公開的,攻擊者完全可以利用這些缺省用戶登錄數(shù)據(jù)庫。
例如,Oracle中有sys、system、sysman、scott等700多個(gè)缺省用戶;MySQL本機(jī)的root用戶可以沒有口令;網(wǎng)絡(luò)上主機(jī)名為build的root和用戶可以沒有口令。
● 數(shù)據(jù)庫安裝后的缺省端口號
在主流數(shù)據(jù)庫中缺省端口號是固定的,如Oracle是1521、SQL Server是1433、MySQL是3306等。
● 數(shù)據(jù)庫安裝后低安全級別設(shè)置
數(shù)據(jù)庫安裝后的缺省設(shè)置,安全級別一般都較低。
如MySQL中本地用戶登錄和遠(yuǎn)程build主機(jī)登錄不校驗(yàn)用戶名密碼
如Oracle中不強(qiáng)制修改密碼、密碼的復(fù)雜度設(shè)置較低、不限定遠(yuǎn)程鏈接范圍、通訊為明文等。
● 啟用不必要的數(shù)據(jù)庫功能
在數(shù)據(jù)庫的缺省安裝中為了便于使用和學(xué)習(xí),提供了過量的功能和配置。如Oracle安裝后無用的示例庫、有威脅的存儲過程; MySQL的自定義函數(shù)功能。
典型數(shù)據(jù)庫泄密案例:Korea會展中心數(shù)據(jù)庫被入侵
2011年5月,黑客入侵Korea會展中心數(shù)據(jù)庫,在網(wǎng)上爆出其中大量的客戶資料數(shù)據(jù),并展示數(shù)據(jù)庫操操作過程。
黑客首先通過端口掃描技術(shù),檢測出該服務(wù)器上開放著1521端口(Oracle數(shù)據(jù)庫的缺省端口),首先探明該主機(jī)便是數(shù)據(jù)庫服務(wù)器。接著利用掃描程序,檢測到缺省系統(tǒng)用戶dbsnmp并未被鎖定,且保留著數(shù)據(jù)庫安裝時(shí)的缺省密碼。
之后黑客利用權(quán)限提升的漏洞,將dbsnmp用戶的權(quán)限提升至DBA,開始了數(shù)據(jù)庫訪問之旅。
2、人為使用漏洞
● 過于寬泛的權(quán)限授予
在很多系統(tǒng)維護(hù)中,數(shù)據(jù)庫管理員并未細(xì)致地按照最小授權(quán)原則給予數(shù)據(jù)庫用戶授權(quán),而是根據(jù)最為方便的原則給予了較為寬泛的授權(quán)。
例如,一個(gè)普通的數(shù)據(jù)庫維護(hù)人員被授予了任意表的創(chuàng)建和刪除、訪問權(quán)限,甚至是給予DBA角色;
例如,一個(gè)大學(xué)管理員在工作中只需要能夠更改學(xué)生的聯(lián)系信息,不過他可能會利用過高的數(shù)據(jù)庫更新權(quán)限來更改分?jǐn)?shù)。
● 口令復(fù)雜度不高
弱口令:非默認(rèn)的數(shù)據(jù)庫口令也是不安全的,通過暴力破解,攻擊者不斷地輸入用戶名和密碼組合,直到找到可以登錄的一組。暴力過程可能是靠猜想,也可能是系統(tǒng)地枚舉可能的用戶名/密碼組合。通常,攻擊者會使用自動(dòng)化程序來加快暴力過程的速度。口令破解的工具有很多,并且通過Google搜索或sectools.org等站點(diǎn)就可以輕易地獲得,這樣就會連接到Cain、Abel或John the Ripper等流行的工具。
密碼曝光:一些公眾權(quán)限的存儲過程、表的調(diào)用會導(dǎo)致密碼曝光。例如,通過執(zhí)行公眾權(quán)限的存儲過程msdb.dbo.sp_get_sqlagent_properties可以獲得SQL Agent密碼;通過查詢公眾權(quán)限的表RTblDBMProps可查看DTS密碼;Microsoft SQL Server允許本地用戶通過數(shù)據(jù)轉(zhuǎn)換服務(wù)(DTS)包屬性對話框獲得數(shù)據(jù)庫密碼。
例如,Oracle 10.1及早期版本,sysman用戶的密碼明文存儲在emoms.properties中,10.2后,雖加密存儲,但密鑰也存在該文件中,且用DES算法,很容易解密。
因此建議管理員細(xì)致地按照最小授權(quán)原則給予數(shù)據(jù)庫用戶授權(quán);數(shù)據(jù)庫用戶口令長度應(yīng)大于等于10,并且應(yīng)該包括字母和數(shù)字,應(yīng)該通過一個(gè)口令檢驗(yàn)函數(shù)來實(shí)施這一點(diǎn)。
3、數(shù)據(jù)庫設(shè)計(jì)缺陷
● 明文存儲引起的數(shù)據(jù)泄密
在當(dāng)前的主流數(shù)據(jù)庫中,數(shù)據(jù)以明文形式放置在存儲設(shè)備中,存儲設(shè)備的丟失將引起數(shù)據(jù)泄密風(fēng)險(xiǎn)。
數(shù)據(jù)庫數(shù)據(jù)文件在操作系統(tǒng)中以明文形式存在,非法使用者可以通過網(wǎng)絡(luò)、操作系統(tǒng)接觸到這些文件,從而導(dǎo)致數(shù)據(jù)泄密風(fēng)險(xiǎn)。
通過UE這樣的文本工具即可得到部分明文的信息,通過DUL/MyDUL這樣的工具能夠完全實(shí)現(xiàn)將數(shù)據(jù)文件格式化導(dǎo)出。
典型數(shù)據(jù)庫泄密案例:7天酒店數(shù)據(jù)庫被盜
會員人數(shù)超過1650萬,酒店總數(shù)逼近600家,在美國紐約證券交易所上市的7天連鎖酒店集團(tuán)無疑是國內(nèi)經(jīng)濟(jì)型連鎖酒店集團(tuán)的龍頭企業(yè)之一,但更多人所不知道的是,從去年開始,7天酒店在國內(nèi)黑客圈中成了“明星”。
黑客通過“刷庫”直接盜走整個(gè)數(shù)據(jù)庫數(shù)據(jù)。即黑客利用企業(yè)網(wǎng)站存在的漏洞入侵?jǐn)?shù)據(jù)庫服務(wù)器,直接將整個(gè)數(shù)據(jù)庫文件拷貝,通過DUL/MyDUL這種類似的工具將所有二進(jìn)制方式存儲的數(shù)據(jù)還原成清晰地格式化數(shù)據(jù)。
● SYSDBA、DBA等超級用戶的存在
在數(shù)據(jù)庫中,以sys和sa為代表的系統(tǒng)管理員,可以訪問到任何數(shù)據(jù);除了系統(tǒng)管理員,以用戶數(shù)據(jù)分析人員、程序員、開發(fā)方維護(hù)人員為代表的特權(quán)用戶,在特殊的時(shí)候,也需要訪問到敏感數(shù)據(jù),從而獲得了權(quán)限。這些都為數(shù)據(jù)的泄密留下了極大的隱患。
例如,掌握特權(quán)用戶口令的維護(hù)人員,進(jìn)入了CRM系統(tǒng),只需具有對數(shù)據(jù)庫的只讀性訪問權(quán)限,這樣,這個(gè)用戶就可以訪問讀取數(shù)據(jù)庫內(nèi)的任何表,包括信用卡信息、個(gè)人信息。
● 無法鑒別應(yīng)用程序的訪問是否合法
對于合法的數(shù)據(jù)庫用戶,通過合法的業(yè)務(wù)系統(tǒng)訪問數(shù)據(jù)是正常的行為,但是數(shù)據(jù)庫的一個(gè)缺陷在于,無法鑒別應(yīng)用程序的合法性。
在現(xiàn)實(shí)系統(tǒng)維護(hù)或開發(fā)過程中,應(yīng)用系統(tǒng)后臺使用的合法數(shù)據(jù)庫用戶,常由于人為因素或管理不善,用戶名及口令很容易泄露給第三方,第三方則可以通過命令行或管理工具直接訪問密文數(shù)據(jù),批量竊取數(shù)據(jù)。
典型數(shù)據(jù)庫泄密案例:陜西移動(dòng)1400萬手機(jī)用戶個(gè)人信息泄漏
今年3月以來,周雙成利用職務(wù)之便,多次侵入陜西移動(dòng)用戶數(shù)據(jù)庫,盜取手機(jī)用戶個(gè)人信息,販賣給偵探公司。
周雙成利用工作之便,能在研發(fā)和維護(hù)系統(tǒng)過程中獲知數(shù)據(jù)庫口令,數(shù)據(jù)庫口令泄漏后,完全可以繞開合法的業(yè)務(wù)系統(tǒng),直接使用該用戶通過其他程序(自己編寫的木馬程序)定期訪問數(shù)據(jù)庫,進(jìn)而竊取數(shù)據(jù)庫中的客戶隱私信息。
4、數(shù)據(jù)庫產(chǎn)品bug
● 緩沖區(qū)溢出
這類漏洞是由于不嚴(yán)謹(jǐn)?shù)木幋a,使數(shù)據(jù)庫內(nèi)核中存在對于過長的連接串、函數(shù)參數(shù)、SQL語句、返回?cái)?shù)據(jù)不能嚴(yán)謹(jǐn)?shù)奶幚恚斐纱a段被覆蓋。
通過覆蓋的代碼段,黑客可以對數(shù)據(jù)庫服務(wù)器進(jìn)行各種操作,最常見的是使數(shù)據(jù)庫崩潰,引起拒絕服務(wù)。
例如,SQL Server中利用緩沖區(qū)溢出,攻擊者可以通過以下函數(shù)或存儲過程執(zhí)行任意代碼:RAISERROR、FORMATMESSAGE、xp_sprintf、 sp_Mscopyscriptfile、xp_sqlinventory、xp_sqlagent_monitor、sp_OACreate、sp_OAMethod、sp_OAGetProperty、sp_OASetProperty、sp_OADestroy;另外,xp_peekqueue、xp_displayparamstmt、xp___execresultset等40多個(gè)函數(shù)或擴(kuò)展存儲過程也存在緩沖區(qū)溢出。
● 拒絕服務(wù)攻擊漏洞
數(shù)據(jù)庫中存在多種漏洞,可以導(dǎo)致服務(wù)拒絕訪問,如命名管道拒絕服務(wù)、拒絕登錄、RPC請求拒絕服務(wù)等。
例如Microsoft SQL 7.0服務(wù)允許一個(gè)遠(yuǎn)程攻擊者通過不正確格式的TDS數(shù)據(jù)包引起拒絕服務(wù);本地或遠(yuǎn)程用戶通過向命名管道發(fā)送一個(gè)長請求引發(fā)拒絕服務(wù)。
例如MySQL中Data_format()函數(shù)在處理用戶提交的參數(shù)時(shí)存在漏洞,畸形的參數(shù)數(shù)據(jù)會導(dǎo)致MySQL服務(wù)器崩潰。
● 權(quán)限提升漏洞
黑客攻擊者可以利用數(shù)據(jù)庫平臺軟件的漏洞將普通用戶的權(quán)限轉(zhuǎn)換為管理員權(quán)限。漏洞可以在存儲過程、內(nèi)置函數(shù)、協(xié)議實(shí)現(xiàn)甚至是SQL語句中找到。
例如,Oracle中一系列系統(tǒng)對象如PL/SQL包,缺省賦予了Public角色的執(zhí)行權(quán)限,借助這些包執(zhí)行注入了” grant dba to user”的函數(shù)或過程,或直接作為參數(shù)執(zhí)行即可。有PUBLIC執(zhí)行權(quán)限的包共計(jì)30多個(gè),例如:ctxsys.driload.validate_stmt()將grant語句作為參數(shù)即可完成權(quán)限提升。
例如,在SQL Server中,通過Job輸出文件覆蓋,沒有權(quán)限的用戶可以創(chuàng)建job并通過SQL Server代理的權(quán)限提升執(zhí)行該job。
例如,一個(gè)金融機(jī)構(gòu)的軟件開發(fā)人員可以利用有漏洞的函數(shù)來獲得數(shù)據(jù)庫管理權(quán)限。使用管理權(quán)限,惡意的開發(fā)人員可以禁用審計(jì)機(jī)制、開設(shè)偽造的賬戶以及轉(zhuǎn)賬。
作為一個(gè)剛?cè)腴T的Liunx愛好者,必須了解的Liunx基礎(chǔ)知識有哪些?
1、Linux的文件系統(tǒng)
Unix的文件系統(tǒng)管理是極具特色的。NFS、UFS、TMPFS、VFS、PROC等各類文件系統(tǒng)均承擔(dān)不同角色。Unix將硬盤、打印機(jī)等字符設(shè)備和塊設(shè)備都以文件的方式管理起來,對這些設(shè)備的操作就如同訪問一個(gè)文件。
2、什么是shell
shell是人機(jī)交互的字符界面。Unix中提供多種shell供使用者選擇,例如RedHat中的bash、tcsh、ksh等,由不同的作者編寫完成。在這些shell中,用戶都可以輸入命令完成系統(tǒng)管理、配置等任務(wù);而在Solaris中,有sh、csh等。
3、Linux用戶與用戶登錄
Linux是一個(gè)真正意義上的多用戶操作系統(tǒng),用戶要使用該系統(tǒng),必須輸入用戶名和密碼,經(jīng)系統(tǒng)驗(yàn)證無誤后才可以登錄系統(tǒng)使用。
Linux下有兩種用戶:
1)root用戶:超級權(quán)限者和系統(tǒng)的擁有者,在Linux系統(tǒng)中有且只有一個(gè)root用戶,它可以在系統(tǒng)中做任何操作。在系統(tǒng)安裝時(shí)所設(shè)定的密碼就是root用戶的密碼,該密碼請牢記,并出于安全考慮,請定期修改。密碼的保密性也要得到保證。
2)普通用戶:Linux系統(tǒng)可以創(chuàng)建許多普通用戶,并為其指定相應(yīng)的權(quán)限,使其有限地使用Linux系統(tǒng),如安裝msyql時(shí)需要?jiǎng)?chuàng)建的mysql用戶。用戶通過本機(jī)的Xwindow或Telnet遠(yuǎn)程登錄后,執(zhí)行exit命令即可退出登錄。
4、修改口令
為了更好地保護(hù)用戶帳號的安全,Linux允許用戶在登錄之后隨時(shí)使用passwd命令修改自己的口令。修改口令需要經(jīng)歷三步:
輸入原來的口令,如果口令輸錯(cuò),將中止程序,無法修改口令;
輸入新的口令;
重新輸入一次新的口令,如果兩次輸入的口令相吻合,則口令修改成功。
5、用戶的環(huán)境變量
環(huán)境變量定義了用戶執(zhí)行命令操作所需要的諸如命令路徑、庫路徑、別名、字符集等等的內(nèi)容。/etc/profile是缺省所有bash用戶的環(huán)境變量文件。而用戶home目錄下的.bash_profile、.bashrc等文件是bash用戶自己定義的環(huán)境變量文件。例如,ifconfig命令在/sbin目錄下,如果不將/sbin路徑加入到環(huán)境變量PATH中,那么每次執(zhí)行這個(gè)命令,都需要輸入/sbin/ifconfig。
不同shell的環(huán)境變量定義方式不同。bash采取賦值的方式,再export生效。
執(zhí)行env命令可以查看當(dāng)前用戶使用的所有環(huán)境變量。
6、Linux文件與目錄權(quán)限
在Linux系統(tǒng)中,每一個(gè)文件和目錄都有相應(yīng)的訪問許可權(quán)限,分為可讀、可寫和可執(zhí)行三種,分別以r、w、x表示,其含義為read、write、execute(目錄的可執(zhí)行指的可以進(jìn)入目錄)。每一個(gè)文件或目錄的訪問權(quán)限都有三組,每組用三位表示,如: d rwx r-x r--。
第一部分:這里的d代表目錄,其它的有:- 代表普通文件,c代表字符設(shè)備文件;
第二部分:文件所有者的權(quán)限字;
第三部分:與文件所有者同組的用戶的權(quán)限字;
第四部分:其它用戶的權(quán)限字。
1)文件/目錄權(quán)限設(shè)置命令:chmod [mode] 文件名
如果要對文件a.txt的權(quán)限要設(shè)置為rw-rw-r--,則轉(zhuǎn)換成二進(jìn)制數(shù)就是110 110 100,再每三位轉(zhuǎn)換成為一個(gè)十進(jìn)制數(shù)得到664,因此我們執(zhí)行命令:
chmod 664 a.txt
表示a.txt文件屬主和同組用戶可讀可寫,其他用戶只可讀。
2)改變文件/目錄的屬主命令:chown [選項(xiàng)] 用戶名:組名文件/目錄名
其中最常用的選項(xiàng)是“R”,加上這個(gè)參數(shù),可以將整個(gè)目錄里的所有子目錄和文件的屬主都改變成指定用戶。
7、Linux的Daemon
Daemon守護(hù)進(jìn)程是指系統(tǒng)啟動(dòng)時(shí)需要加載的必要的服務(wù)和應(yīng)用。如xinetd等。主要的守護(hù)進(jìn)程在/etc/xinetd.d目錄下,而/etc/init.d是在系統(tǒng)初始化的時(shí)候需要加載的進(jìn)程,如syslogd、sendmail等。
例如,當(dāng)我們telnet一個(gè)Linux主機(jī)時(shí),xinetd監(jiān)聽23端口,當(dāng)發(fā)現(xiàn)有連接請求時(shí),xinetd啟動(dòng)telnetd守護(hù)進(jìn)程,處理這個(gè)telnet連接。
4、Drive quality upstream
我們都知道bug越是滯后發(fā)現(xiàn),修復(fù)的成本越高。據(jù)微軟統(tǒng)計(jì),如果產(chǎn)品發(fā)布以后需要發(fā)布一個(gè)熱修復(fù),它的直接成本是150萬美元(間接成本在200萬美元),而在發(fā)布之前的一個(gè)月發(fā)現(xiàn)的話,修復(fù)成本是5萬,設(shè)計(jì)階段修復(fù)成本是1千,需求階段修復(fù)成本是1百。在需求分析階段,測試人員主要職責(zé)就是驗(yàn)證需求分析的可行性和可靠性。PM和DEV的共性是易于樂觀,傾向于把實(shí)際情況簡單化,所以會作出很多假設(shè)。比如用戶肯定不會這么使用,用戶肯定知道如何用,所有用戶的環(huán)境肯定都有該配置。但是實(shí)際情況下總會有用戶不知道如何用,總會有用戶會不按“預(yù)先設(shè)計(jì)”的方式使用,總會有用戶環(huán)境沒有該項(xiàng)配置。所以測試人員的主要職責(zé)就是找出這些假設(shè)并提出疑問并加以驗(yàn)證。
在dev設(shè)計(jì)階段,測試人員需要驗(yàn)證設(shè)計(jì),同樣找出dev的假設(shè)然后疑問這些假設(shè)是否合理,看看該設(shè)計(jì)是否處理很多沒有預(yù)料的但是有可能會發(fā)生的情況,比如用戶輸入特殊字符,非常規(guī)操作,非常規(guī)流程,機(jī)器重啟,死機(jī),數(shù)據(jù)庫連接中斷,網(wǎng)絡(luò)中斷,內(nèi)存耗盡等等。除了驗(yàn)證設(shè)計(jì)是否處理非正常情況外,測試人員的另外一個(gè)更為重要的職責(zé)是驗(yàn)證設(shè)計(jì)的可測試性。可測試性是指測試軟件容易的程度。軟件的可測性對于提高軟件的質(zhì)量至關(guān)重要。道理很簡單,如果你的軟件很難測試,無從下手,測試一個(gè)用例需要幾個(gè)小時(shí)甚至幾天的話,你的軟件質(zhì)量也就無從保證。提高軟件可測試性通常的做法是把軟件模塊化,松耦合,模塊內(nèi)部運(yùn)行狀態(tài)可見,模塊內(nèi)部運(yùn)行分支可控制。在評審一個(gè)設(shè)計(jì)時(shí)通常問的問題是該如何測試該模塊,是否容易測試它,能不能單獨(dú)測試它。如果不可以的話,需要重新考慮設(shè)計(jì)。因?yàn)橐粋€(gè)設(shè)計(jì)的很容易測試的模塊和產(chǎn)品可以使得后期的測試代價(jià)大大降低。微軟大部分復(fù)雜系統(tǒng)都會有一個(gè)“one-box”版本,該版本是個(gè)假的模擬系統(tǒng)但是擁有真正系統(tǒng)的幾乎所有功能,它可以運(yùn)行在任何機(jī)器上。系統(tǒng)的絕大部分功能都可以在該模擬系統(tǒng)上快速方便驗(yàn)證。我在培訓(xùn)的時(shí)候很多學(xué)生問??他們在后期測試的時(shí)候遇到許多無法測試或者很難測試的困境,問我該如何解決。在具體了解困難和困境之后,我發(fā)現(xiàn)他們的測試策略非常單調(diào),只能把產(chǎn)品部署到有限的測試環(huán)境下從用戶界面上做端到端的測試。如果想測試某個(gè)特定模塊或功能需要好幾個(gè)其它模塊配合起來才可以。我就坦率的說如果你的軟件是這么架構(gòu)設(shè)計(jì)的話而且已經(jīng)到了這一步,世界上沒有人可以解決你現(xiàn)在面臨的困難。因?yàn)榭雌饋砗孟衲闶强ㄔ诂F(xiàn)在這一步了,但實(shí)際上根本問題是在前期,在需求或設(shè)計(jì)。就像解決河流的水質(zhì)污染問題,你在下游無論多大的代價(jià)也只能稍微改善,解決問題的根本方法是在解決上游的污染源。或者像隔靴撓癢,隔著厚厚的靴子無法撓到關(guān)鍵地方,必須能夠把靴子脫掉直接撓。
5、Getting better every day
軟件測試的目的一個(gè)是找出bug,另外一個(gè)是衡量軟件的質(zhì)量。通過測試來了解產(chǎn)品哪些地方薄弱,哪些地方不穩(wěn)定. 通過監(jiān)控測試的結(jié)果,包括功能測試,性能壓力測試,安全測試,本地化測試,容錯(cuò)測試等等來反映當(dāng)前軟件的質(zhì)量。這也是持續(xù)集成的一個(gè)重要原因,它一方面短期迭代,另一方面可以在最短的時(shí)間內(nèi)知道軟件的質(zhì)量,同時(shí)跟蹤軟件質(zhì)量重開始到發(fā)布,軟件質(zhì)量的變化曲線。衡量軟件質(zhì)量的通常指標(biāo)有:測試用例通過率/趨勢,bug數(shù)量,種類/趨勢,代碼覆蓋率等等。另外測試在開發(fā)周期中通常做的其它工作還有:bug root cause analysis,Bug analysis,Testcase failure analysis, test gap analysis,Bug talk,bug share,CCS data analysis等等。這一方面促使產(chǎn)品質(zhì)量逐漸變好,而且是看得見的好。另外也促使自己放下繁忙的工作靜心思考一下,如何更有效率更進(jìn)一步提高質(zhì)量。
6、Engineering agility
隨著軟件即服務(wù)和云計(jì)算的興起,它們給開發(fā)管理,質(zhì)量管理,運(yùn)營管理等提出了很多新的挑戰(zhàn)。以前那種保密開發(fā)測試2-3年再突然發(fā)布的策略無法適應(yīng)互聯(lián)網(wǎng)應(yīng)用服務(wù)的要求,而是逐漸演變成快速開發(fā)出產(chǎn)品基本原型,然后就發(fā)布,根據(jù)用戶反饋不斷加以改進(jìn)的方式。質(zhì)量管理方面,基于服務(wù)的產(chǎn)品除了關(guān)注功能性能,還有其它特別重要的方面,比如scalability, high availability, fault tolerance, disaster recovery, etc.. 這些都是桌面型產(chǎn)品所沒有的或不重視的。微軟從2010年開始在很多組開始實(shí)踐如何提高服務(wù)型產(chǎn)品的質(zhì)量,比如Azure, Bing, etc…。其中最為根本的一點(diǎn)就是提高整個(gè)團(tuán)隊(duì)的敏捷度,團(tuán)隊(duì)能夠跟的上快速迭代交付的節(jié)奏。這需要從產(chǎn)品設(shè)計(jì)上到測試自動(dòng)化,工具,基礎(chǔ)設(shè)施上得以保障。另外一個(gè)非常重要的實(shí)踐就是TiP (test in production) 或 crowd-sourced testing. 我們在前面談到“drive quality upstream”,也即是提前測試。test in production正好相反是“drive quality downstream”,也即是在產(chǎn)品環(huán)境中測試。傳統(tǒng)的桌面產(chǎn)品發(fā)布之后就不再測試了。服務(wù)型產(chǎn)品,產(chǎn)品發(fā)布后繼續(xù)測試。它的基本出發(fā)點(diǎn)是:無論投資多少建立測試環(huán)境用以模擬產(chǎn)品運(yùn)行環(huán)境,測試環(huán)境和真正產(chǎn)品環(huán)境總會有不同。無論花費(fèi)多大的代價(jià)做前期測試,都不可能找出所有的bug。所以無論在發(fā)布之前花費(fèi)多大的代價(jià)做測試,在產(chǎn)品上線后總會發(fā)現(xiàn)新的bug。現(xiàn)在既然發(fā)布產(chǎn)品更新非常快和容易,為什么不干脆在真正產(chǎn)品環(huán)境中來測試,或者利用真正的用戶使用真正的產(chǎn)品來測試(當(dāng)然用戶意識不到)。一旦發(fā)現(xiàn)bug,馬上修復(fù),馬上更新。這不僅減輕了前期測試的投入,而且提高的測試效率。看看我們在測試環(huán)境中發(fā)現(xiàn)的bug有多少沒有被認(rèn)為是“真正”的bug就明白了。當(dāng)然要做到test in production, 需要很多具體的流程、技術(shù),工具作為保障。不能影響用戶的關(guān)鍵業(yè)務(wù),不能讓用戶發(fā)現(xiàn)過于簡單的bug。 除了基本的測試自動(dòng)化基礎(chǔ)設(shè)施和測試用例外,常用的一些TiP技術(shù)有:A/B testing,expose control/slicing model,on/off features,chaos-monkey,measuring/monitoring.
最后一篇將和大家討論一下測試工程師的技能提高和職業(yè)發(fā)展,to be continued .....
相關(guān)鏈接:
第一部分:重構(gòu)
第二部分:設(shè)計(jì)原則與代碼所有權(quán)
第三部分:進(jìn)化型設(shè)計(jì)
第四部分:靈活性與復(fù)雜性
第五部分:測試驅(qū)動(dòng)開發(fā)
第六部分:性能與過程調(diào)優(yōu)
第三部分:進(jìn)化型設(shè)計(jì)
在連載的第三部分,福勒討論了計(jì)劃型設(shè)計(jì)和進(jìn)化型設(shè)計(jì)的區(qū)別,揭示了著眼于解決表象問題可以使開發(fā)者發(fā)現(xiàn)本質(zhì)問題,并主張好的設(shè)計(jì)工作不會降低工作效率。
計(jì)劃型設(shè)計(jì)和進(jìn)化型設(shè)計(jì)
比爾:在你的論文《設(shè)計(jì)是否已死》(Is Design Dead)一文中,談到了計(jì)劃型設(shè)計(jì)。那么什么是計(jì)劃型設(shè)計(jì)?
馬丁:我將設(shè)計(jì)區(qū)分為計(jì)劃型設(shè)計(jì)和進(jìn)化型設(shè)計(jì)。當(dāng)開發(fā)者著手實(shí)施一個(gè)軟件時(shí),他首先需要做設(shè)計(jì),然后再按照這個(gè)設(shè)計(jì)進(jìn)行編 碼實(shí)現(xiàn)軟件,這就是我所說的計(jì)劃型設(shè)計(jì)。計(jì)劃型設(shè)計(jì)可能借助 UML;或者把整個(gè)系統(tǒng)分為若干子系統(tǒng),定義這些子系統(tǒng)間的接口。在計(jì)劃型設(shè)計(jì)中,在設(shè)計(jì)和代碼實(shí)現(xiàn)這二者之間存在明確的切換。而這二者又往往由不同的人 來完成。架構(gòu)師構(gòu)思設(shè)計(jì),開發(fā)者編碼實(shí)現(xiàn)。做好的設(shè)計(jì)并不是說一點(diǎn)都不能改變,但基本上是固定的。你可能會說,設(shè)計(jì)做得越好,在編碼的時(shí)候,就會越少對設(shè) 計(jì)做出改動(dòng)。
而在進(jìn)化型設(shè)計(jì)中,開發(fā)者在編程實(shí)踐的過程中逐漸完善設(shè)計(jì)。剛開始的時(shí)候并沒有設(shè)計(jì),而是先實(shí)現(xiàn)一些小的功能。隨著實(shí)現(xiàn)的功能越來越多,設(shè)計(jì)才逐漸成型。
我在《設(shè)計(jì)是否已死》一文中想要強(qiáng)調(diào)的是,很多人在嘗試進(jìn)化型設(shè)計(jì)時(shí),往往是在一種無約束無原則的環(huán)境里,最終的設(shè)計(jì)必然很蹩腳。這是人們之所以傾向于計(jì)劃型設(shè)計(jì)的原因之一。
但是,在我看來,極限編程實(shí)踐中,通過持續(xù)不斷的集成、測試和重構(gòu),進(jìn)化型設(shè)計(jì)能夠做到比計(jì)劃型設(shè)計(jì)更有效。計(jì)劃型設(shè)計(jì)的弱點(diǎn)就是,要想做出一個(gè)好的設(shè)計(jì)非常難。
比爾:為什么?
馬丁:我解釋不清楚。這就跟解釋不清楚為什么譜一曲交響樂會如此困難一樣。世界上能把這些工作做好的人可以說是鳳毛麟角。我想,我認(rèn)為預(yù)先設(shè)計(jì)(upfront design)就屬于這類工作。要做好這類工作實(shí)在是太難了。
有趣的是,很多進(jìn)化型設(shè)計(jì)的倡導(dǎo)者,比如肯特·貝克和沃德·坎寧安,都是非常出色的設(shè)計(jì)師。但正是他們,最后認(rèn)識到自己所做的預(yù)先設(shè)計(jì)往 往不夠好。他們?nèi)菀装岩恍┦虑檫^于工程化,在不需要靈活性的地方設(shè)計(jì)靈活性,而在需要靈活性的地方又未予以考慮。因此,他們最終采用了進(jìn)化型設(shè)計(jì),并通過 運(yùn)用一套規(guī)則,保證了設(shè)計(jì)效果。其結(jié)果是,不但最終的設(shè)計(jì)更加出色,并且速度也加快了。拿我自己來說,80%左右的時(shí)間里,進(jìn)化型設(shè)計(jì)會得到不錯(cuò)的結(jié)果。 而不客氣地說一句,我認(rèn)為我的設(shè)計(jì)水平要比一般人高。因此,我認(rèn)為進(jìn)化型設(shè)計(jì)應(yīng)該可以適用于更廣泛的人群。
重構(gòu)與預(yù)先設(shè)計(jì)
比爾:重構(gòu)如何改變了預(yù)先設(shè)計(jì)的地位?
馬丁:人們之所以采用計(jì)劃型設(shè)計(jì),是因?yàn)樗麄冋J(rèn)為改動(dòng)代碼是件很麻煩的事情。因?yàn)楫?dāng)你改變代碼時(shí),有可能破壞了一些東西并造成許多漏洞。可是,如果你既有單元測試,又有在測試基礎(chǔ)上建立起來的有條理的重構(gòu)技術(shù),那么你就能十分快速有效地改動(dòng)代碼,并且不太會引起什么問題。
比爾:預(yù)先設(shè)計(jì)在重構(gòu)和其他可行的實(shí)踐中有什么作用?你現(xiàn)在還會做一些預(yù)先設(shè)計(jì)嗎?
馬丁:我認(rèn)為一些地方還是可以用到預(yù)先設(shè)計(jì)的,不過不會很多。一些人——像肯特·貝克和羅恩·杰弗里斯——認(rèn)為預(yù)先設(shè)計(jì)已 經(jīng)消亡了。從某種意義上來說,他們是對的,因?yàn)槟憧梢越柚M(jìn)化型設(shè)計(jì)搭建更復(fù)雜的系統(tǒng)。但在某些情況下,預(yù)先設(shè)計(jì)可能讓你的進(jìn)度加快一些。比如說,我不贊 同在架設(shè)數(shù)據(jù)庫之前就討論數(shù)據(jù)庫的進(jìn)化問題。我可能會對數(shù)據(jù)庫的存在有一個(gè)初步的判斷,然后以此作為起點(diǎn)。當(dāng)然,我仍會用進(jìn)化的方式完成大部分設(shè)計(jì)。
比爾:構(gòu)造良好的(well-factored)程序和設(shè)計(jì)良好的(well-designed)程序之間有區(qū)別嗎?
馬丁:它們在本質(zhì)上并沒有什么區(qū)別,但側(cè)重點(diǎn)有所不同。設(shè)計(jì)強(qiáng)調(diào)的是構(gòu)造——將程序劃分為若干分割清晰的部分。對我來說,“構(gòu)造良好的”一詞表達(dá)了更多對于設(shè)計(jì)的感覺,也即當(dāng)你審視或使用這個(gè)設(shè)計(jì)時(shí)的感覺。代碼中的“壞味道”
比爾:在你的《重構(gòu)》一書中,關(guān)于何時(shí)應(yīng)用重構(gòu),你是這樣闡述的:“與其去追求一些很模棱兩可的編程美學(xué)原則(坦率地說,這是我們咨詢師們最喜歡做的事情),還不如來點(diǎn)兒更實(shí)際的。”
我很好奇,你是怎么看待美學(xué)的?我的經(jīng)歷中有很多次都是跟已有的代碼打交道。這些代碼的設(shè)計(jì)很爛,因此處理起來非常痛苦。如果這些設(shè)計(jì)能做得好些,讓我不那么痛苦的話,我會非常感激。所以,對我來說,美學(xué)至少還是有些意義的,它可以使我的工作輕松些。
馬丁:嗯,在討論何時(shí)應(yīng)用重構(gòu)時(shí),我的確是這樣談?wù)撁缹W(xué)的。從某種意義上說,我在重構(gòu)準(zhǔn)則中所給的那些條條框框也是一種模 棱兩可的美學(xué)原則。不過,我試著給出更多的說明,而不是僅僅說“當(dāng)你的代碼看上去很丑陋的時(shí)候就需要做重構(gòu)。”比如說,重復(fù)的代碼是一種“壞味道”,再比如 說,很長的方法是一種“壞味道”,或者很臃腫的類也是一種“壞味道”。
很多“壞味道”是很容易嗅出來的。令人驚奇的是,當(dāng)你審視你的程序時(shí),往往一些很明顯的現(xiàn)象,像是某個(gè)方法長達(dá)100行,就能引導(dǎo)你改進(jìn)你的設(shè)計(jì)。
在重構(gòu)這個(gè)長達(dá)100行的方法時(shí),你可能會發(fā)現(xiàn)一些諸如責(zé)任分配(responsibility allocation)不合理這樣的設(shè)計(jì)問題。像這樣的問題,你是不可能光憑掃一眼代碼就發(fā)現(xiàn)的;但是,一個(gè)長達(dá)100行的方法,卻是一眼就能看出來的。 所以說,很表象的問題能夠把你帶向更深層的問題。
比爾:我參與過的一個(gè)項(xiàng)目竟然有一個(gè)長達(dá)11頁的 while 循環(huán)。
馬丁:這太不可思議了。
比爾:我們苦干了六個(gè)月試圖讓這個(gè)軟件穩(wěn)定運(yùn)行,可是我們不敢動(dòng)這個(gè)11頁的循環(huán)。
馬丁:這恰恰從另一個(gè)側(cè)面說明了這個(gè)長達(dá)11頁的循環(huán)是個(gè)糟糕的設(shè)計(jì)。如果你對改動(dòng)某處代碼心存顧慮的話,那它顯然是一個(gè)蹩腳的設(shè)計(jì)。
良好的設(shè)計(jì)與效率
比爾:我想,人們不注重設(shè)計(jì)的一個(gè)原因是由于工作的流動(dòng)性。那個(gè)最初編寫11頁 while 循環(huán)的程序員在我們接手項(xiàng)目的時(shí)候已經(jīng)離開了公司。我認(rèn)為,程序員因?yàn)樽约旱脑愀庠O(shè)計(jì)而自食其果的情況很少發(fā)生,因此他們沒有足夠的動(dòng)因去注重設(shè)計(jì)。此 外,即便他們注重,但設(shè)計(jì)畢竟是一項(xiàng)費(fèi)力不討好的工作,好的設(shè)計(jì)需要時(shí)間,而開發(fā)中的時(shí)間壓力往往很大。
馬丁:我不同意“好的設(shè)計(jì)需要更長的時(shí)間”這樣的說法。
比爾:為什么?
馬丁:這是很有意思的一件事。在軟件業(yè),我們似乎普遍認(rèn)為,慢工出細(xì)活。但當(dāng)我回顧我自己的經(jīng)歷時(shí),卻發(fā)現(xiàn)保持代碼的良好構(gòu)造以及編寫測試反而使我的工作加快了。
我想,人們把改進(jìn)設(shè)計(jì)所花的時(shí)間看作是“失去”的時(shí)間,但卻沒有看到將來對代碼的改動(dòng)會容易得多,往往只需要幾分鐘的時(shí)間,否則可能要花上兩三個(gè)小時(shí)。
人們往往還低估了在調(diào)試上所花的時(shí)間,低估了他們用來追蹤一個(gè)“潛伏”很久的漏洞所花的時(shí)間。我在寫代碼的時(shí)候可以立即察覺產(chǎn)生的漏洞,這使得我能在它潛伏下來之前就解決它。沒有幾件事比調(diào)試更花時(shí)間和更令人沮喪的了。假如我們一開始就能避免產(chǎn)生漏洞,那效率豈不是要提高很多?
為了充分利用LoadRunner的場景控制和分析器,幫助我們更好地控制腳本加載過程,從而展現(xiàn)更直觀有效的場景分析圖表。本次將重點(diǎn)討論LoadRunner如何調(diào)用Java測試代碼,完成壓力測試。
通常我們在執(zhí)行一些Server的壓力測試的時(shí)候,總會不經(jīng)意間想要一個(gè)Client完成對Server的調(diào)用示例,以至于我們可以通過LoadRunner直接錄制,對于測試人員來說確實(shí)很方便。不過,開發(fā)人員如果沒有那么多時(shí)間去為測試人員服務(wù),那可能就比較悲劇了,只能自己嘗試去調(diào)用接口來完成壓力測試了,這樣就需要具備一些代碼的功底了。當(dāng)然如果完成接口代碼的調(diào)用之后,還要保證LoadRunner能夠正確錄制,那確實(shí)有點(diǎn)麻煩了。很多時(shí)候,我們的接口壓力可能確實(shí)無法通過Client端來展現(xiàn),那就只能在Server使用純代碼形式完成,要么是多線程,要么是重復(fù)調(diào)用,但對于測試結(jié)果的收集就不那么方便了。所以我們還是要向辦法利用一些工具的優(yōu)勢,取之所長,為我們所用,LoadRunner的圖表分析就是所有工具里面最優(yōu)秀的,正好它也支持Java代碼、C++等調(diào)用接口,這里以Java為例。
通常在一個(gè)工程文件下,我們想對其中某一個(gè)Java文件進(jìn)行調(diào)用,但這一個(gè)Java代碼總是與其他的代碼進(jìn)行相互調(diào)用,所以我就需要引入很多的Java包或者是一些Jar包,下面就通過本次實(shí)踐來詳細(xì)講述LoadRunner調(diào)用Java代碼的步驟了,我這里的環(huán)境是LoadRunner11,JDK1.6,工程開發(fā)是Java+Flex,完成的是一個(gè)統(tǒng)一登錄接口的測試。具體步驟如下:
1、將Java工程文件打包成jar格式,比如我這里的工程為logindemo,打包jar之后為logindemo.jar
2、將Java工程文件下的lib目錄找出來,lib文件中基本都是jar包,這個(gè)是工程文件的調(diào)用jar包
3、開啟LoadRunner新建一個(gè)Java Vuser的腳本,先運(yùn)行哈,看腳本是否正確執(zhí)行,并保存文件
4、將logindemo.jar和lib下的jar全部導(dǎo)入到LoadRunner的ClassPath下,前提是要保證JDK文件要導(dǎo)入到LR中,在LR中的Vuser——》運(yùn)行時(shí)設(shè)置里,如下圖所示;

5、完成以上操作之后,這個(gè)時(shí)候我們就可以在Vuser腳本中引入Java中的調(diào)用程序了,本人開始用import方式引入的時(shí)候,發(fā)現(xiàn)很奇怪,第一次居然是成功的,但后臺就一直都報(bào)找不到包,暫停了很久,于是用package的方式引入,發(fā)現(xiàn)還是真可以,于是接下來就開始運(yùn)行,發(fā)現(xiàn)很好用,而且程序調(diào)用也確實(shí)是ok的。
注意:這里其實(shí)沒什么特殊的操作,主要是對于jar包的調(diào)用方式上,可能需要注意一下幾點(diǎn):
1、在引入jar包的時(shí)候,比如上圖上面的引入jar文件夾的方式,就不可行,這個(gè)是一個(gè)疑點(diǎn),所以就只能選擇將所有的jar包一并導(dǎo)入就ok了。
2、在Vuser中引入Java包文件時(shí),import方式居然是偶爾行偶爾不行,所以選擇package方式是絕對可行的。
3、在Vuser引入Java包之后運(yùn)行時(shí)可能會出現(xiàn)一些報(bào)錯(cuò),基本都是jar文件情況,只要找對了LoadRunner的Classpath就基本沒問題,還有就是JDK一定要先導(dǎo)入,不然會直接提示進(jìn)程被終止,不能運(yùn)行Java文件。
4、LoadRunner對于Java文件的引入方式有多種,這里調(diào)用jar包的方式是我們平常應(yīng)用最多的,也是最方便的。當(dāng)然還可以將Java編譯后的class文件,復(fù)制到LoadRunner的安裝路徑下的classes下,必須將Java class整個(gè)包文件夾全部復(fù)制才能執(zhí)行,這種方式可需要花費(fèi)調(diào)用的時(shí)間,而且最終要調(diào)用的jar包,還是要到Classpath下導(dǎo)入才行。
至于Java業(yè)務(wù)邏輯的實(shí)現(xiàn),可以在Java中實(shí)現(xiàn),然后Vuser直接調(diào)用方法接口,淡然也可以在Vuser中完成。然后就可以到場景中設(shè)置虛擬用戶數(shù)進(jìn)行場景測試了,然后根據(jù)場景測試的結(jié)果得出分析圖表,跟其他錄制之后的測試方式基本相同。
本文主要學(xué)習(xí)以下幾點(diǎn)內(nèi)容:

以下這些案例,有的帶來巨大的經(jīng)濟(jì)損失,有的帶來很大的人員傷亡,軟件錯(cuò)誤案例真正的影響到了我們的生活,我們對軟件錯(cuò)誤不得不加以特別的重視。

說了半天,到底什么是軟件缺陷呢?用英文說就是Bug。

為什么會出現(xiàn)軟件缺陷呢?是不是軟件測試員不合格呢?其實(shí),不是即使用再好的測試員,軟件測試員,軟件缺陷也是無法避免的,并且軟件缺陷的修復(fù)費(fèi)用會隨時(shí)間的推移,數(shù)十倍的增長。

那么,作為軟件測試員,究竟要做些什么呢?

怎么樣才能成為一名優(yōu)秀的軟件測試員呢?

軟件測試和做科學(xué)實(shí)驗(yàn)一樣,經(jīng)常需要在被測試數(shù)據(jù)之外,增加設(shè)計(jì)一組數(shù)據(jù)作為對比樣本,用以判斷軟件在被測試條件下的實(shí)際執(zhí)行結(jié)果是否與預(yù)期相符。在很多時(shí)候,這個(gè)對比樣本也就是預(yù)期結(jié)果。在工作中,經(jīng)常有測試人員對于如何選取測試對比樣本感到比較疑惑,所以我把個(gè)人在這方面的經(jīng)驗(yàn)總結(jié)成本文。
在我的測試經(jīng)驗(yàn)中,測試對比樣本主要可以通過如下幾種途徑來選取:
1、人工分析;
2、被測軟件的其他輸入;
3、同類軟件。
一、人工分析
人工分析:也就是根據(jù)相關(guān)的需求和設(shè)計(jì)文檔,分析待測程序在被測試條件下的內(nèi)部處理邏輯,以人工方式進(jìn)行計(jì)算、推導(dǎo),得到的預(yù)期結(jié)果。
當(dāng)然,這里說的人工方式,其實(shí)也有可能是測試人員自己編寫一個(gè)小程序或腳本、甚至是電子表格公式來計(jì)算,取決于分析和計(jì)算的復(fù)雜程度。一般情況下,測試對比樣本(預(yù)期結(jié)果)都可以通過這種方式得到。實(shí)際的例子太多了,比如:測試軟件有個(gè)功能是用來改變某個(gè)數(shù)據(jù)對象的狀態(tài)的,可以直接通過設(shè)計(jì)文檔知道該功能會影響哪些數(shù)據(jù)和文件屬性等。
優(yōu)點(diǎn):應(yīng)用廣泛、幾乎適用于所有情況
缺點(diǎn):在復(fù)雜的計(jì)算中較為費(fèi)時(shí)費(fèi)力、只能從正面驗(yàn)證
二、被測軟件的其他輸入
被測軟件的其他輸入:被測試軟件在與被測條件可類比的輸入條件下的執(zhí)行結(jié)果,使被測條件下的實(shí)際結(jié)果是否正確能一目了然。
增加或減少特定的被測條件,就可以直觀地了解到該條件對結(jié)果的影響,從而判斷實(shí)際執(zhí)行結(jié)果是否與該被測條件對結(jié)果的預(yù)期影響相符。特別需要提醒注意,此方法有一個(gè)前提:被測試軟件在其他條件下的執(zhí)行結(jié)果可信賴,也就是用以進(jìn)行類比的測試條件通過了測試。實(shí)際的例子,比如搜索功能里的是否區(qū)分大小寫選項(xiàng),測試時(shí)只需要在不啟用該選項(xiàng)時(shí)的結(jié)果通過測試的情況下,與啟用該選項(xiàng)后的結(jié)果對比即可。
優(yōu)點(diǎn):直觀、比手工分析省時(shí)省力、可進(jìn)行負(fù)面驗(yàn)證
缺點(diǎn):依賴于待測軟件的已驗(yàn)證功能、難以應(yīng)付預(yù)期結(jié)果不是“非此即彼”的測試條件
三、同類軟件
同類軟件:其他可信賴的同類軟件在被測條件或類似條件下的執(zhí)行結(jié)果。
這里的同類軟件,可以是被測試軟件的已通過測試版本,也可以是使用同一設(shè)計(jì)標(biāo)準(zhǔn)的同行(競爭對手)軟件。實(shí)際的例子,如計(jì)算兩個(gè)句子的相似程度。由于該算法比較復(fù)雜,用第一種方式雖然是最準(zhǔn)確的,但是太費(fèi)時(shí)費(fèi)力,在實(shí)際測試中不具有可行性,所以我們一般是用待測軟件的上一個(gè)公開發(fā)布版本作參考。另外一個(gè)選擇是其他競爭對手的同類軟件,但由于算法上的細(xì)節(jié)或多或少肯定有區(qū)別,這類結(jié)果只能作為參考,而不能直接照搬。
優(yōu)點(diǎn):直觀、比手工分析省時(shí)省力
缺點(diǎn):依賴于被測軟件的已測試版本或其他同類軟件
CentOS安裝mysql
原文地址
http://www.centospub.com/make/mysql.html安裝MySQL。[root@sample ~]# yum -y install mysql-server ← 安裝MySQL
[root@sample ~]# yum -y install php-mysql ← 安裝php-mysql
配置MySQL[root@sample ~]#gedit /etc/my.cnf ← 編輯MySQL的配置文件[mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
# Default to using old password format for compatibility with mysql 3.x
# clients (those using the mysqlclient10 compatibility package).
old_passwords=1 ← 找到這一行,在這一行的下面添加新的規(guī)則,讓MySQL的默認(rèn)編碼為UTF-8
default-character-set = utf8 ← 添加這一行
然后在配置文件的文尾填加如下語句:
[mysql]
default-character-set = utf8
啟動(dòng)MySQL服務(wù)[root@sample ~]# chkconfig mysqld on ← 設(shè)置MySQL服務(wù)隨系統(tǒng)啟動(dòng)自啟動(dòng)
[root@sample ~]# chkconfig --list mysqld ← 確認(rèn)MySQL自啟動(dòng)
mysqld 0:off 1:off 2:on 3:on 4:on 5:on 6:off ← 如果2--5為on的狀態(tài)就OK
[root@sample ~]#/etc/rc.d/init.d/mysqld start ← 啟動(dòng)MySQL服務(wù)Initializing MySQL database: [ OK ]
Starting MySQL: [ OK ]
MySQL初始環(huán)境設(shè)定
[1]為MySQL的root用戶設(shè)置密碼
MySQL在剛剛被安裝的時(shí)候,它的root用戶是沒有被設(shè)置密碼的。首先來設(shè)置MySQL的root密碼。
[root@sample ~]# mysql -u root ← 用root用戶登錄MySQL服務(wù)器Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 2 to server version: 4.1.20
Type 'help;' or '\h' for help. Type '\c' to clear the buffer.
mysql> select user,host,password from mysql.user; ← 查看用戶信息
+------+------------------------------+---------------+
| user | host | password |
+------+------------------------------+---------------+
| root | localhost | | ← root密碼為空
| root | sample.centospub.com | | ← root密碼為空
| | sample.centospub.com | |
| | localhost | |
|root | % |XXX |
| | | |
+------+------------------------------+---------------+
4 rows in set (0.00 sec)
mysql> set password for root@localhost=password('在這里填入root密碼'); ← 設(shè)置root密碼
Query OK, 0 rows affected (0.01 sec)
mysql> set password for root@'sample.centospub.com'=password('在這里填入root密碼'); ← 設(shè)置root密碼
Query OK, 0 rows affected (0.01 sec)
只有設(shè)置了這個(gè)才可以,才可以通過數(shù)據(jù)庫來安裝網(wǎng)址
mysql> set password for root@'xxx'=password('xxx'); ← 設(shè)置root密碼
Query OK, 0 rows affected (0.01 sec)
mysql> select user,host,password from mysql.user; ← 查看用戶信息
+------+--------------------------------+--------------------------+
| user | host | password |
+------+--------------------------------+--------------------------+
| root | localhost | 19b68057189b027f | ← root密碼被設(shè)置
| root | sample.centospub.com | 19b68057189b027f | ← root密碼被設(shè)置
| | sample.centospub.com | |
| | localhost | |
+------+--------------------------------+--------------------------+
4 rows in set (0.01 sec)
mysql> exit ← 退出MySQL服務(wù)器
Bye
然后,測試一下root密碼有沒有生效。
[root@sample ~]# mysql -u root ← 通過空密碼用root登錄ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO) ← 出現(xiàn)此錯(cuò)誤信息說明密碼設(shè)置成功
[root@localhost ~]# mysql -u root -h sample.centospub.com ← 通過空密碼用root登錄
ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO) ← 出現(xiàn)此錯(cuò)誤信息說明密碼設(shè)置成功
[root@sample ~]#mysql -u root -p ← 通過密碼用root登錄
Enter password: ← 在這里輸入密碼Welcome to the MySQL monitor. Commands end with ; or \g. ← 確認(rèn)用密碼能夠成功登錄
Your MySQL connection id is 5 to server version: 4.1.20
Type 'help;' or '\h' for help. Type '\c' to clear the buffer.
mysql> exit
Bye
[root@sample ~]# mysql -u root -h sample.centospub.com -p ← 通過密碼用root登錄
Enter password: ← 在這里輸入密碼
Welcome to the MySQL monitor. Commands end with ; or \g. ← 確認(rèn)用密碼能夠成功登錄
Your MySQL connection id is 6 to server version: 4.1.20
Type 'help;' or '\h' for help. Type '\c' to clear the buffer.
mysql> exit ← 退出MySQL服務(wù)器
Bye
[2] 刪除匿名用戶
在MySQL剛剛被安裝后,存在用戶名、密碼為空的用戶。這使得數(shù)據(jù)庫服務(wù)器有無需密碼被登錄的可能性。為消除隱患,將匿名用戶刪除。
[root@sample ~]# mysql -u root -p ← 通過密碼用root登錄
Enter password: ← 在這里輸入密碼
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 7 to server version: 4.1.20
Type 'help;' or '\h' for help. Type '\c' to clear the buffer.
mysql> select user,host from mysql.user; ← 查看用戶信息
+------+----------------------------+
| user | host |
+------+----------------------------+
| | localhost |
| root | localhost |
| | sample.centospub.com |
| root | sample.centospub.com |
+------+----------------------------+
4 rows in set (0.02 sec)
mysql> delete from mysql.user where user=''; ← 刪除匿名用戶
Query OK, 2 rows affected (0.17 sec)
mysql> select user,host from mysql.user; ← 查看用戶信息
+------+----------------------------+
| user | host |
+------+----------------------------+
| root | localhost |
| root | sample.centospub.com |
+------+----------------------------+
2 rows in set (0.00 sec)
mysql> exit ← 退出MySQL服務(wù)器
Bye
好了,下面都不是必須的了!
測試MySQL
[root@sample ~]# mysql -u root -p ← 通過密碼用root登錄
Enter password: ← 在這里輸入密碼
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 9 to server version: 4.1.20
Type 'help;' or '\h' for help. Type '\c' to clear the buffer.
mysql> grant all privileges on test.* to centospub@localhost identified by '在這里定義密碼'; ← 建立對test數(shù)據(jù)庫有完全操作權(quán)限的名為centospub的用戶
Query OK, 0 rows affected (0.03 sec)
mysql> select user from mysql.user where user='centospub'; ← 確認(rèn)centospub用戶的存在與否
+---------+
| user |
+---------+
| centospub | ← 確認(rèn)centospub已經(jīng)被建立
+---------+
1 row in set (0.01 sec)
mysql> exit ← 退出MySQL服務(wù)器
Bye
[root@sample ~]# mysql -u centospub -p ← 用新建立的centospub用戶登錄MySQL服務(wù)器
Enter password: ← 在這里輸入密碼
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 10 to server version: 4.1.20
Type 'help;' or '\h' for help. Type '\c' to clear the buffer.
mysql> create database test; ← 建立名為test的數(shù)據(jù)庫
Query OK, 1 row affected (0.00 sec)
mysql> show databases; ← 查看系統(tǒng)已存在的數(shù)據(jù)庫
+-------------+
| Database |
+-------------+
| test |
+-------------+
1 row in set (0.00 sec)
mysql> use test ← 連接到數(shù)據(jù)庫
Database changed
mysql> create table test(num int, name varchar(50)); ← 在數(shù)據(jù)庫中建立表
Query OK, 0 rows affected (0.03 sec)
mysql> show tables; ← 查看數(shù)據(jù)庫中已存在的表
+-------------------+
| Tables_in_test |
+-------------------+
| test |
+-------------------+
1 row in set (0.01 sec)
mysql> insert into test values(1,'Hello World!'); ← 插入一個(gè)值到表中
Query OK, 1 row affected (0.02 sec)
mysql> select * from test; ← 查看數(shù)據(jù)庫中的表的信息
+------+-------------------+
| num | name |
+------+-------------------+
| 1 | Hello World! |
+------+-------------------+
1 row in set (0.00 sec)
mysql> update test set name='Hello Everyone!'; ← 更新表的信息,賦予新的值
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> select * from test; ← 查看數(shù)據(jù)庫中的表的信息
+------+----------------------+
| num | name |
+------+----------------------+
| 1 | Hello Everyone! | ← 確認(rèn)被更新到新的值
+------+----------------------+
1 row in set (0.01 sec)
mysql> delete from test where num=1; ← 刪除表內(nèi)的值
Query OK, 1 row affected (0.00 sec)
mysql> select * from test; ← 確認(rèn)刪除結(jié)果
Empty set (0.01 sec)
mysql> drop table test; ← 刪除表
Query OK, 0 rows affected (0.01 sec)
mysql> show tables; ← 查看表信息
Empty set (0.00 sec) ← 確認(rèn)表已被刪除
mysql> drop database test; ← 刪除名為test的數(shù)據(jù)庫
Query OK, 0 rows affected (0.01 sec)
mysql> show databases; ← 查看已存在的數(shù)據(jù)庫
Empty set (0.01 sec) ← 確認(rèn)test數(shù)據(jù)庫已被刪除(這里非root用戶的關(guān)系,看不到名為mysql的數(shù)據(jù)庫)
mysql> exit ← 退出MySQL服務(wù)器
Bye
然后,刪除測試用過的遺留用戶。
[root@sample ~]# mysql -u root -p ← 通過密碼用root登錄
Enter password: ← 在這里輸入密碼
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 12 to server version: 4.1.20
Type 'help;' or '\h' for help. Type '\c' to clear the buffer.
mysql> revoke all privileges on *.* from centospub@localhost; ← 取消centospub用戶對數(shù)據(jù)庫的操作權(quán)限
Query OK, 0 rows affected (0.00 sec)
mysql> delete from mysql.user where user='centospub' and host='localhost'; ← 刪除centospub用戶
Query OK, 1 row affected (0.01 sec)
mysql> select user from mysql.user where user='centospub'; ← 查找用戶centospub,確認(rèn)已刪除與否
Empty set (0.01 sec) ← 確認(rèn)centospub用戶已不存在
mysql> flush privileges; ← 刷新,使以上操作生效
Query OK, 0 rows affected (0.01 sec)
mysql> exit
Bye
[root@sample ~]# /etc/rc.d/init.d/httpd restart ← 重新啟動(dòng)HTTP服務(wù)
Stopping httpd: [ OK ]
Starting httpd: [ OK ]
用sudo時(shí)提示"xxx is not in the sudoers file. This incident will be reported.其中XXX是你的用戶名,也就是你的用戶名沒有權(quán)限使用sudo,我們只要修改一下/etc/sudoers文件就行了。下面是修改方法:
1)進(jìn)入超級用戶模式。也就是輸入"su -",系統(tǒng)會讓你輸入超級用戶密碼,輸入密碼后就進(jìn)入了超級用戶模式。(當(dāng)然,你也可以直接用root用)
2)添加文件的寫權(quán)限。也就是輸入命令"chmod u+w /etc/sudoers"。
3)編輯/etc/sudoers文件。也就是輸入命令"vim /etc/sudoers",輸入"i"進(jìn)入編輯模式,找到這一 行:"root ALL=(ALL) ALL"在起下面添加"xxx ALL=(ALL) ALL"(這里的xxx是你的用戶名),然后保存(就是先按一 下Esc鍵,然后輸入":wq")退出。
4)撤銷文件的寫權(quán)限。也就是輸入命令"chmod u-w /etc/sudoers"。
1、添加用戶,首先用adduser命令添加一個(gè)普通用戶,命令如下:
#adduser junguoguo//添加一個(gè)名為junguoguo的用戶
#passwd junguoguo //修改密碼
Changing password for user junguoguo.
New UNIX password: //在這里輸入新密碼
Retype new UNIX password: //再次輸入新密碼
passwd: all authentication tokens updated successfully.
2、賦予root權(quán)限
方法一: 修改 /etc/sudoers 文件,找到下面一行,把前面的注釋(#)去掉
## Allows people in group wheel to run all commands
%wheel ALL=(ALL) ALL
然后修改用戶,使其屬于root組(wheel),命令如下:
#usermod -g root junguoguo
修改完畢,現(xiàn)在可以用junguoguo帳號登錄,然后用命令 su – ,即可獲得root權(quán)限進(jìn)行操作。
方法二: 修改 /etc/sudoers 文件,找到下面一行,在root下面添加一行,如下所示:
## Allow root to run any commands anywhere
root ALL=(ALL) ALL
junguoguo ALL=(ALL) ALL
修改完畢,現(xiàn)在可以用junguoguo帳號登錄,然后用命令 su – ,即可獲得root權(quán)限進(jìn)行操作。
方法三: 修改 /etc/passwd 文件,找到如下行,把用戶ID修改為 0 ,如下所示:
junguoguo:x:500:500:junguoguo:/home/junguoguo:/bin/bash
修改后如下
junguoguo:x:0:500:junguoguo:/home/junguoguo:/bin/bash
保存,用junguoguo賬戶登錄后,直接獲取的就是root帳號的權(quán)限。
之前寫了一篇文章:關(guān)于SQL函數(shù)效率的一些測試與思考,在當(dāng)中提到了將數(shù)據(jù)庫中一對多關(guān)系轉(zhuǎn)換為一對一關(guān)系顯示的兩種方法:第一種方法是在數(shù)據(jù)庫中寫一個(gè)函數(shù),第二種方法為在程序中獲取表Class與表Student所有數(shù)據(jù),然后對比ClassID。
那么除了這兩種方法,還有沒有更快、更好的方法呢?在這里我再介紹兩種方法與大家分享、討論
閑話不多說,下面進(jìn)入正文。還是那兩張表
Student:

Class:

想要獲得的數(shù)據(jù)效果為

第三種方法:使用SQL函數(shù)stuff
SQL語句如下
SELECT C.ID, C.ClassName,stuff((select ',' + S.StuName from dbo.Student S where S.ClassID = C.ID for xml path('')),1,1,'')as stuName FROM Class C |
將第三種方法與第二種方法(在程序中獲取表Class與表Student所有數(shù)據(jù),然后對比ClassID)對比效率,輸出結(jié)果如下:
00:00:00.5497196
00:00:00.3517834
效率比1.562665
=============================
00:00:01.0181020
00:00:00.7060913
效率比1.441884
=============================
00:00:01.4912831
00:00:01.0682834
效率比1.395962
=============================
00:00:01.9636678
00:00:01.4199062
效率比1.382956
=============================
00:00:02.4391574
00:00:01.7712431
效率比1.377088
=============================
00:00:02.9111560
00:00:02.1255719
效率比1.369587
=============================
00:00:03.3923697
00:00:02.5069699
效率比1.353175
=============================
00:00:03.8671226
00:00:02.8594541
效率比1.352399
=============================
00:00:04.3314012
00:00:03.2064415
效率比1.350844
=============================
00:00:04.8019142
00:00:03.5546490
效率比1.350883
=============================
第一個(gè)時(shí)間為第二種方法的執(zhí)行時(shí)間,第二個(gè)時(shí)間為第三種方法執(zhí)行時(shí)間。每種方法循環(huán)了10次以確保數(shù)據(jù)準(zhǔn)確性
關(guān)于測試程序代碼在之前的文章中有提到,改一下SQL語句就可以使用了
數(shù)據(jù)結(jié)果顯示第三種方法要優(yōu)秀不少。至于為什么第三種方法快,我心里已經(jīng)有了個(gè)大致的想法,不過因?yàn)樘y表述了,就懶得浪費(fèi)口水說了,大家記住結(jié)論就好了
接下來介紹第四種方法:在SQL中加載程序集,在查詢時(shí)調(diào)用程序集
加載程序集的方法有些難以表述,感興趣的朋友可以自己去查找相關(guān)資料。在此我貼出主要代碼:
View Code /// <summary> /// The variable that holds the intermediate result of the concatenation /// </summary> private StringBuilder intermediateResult; /// <summary> /// Initialize the internal data structures /// </summary> public void Init() { this.intermediateResult = new StringBuilder(); } /// <summary> /// Accumulate the next value, not if the value is null /// </summary> /// <param name="value"></param> public void Accumulate(SqlString value) { if (value.IsNull) { return; } this.intermediateResult.Append(value.Value).Append(','); } /// <summary> /// Merge the partially computed aggregate with this aggregate. /// </summary> /// <param name="other"></param> public void Merge(Concatenate other) { this.intermediateResult.Append(other.intermediateResult); } /// <summary> /// Called at the end of aggregation, to return the results of the aggregation. /// </summary> /// <returns></returns> public SqlString Terminate() { string output = string.Empty; //delete the trailing comma, if any if (this.intermediateResult != null && this.intermediateResult.Length > 0) { output = this.intermediateResult.ToString(0, this.intermediateResult.Length - 1); } return new SqlString(output); } public void Read(BinaryReader r) { intermediateResult = new StringBuilder(r.ReadString()); } public void Write(BinaryWriter w) { w.Write(this.intermediateResult.ToString()); } |
這個(gè)方法比第三種方法快得不多,大概只有5%到10%的性能提升,但是這種方法十分優(yōu)雅,我竊以為這種方法是解決一對多關(guān)系轉(zhuǎn)換一對一方法中最好的方法
PS:最近太懶了,都沒有來寫東西。罪過罪過
再PS:想吐槽一下,最近園子里幾個(gè)小妹子寫的生活上的雜七雜八的東西居然引起了那么多人的追捧,而真正的技術(shù)貼卻是無人問津,不得不說是一種悲哀
再再PS:歡迎留言討論,歡迎轉(zhuǎn)載。不足之處望海涵
LoadRunner controller將使用驅(qū)動(dòng)程序mmdrv運(yùn)行Vuser。用戶可以在controller的run-time setting中選擇Vuser的運(yùn)行方式, 是多進(jìn)程方式or多線程方式。
如果選擇以線程方式來運(yùn)行虛擬用戶:
在場景設(shè)置時(shí),“是單行腳本,還是多行腳本”會決定系統(tǒng)啟動(dòng)的進(jìn)程數(shù)的多少: 假設(shè)并發(fā)用戶設(shè)置為30,如果是單行30個(gè)用戶,系統(tǒng)只需啟動(dòng)一個(gè)進(jìn)程; 假設(shè)并發(fā)用戶設(shè)置為30,如果是多行,30行,每行一個(gè)用戶,系統(tǒng)就需要啟動(dòng)30個(gè)進(jìn)程;
如果選擇以線程程方式來運(yùn)行虛擬用戶:
那么無論腳本在場景組中怎么設(shè)置,是單行多用戶還是多行少用戶方式,系統(tǒng)需要啟動(dòng)的進(jìn)程數(shù)是一定的,就是并發(fā)用戶的總數(shù);
進(jìn)程方式和線程方式的優(yōu)缺點(diǎn):
如果選擇按照進(jìn)程方式運(yùn)行,每個(gè)用戶都將啟動(dòng)一個(gè)mmdrv進(jìn)程,多個(gè)mmdrv進(jìn)程會占用大量內(nèi)存及其他系統(tǒng)資源,這就限制了可以在任一負(fù)載生成器上運(yùn)行的并發(fā)用戶數(shù)的數(shù)量,因?yàn)樨?fù)載機(jī)的資源(內(nèi)存及其他系統(tǒng)資源)是有限的。 如果選擇按照線程方式運(yùn)行,在默認(rèn)情況下,controller為每50個(gè)用戶僅啟動(dòng)一個(gè)mmdrv進(jìn)程,而每個(gè)用戶都按線程方式來運(yùn)行,這些線程用戶將共享父進(jìn)程的內(nèi)存段,這就節(jié)省了大量內(nèi)存空間,從而可以在一個(gè)負(fù)載生成器上運(yùn)行更多的用戶。(如果選擇線程方式來運(yùn)行用戶,每個(gè)進(jìn)程中會多出幾個(gè)線程,例如是53個(gè),多出來的進(jìn)程可能是用于維護(hù)進(jìn)程之間的運(yùn)行的) 選擇線程方式雖然可以減少啟動(dòng)的mmdrv進(jìn)程數(shù),減少了內(nèi)存的占用,但是也容易出現(xiàn)一個(gè)問題,例如,同一個(gè)測試場景,用線程并發(fā)就會出現(xiàn)超時(shí)失敗或報(bào)錯(cuò),而用進(jìn)程并發(fā)就沒錯(cuò)。為什么呢?因?yàn)榫€程的資源是從進(jìn)程資源中分配出來的,因此同一個(gè)進(jìn)程中的多個(gè)線程會有共享的內(nèi)存空間,假設(shè)a線程要用資源就必須等待b線程釋放,而b線程也在等待其他資源釋放才能繼續(xù),這樣就會出現(xiàn)這個(gè)問題。
系統(tǒng)需要啟動(dòng)的mmdrv進(jìn)程數(shù)與哪些因素有關(guān):
與在controller 的運(yùn)行時(shí)設(shè)置中選擇的是進(jìn)程方式or線程方式來運(yùn)行虛擬用戶有關(guān) 進(jìn)程方式:無論是單行or多行腳本,需要啟動(dòng)的進(jìn)程數(shù)就是并發(fā)用戶數(shù); 線程方式:假設(shè)是單行腳本,每50個(gè)用戶才啟動(dòng)一個(gè)進(jìn)程;多行腳本,有幾行(每行<50人)就啟動(dòng)幾個(gè)進(jìn)程,而不是每個(gè)用戶啟動(dòng)一個(gè)進(jìn)程。 如果選擇了線程方式,需啟動(dòng)的進(jìn)程數(shù),進(jìn)一步還與腳本是單行還是多行有關(guān) 單行腳本,多用戶,假設(shè)少于50,只需啟動(dòng)一個(gè)進(jìn)程,100個(gè)用戶,只需啟動(dòng)2個(gè)進(jìn)程,依此類推; 多行腳本,即使每行一個(gè)用戶,也需要啟動(dòng)一個(gè)進(jìn)程,多一行就需要多啟動(dòng)一個(gè)進(jìn)程;不是每個(gè)用戶啟動(dòng)一個(gè)進(jìn)程,有幾行(每行<50人)就需要啟動(dòng)幾個(gè)進(jìn)程。 在啟動(dòng)了IP欺騙功能后,所需啟動(dòng)的進(jìn)程數(shù),還與選擇的是按進(jìn)程還是按線程來分配IP地址有關(guān) 按進(jìn)程分IP:每個(gè)ip(負(fù)載生成器)就需要多啟動(dòng)一個(gè)進(jìn)程; 按線程分IP:每個(gè)ip(負(fù)載生成器)不需要多啟動(dòng)一個(gè)進(jìn)程。