顧名思義,標記-清除算法分爲兩個階段,標記(mark)和清除(sweep).html
在標記階段,collector從mutator根對象開始進行遍歷,對從mutator根對象能夠訪問到的對象都打上一個標識,通常是在對象的header中,將其記錄爲可達對象。java
而在清除階段,collector對堆內存(heap memory)從頭至尾進行線性的遍歷,若是發現某個對象沒有標記爲可達對象-經過讀取對象的header信息,則就將其回收。node
從上圖咱們能夠看到,在Mark階段,從根對象1能夠訪問到B對象,從B對象又能夠訪問到E對象,因此B,E對象都是可達的。同理,F,G,J,K也都是可達對象。到了Sweep階段,全部非可達對象都會被collector回收。同時,Collector在進行標記和清除階段時會將整個應用程序暫停(mutator),等待標記清除結束後纔會恢復應用程序的運行。linux
缺點:web
標記-清除算法的比較大的缺點就是垃圾收集後有可能會形成大量的內存碎片,像上面的圖片所示,垃圾收集後內存中存在三個內存碎片,假設一個方格表明1個單位的內存,若是有一個對象須要佔用3個內存單位的話,那麼就會致使Mutator一直處於暫停狀態,而Collector一直在嘗試進行垃圾收集,直到Out of Memory。算法
顧名思義,標記-壓縮算法分爲兩個階段,標記(mark)和壓縮(compact).sql
其中標記階段跟標記-清除算法中的標記階段是同樣的,而對於壓縮階段,它的工做就是移動全部的可達對象到堆內存的同一個區域中,使他們緊湊的排列在一塊兒,從而將全部非可達對象釋放出來的空閒內存都集中在一塊兒,經過這樣的方式來達到減小內存碎片的目的。數據庫
堆內存對半分爲兩個半區,只用其中一個半區來進行對象內存的分配,若是在這個半區內存不夠給新的對象分配了,那麼就開始進行垃圾收集,將這個半區中的全部可達對象都拷貝到另一個半區中去,而後繼續在另外那個半區進行新對象的內存分配。express
**缺點: **編程
內存壓縮爲原來的一半,利用率比較低,典型的空間換時間
經過在對象頭中分配一個空間來保存該對象被引用的次數。若是該對象被其它對象引用,則它的引用計數加一,若是刪除對該對象的引用,那麼它的引用計數就減一,當該對象的引用計數爲0時,那麼該對象就會被回收。
採用引用計數的垃圾收集機制跟前面三種垃圾收集機制最大的不一樣在於,垃圾收集的開銷被分攤到整個應用程序的運行當中了,而不是在進行垃圾收集時,要掛起整個應用的運行,直到對堆中全部對象的處理都結束。所以,採用引用計數的垃圾收集不屬於嚴格意義上的"Stop-The-World"的垃圾收集機制。
注意:
當某個對象的引用計數減爲0時,collector須要遞歸遍歷它所指向的全部域,將它全部域所指向的對象的引用計數都減一,而後才能回收當前對象。
可是這種引用計數算法有一個比較大的問題,那就是它不能處理環形數據 - 即若是有兩個對象相互引用,那麼這兩個對象就不能被回收,由於它們的引用計數始終爲1。這也就是咱們常說的「內存泄漏」問題。以下圖:
當前的商業虛擬機都採用的是」分代收集「算法,通常是把java堆分紅新生代和老生代,這樣就能夠根據各個年代的特色採用最適當的垃圾收集算法,新生代中,對象大可能是」朝生夕死「能夠採用複製算法,而老年代的對象存活率比較高,並且沒有擔保空間進行內存分配,就要採用」標記-清除算法「或者」標記-整理「算法。
## 2、Java垃圾回收
其中,堆內存分爲年輕代和年老代,非堆內存主要是Permanent區域,主要用於存儲一些類的元數據,常量池等信息。而年輕代又分爲兩種,一種是Eden區域,另一種是兩個大小對等的Survivor區域。
部分的新建立對象分配在新生代。由於大部分對象很快就會變得不可達,因此它們被分配在新生代,而後消失再也不。當對象重新生代移除時,咱們稱之爲"Minor GC"。新生代使用的是複製收集算法。
新生代劃分爲三個部分:分別爲Eden、Survivor from、Survivor to,大小比例爲8:1:1(爲了防止複製收集算法的浪費內存過大)。每次只使用Eden和其中的一塊Survivor,回收時將存活的對象複製到另外一塊Survivor中,這樣就只有10%的內存被浪費,可是若是存活的對象總大小超過了Survivor的大小,那麼就把多出的對象放入老年代中。
在三個區域中有兩個是Survivor區。對象在三個區域中的存活過程以下:
如上所述,兩個Survivor區域在任什麼時候候一定有一個保持空白。若是同時有數據存在於兩個Survivor區或者兩個區域的的使用量都是0,則意味着你的系統可能出現了運行錯誤。
存活在新生代中但未變爲不可達的對象會被複制到老年代。通常來講老年代的內存空間比新生代大,因此在老年代GC發生的頻率較新生代低一些。當對象從老年代被移除時,咱們稱之爲 "Major GC"(或者Full GC)。 老年代使用標記-清理或標記-整理算法
在發生Minor GC前,虛擬機會先檢查老年代最大可用的連續空間是否大於新生代全部對象總空間。
若是大於,那麼Minor GC能夠確保是安全的。
若是小於,虛擬機會查看HandlePromotionFailure設置值是否容許擔任失敗。
前面提到過,新生代使用複製收集算法,但爲了內存利用率,只使用其中一個Survivor空間來做爲輪換備份,所以當出現大量對象在Minor GC後仍然存活的狀況時(最極端就是內存回收後新生代中全部對象都存活),就須要老年代進行分配擔保,讓Survivor沒法容納的對象直接進入老年代。與生活中的貸款擔保相似,老年代要進行這樣的擔保,前提是老年代自己還有容納這些對象的剩餘空間,一共有多少對象會活下來,在實際完成內存回收以前是沒法明確知道的,因此只好取以前每一次回收晉升到老年代對象容量的平均大小值做爲經驗值,與老年代的剩餘空間進行比較,決定是否進行Full GC來讓老年代騰出更多空間。
取平均值進行比較其實仍然是一種動態機率的手段,也就是說若是某次Minor GC存活後的對象突增,遠遠高於平均值的話,依然會致使擔保失敗(Handle Promotion Failure)。若是出現了HandlePromotionFailure失敗,那就只好在失敗後從新發起一次Full GC。雖然擔保失敗時繞的圈子是最大的,但大部分狀況下都仍是會將HandlePromotionFailure開關打開,避免Full GC過於頻繁。
Serial收集器(Serial/Serial Old)
Serial是一個單線程的收集器,但它的「單線程」意義並不只僅說明它只會使用一個CPU或一條手機此案成去完成垃圾和收集工做,更重要的是它進行垃圾收集時,必須暫停其餘全部的工做線程,直到它收集結束。
ParNew收集器
ParNew收集器其實就是Serial收集器的多線程版本。
它是運行在Server模式下的虛擬機中首選的新生代收集器,其中有一個與性能無關但很重要的緣由是:除了Serial收集器外,目前只有它能與CMS收集器配合工做。
Parallel Scavenge收集器
該收集器也是一個新生代的垃圾收集器,他也是使用複製算法的收集器,又是一個並行的垃圾收集器。該收集器的特色是他的關注點與其餘的收集器不一樣,CMS等收集器的關注點是儘量縮短垃圾回收時用戶線程的停頓時間,而parallel Scavenge收集器的目標是達到一個可控制的吞吐量。所謂吞吐量就是CPU用於運行代碼的時間與CPU總消耗時間的比值,即吞吐量=運行用戶代碼時間/(運行用戶代碼時間+垃圾回收時間),好比虛擬機總共運行100分鐘,垃圾回收佔用了1分鐘,那麼吞吐量就是99%。
Parallel Old收集器
Parallel Old是Parallel Scavenge收集器的老年代版本,使用多線程和「標記-整理」算法。
CMS收集器
CMS(Concurrent Mark Sweep)收集器是一種以獲取最短回收停頓時間爲目標的收集器。CMS是基於「標記-清除」算法實現的,它的運做過程相對於前面幾種收集器來講更復雜一些,整個過程分爲4個步驟,包括:
其中,初始標記、從新標記這兩個步驟仍然須要」Stop The world」。初始標記僅僅只是標記一下GC Roots Tracing的過程,而從新標記階段則是爲了修正併發標記期間因用戶程序繼續運做而致使標記產生變更的那一部分對象的標記記錄,這個階段的停頓時間通常會比初始標記階段稍長一些,但遠比並發標記的時間短。
因爲整個過程當中耗時最長的併發標記和併發清除過程收集器線程均可以與用戶線程一塊兒工做,因此,從整體上來講,CMS收集器的內存回收過程是與用戶線程一塊兒併發執行的。
**CMS的優點:**併發收集、低停頓。
CMS的缺點:
G1收集器
G1是一款面向服務端應用的垃圾收集器。HOtSpot開發團隊賦予它的使命是將來能夠替換掉CMS收集器。
G1具有以下特色:
G1垃圾收集器和CMS垃圾收集器有幾點不一樣。首先,最大的不一樣是內存的組織方式變了。Eden,Survivor和Tenured等內存區域再也不是連續的了,而是變成了一個個大小同樣的region - 每一個region從1M到32M不等。
一個region有可能屬於Eden,Survivor或者Tenured內存區域。圖中的E表示該region屬於Eden內存區域,S表示屬於Survivor內存區域,T表示屬於Tenured內存區域。圖中空白的表示未使用的內存空間。G1垃圾收集器還增長了一種新的內存區域,叫作Humongous內存區域,如圖中的H塊。這種內存區域主要用於存儲大對象-即大小超過一個region大小的50%的對象。
在G1垃圾收集器中,年輕代的垃圾回收過程跟PS垃圾收集器和CMS垃圾收集器差很少。
對於年老代上的垃圾收集,G1垃圾收集器也分爲4個階段,基本跟CMS垃圾收集器同樣,但略有不一樣:
Initial Mark階段 - 同CMS垃圾收集器的Initial Mark階段同樣,G1也須要暫停應用程序的執行,它會標記從根對象出發,在根對象的第一層孩子節點中標記全部可達的對象。可是G1的垃圾收集器的Initial Mark階段是跟minor gc一同發生的。也就是說,在G1中,你不用像在CMS那樣,單獨暫停應用程序的執行來運行Initial Mark階段,而是在G1觸發minor gc的時候一併將年老代上的Initial Mark給作了。
Concurrent Mark階段 - 在這個階段G1作的事情跟CMS同樣。但G1同時還多作了一件事情,那就是,若是在Concurrent Mark階段中,發現哪些Tenured region中對象的存活率很小或者基本沒有對象存活,那麼G1就會在這個階段將其回收掉,而不用等到後面的clean up階段。這也是Garbage First名字的由來。同時,在該階段,G1會計算每一個 region的對象存活率,方便後面的clean up階段使用 。
Remark階段 - 在這個階段G1作的事情跟CMS同樣, 可是採用的算法不一樣,可以在Remark階段更快的標記可達對象。
Clean up/Copy階段 - 在G1中,沒有CMS中對應的Sweep階段。相反 它有一個Clean up/Copy階段,在這個階段中,G1會挑選出那些對象存活率低的region進行回收,這個階段也是和minor gc一同發生的,以下圖所示:
從上能夠看到,因爲Initial Mark階段和Clean up/Copy階段都是跟minor gc同時發生的,相比於CMS,G1暫停應用程序的時間更少,從而提升了垃圾回收的效率。
內建基本數學函數
cos 餘弦函數 (弧度制) sin 正弦函數 (弧度制) tan 正切函數 (弧度制) exp 指數函數 (e x ) log 以 e 爲底的指數函數 log10 以 10 爲底的指數函數 sinh 雙曲正弦函數 tanh 雙曲正切函數 cosh 雙曲餘弦函數 acos 反餘弦函數 acosh 反雙曲餘弦函數 asin 反正弦函數 asinh 反雙曲正弦函數 atan 反正切函數 atanh 反雙曲正切函數 abs 絕對值函數 (複數取模) round 四捨五入 floor 近似爲比它小的最大整數 ceil 近似爲比它大的最小整數 fix 向 0 方向近似 rem 求餘數
變量
Octave 中變量的類型是不用聲明的。Octave 全部的變量都是浮點型或者字符串。
如:deg=pi/180
注:ans 變量存儲你每次最近運算的結果。
數組和向量
示例:a = [1 4 5] b = [1, 4, 5] c = [1; 4; 5] 在方括號中由空格或者逗號隔開的一組數據被定義爲行向量; 而由分號或者回車隔開的一組數據被定義爲列向量。
冒號表達式
示例:a = 2: 6 即 a = [2 3 4 5 6]
a = 2: 0.5: 4 即 a = [2.0000 2.5000 3.0000 3.5000 4.0000]
向量中的元素操做
a=[1:2:6 -1 0] 則 a(3) 爲 5
注:向量中的元素經過括號 (),而第一個元素的編號爲 1
向量計算
使用 +− 算符,你一樣能夠對該向量中的每一個元素都加上或者減去一個數值。
兩個向量的相乘遵循矩陣的乘法法則,向量乘法並非對應元素的相乘。若是要進行對應元素的乘除法,你能夠使用
.* 和 ./ (注意前面有個點)
基本畫圖命令: plot(x, y) x爲橫軸,y爲縱軸
控制語句
判斷語句:if expression statements elseif expression statements
else statements end
switch語句 :switch x
case x1 statements case x2 statements otherwise statements end
for 循環:for variable=vector statements end
while循環:while expression statements end
函數
示例:function s=sind(x) % SIND(x) Calculates sine(x) in degrees s=sin(x*pi/180); endfunction
矩陣和向量
在 Octave 中輸入矩陣與輸入向量類似,逐行輸入: octave:##> A= [ 5 7 9 -1 3 -2 ]
或者使用分號來標定一行的結束,例如: octave:##> B=[2 0; 0 -1; 1 0] octave:##> B= 2 0 0 -1 1 0
其餘: 單位矩陣建立: I = eye(4)
對角矩陣建立: M = diag([-1 7 4]) -1 7 4 爲對角的值
矩陣轉置符 如:A'
提取矩陣元
如: J(1, 3) 1, 3 分別爲行號和列號
J(1:3, 5) 1到3行,第五列
賦值
如:J(1, 3) = 4
基本矩陣函數
eye 建立單位矩陣 zeros 建立全零矩陣 ones 建立全一矩陣 rand 建立隨機數矩陣 diag 建立一個對角矩陣,或者提取一個矩陣的對角元 inv 求矩陣逆矩陣 trace 求矩陣的跡 rank 求矩陣的秩
機器學習的兩種方式:
有監督學習:相似與我知道一個問題的答案,因此我能夠從這個答案問題出發設計出一個推理邏輯。
受監督的學習問題分爲「迴歸」和「分類」問題。在迴歸問題中,咱們試圖在連續輸出中預測結果,這意味着咱們正在嘗試將輸入變量映射到一些連續函數。在分類問題中,咱們試圖用離散輸出來預測結果。換句話說,咱們正在嘗試將輸入變量映射到離散類別。
示例: 迴歸 - 鑑於一我的的照片,咱們必須根據給定的圖片來預測他們的年齡
分類 - 鑑於腫瘤患者,咱們必須預測腫瘤是惡性仍是良性
無監督學習:相似於我給你一堆數據,你也不知道它是幹什麼用的,可是你或許能夠找出這些數據中蘊含的某種規律。無監督學習問題可分爲聚類和非聚類兩種。
聚類:收集100萬個不一樣的基因,並找到一種自動將這些基因組合成不一樣變量(如壽命,位置,做用等)類似或相關的組。
非聚類:「雞尾酒會算法」,讓您在混亂的環境中找到結構。(即從雞尾酒會的聲音網格中識別我的的聲音和音樂)。
模型表示
爲了更準確地描述監督學習問題,咱們的目標是給出一個訓練集,以學習一個函數h:X→Y,使得h(x)是相應的y值的「好」預測因子。因爲歷史緣由,這個函數h被稱爲假設。從形象上看,這個過程是這樣的:
成本函數
咱們能夠經過使用成本函數來衡量假設函數的準確性。這取決於x的輸入和實際輸出y的假設的全部結果的平均差別(其實是平均值的平均值)。
梯度降低
上圖中的每一個「星」之間的距離表示由咱們的參數α肯定的步長。較小的α將致使較小的步長,較大的α致使較大的步長。採起步驟的方向由偏導數決定Ĵ(i0,θ1)。根據圖上的哪個開始,人們可能會在不一樣的地方結束。上圖顯示了兩個不一樣的起點,最終出如今兩個不一樣的地方。
下圖展現了梯度向下的算式,當左式恆等於右式之時,找到局部最優解。
線性迴歸的梯度降低
![2017-10-24 21-50-27屏幕截圖](/home/tofar/圖片/2017-10-24 21-50-27屏幕截圖.png)
上面所示的橢圓是二次函數的輪廓。還顯示了由(48,30)初始化的梯度降低所採起的軌跡。圖中的x(由直線鏈接)標記梯度降低通過的θ的連續值,由於它收斂到最小值。
特徵縮放
們能夠經過使咱們的每一個輸入值在大體相同的範圍內來加快梯度降低。這是由於在較小的範圍內,θ會快速降低,而在較大的範圍內會慢慢降低,所以當變量很是不均勻時,它會低效地擺動到最佳狀態。
防止這種狀況的方法是修改輸入變量的範圍,使其大體相同。理想的狀況是:
-1 <= X <= 1 或者 -0.5 <= X <= 0.5
計算公式:
![2017-10-26 14-26-15屏幕截圖](/home/tofar/圖片/2017-10-26 14-26-15屏幕截圖.png)
分母爲範圍。。。。
學習比率
**調試梯度降低。**在x軸上繪製一個迭代次數的圖。如今繪製成本函數J(θ)超過梯度降低次數。若是J(θ)增長,那麼您可能須要減小α。
若是 一 過小:收斂緩慢
若是 一 太大:每次迭代都不能減小,從而可能不會收斂。
特徵和多項式迴歸
咱們能夠經過幾種不一樣的方式改進咱們的特徵和咱們的假設函數的形式。
咱們能夠將多個功能組合成一個。例如,咱們能夠結合X1 和 X2 成爲新功能 X3 經過服用 X1⋅X2.
若是不符合數據,咱們的假設函數不須要是線性的(直線)。
咱們能夠經過使其成爲二次,立方或平方根函數(或任何其餘形式)來改變假設函數的行爲或曲線。
例如,若是咱們的假設函數是 H我(x)= θ0+ θ1X1 那麼咱們能夠建立基於的附加功能 X1,獲得二次函數 H我(x)= θ0+ θ1X1+ θ2X21 或立方函數 H我(x)= θ0+ θ1X1+ θ2X21+ θ3X31
在立方體版本中,咱們建立了新功能 X2 和 X3 哪裏 X2= x21 和 X3= x31.
爲了使其成爲平方根函數,咱們能夠作: H我(x)= θ0+ θ1X1+ θ2√X1
正規方程法
計算公式:θ=(XTX)−1XTy
如下是梯度降低與正態方程的比較:
梯度降低 | 正常方程式 |
---|---|
須要選擇alpha | 不須要選擇alpha |
須要不少次迭代 | 不須要迭代 |
T至ñ2) | Tñ3),須要計算倒數 XŤX |
當n大時,效果很好 | 若是n很是大,則慢 |
正態方程不可逆
當在八度中實現正態方程時,咱們要使用'pinv'函數而不是'inv'。'pinv'功能會給你一個值我 即便 XŤX 是不可逆的
若是 XŤX是**不可逆的,**常見的緣由多是:
解決上述問題的方法包括刪除與另外一個線性相關的特徵或者當具備太多特徵時刪除一個或多個特徵。
邏輯迴歸
咱們不能使用與線性迴歸相同的成本函數,由於邏輯函數會致使輸出波浪形,致使許多局部最優。換句話說,它不會是一個凸函數。
相反,咱們用於邏輯迴歸的成本函數以下所示:
優化
**"Conjugate gradient", "BFGS", and "L-BFGS" **are more sophisticated, faster ways to optimize θ that can be used instead of gradient descent. We suggest that you should not write these more sophisticated algorithms yourself (unless you are an expert in numerical computing) but use the libraries instead, as they're already tested and highly optimized. Octave provides them.
一對多
因爲y = {0,1 ... n},咱們將問題劃分爲n + 1(+1,由於索引從0開始)二進制分類問題; 在每一個類中,咱們預測'y'是咱們其中一個類的成員的機率。
y∈{0,1...n}
h(0)θ(x)=P(y=0|x;θ)
h(1)θ(x)=P(y=1|x;θ)
⋯
h(n)θ(x)=P(y=n|x;θ)
prediction=maxi(h(i)θ(x))
複製代碼
過分擬合
圖中左圖爲欠擬合,中間的圖片差很少正好,右圖爲過擬合
低估或高誤差是當咱們的假設函數的形式h映射到數據的趨勢。它一般是由一個功能太簡單或功能太少形成的。在另外一個極端,過分擬合或高度變異是由適合可用數據的假設函數引發的,但不能很好地推廣以預測新的數據。這一般是由一個複雜的函數形成的,這個函數會產生大量與數據無關的沒必要要的曲線和角度。
有兩個主要的選擇來解決過分擬合的問題:
1)減小功能的數量:
2)正規化
正則化和處罰機制
使用上述成本函數與額外的總和,咱們能夠平滑咱們的假設函數的輸出,以減小過分擬合。若是選擇的lambda太大,可能會使功能過於平滑,致使不足。
正規化線性迴歸
梯度向下
...
正規方程
是一個矩陣,左上角爲0,下角爲1,其餘地方爲0。它應該有尺寸(n + 1)×(n + 1)。直覺上,這是身份矩陣(雖然咱們不包括在內)X0)乘以單個實數λ。
回想一下,若是m <n,那麼 XŤX是不可逆的。可是,當咱們添加術語λ⋅L時,XŤX +λ⋅L變成可逆的。
正則化邏輯迴歸
邏輯迴歸的成本函數:
...
正則化邏輯迴歸的成本函數:
...
神經網絡
模型表示
讓咱們來看看如何使用神經網絡來表示一個假設函數。在一個很是簡單的層面上,神經元基本上是計算單位,它們將輸入(樹突)做爲輸入(軸突)的電輸入(稱爲「尖峯」 )。在咱們的模型中,咱們的樹突就像輸入的特徵X1⋯xñ,輸出是咱們假設函數的結果。在這個模型中咱們X0輸入節點有時被稱爲「偏置單元」。它老是等於1.在神經網絡中,咱們使用與分類中相同的邏輯函數,1/(1 + e- θŤX),但咱們有時將其稱爲sigmoid(邏輯)激活功能。在這種狀況下,咱們的「theta」參數有時被稱爲「權重」。
例如:
Example: If layer 1 has 2 input nodes and layer 2 has 4 activation nodes. Dimension of Θ(1) is going to be 4×3 where sj=2 and sj+1=4, so sj+1×(sj+1)=4×3.
應用
an example of the logical operator 'OR', meaning either x1 is true or x~2~ is true, or both:
Where g(z) is the following:
Examples and Intuitions II
here we have the XNOR operator using a hidden layer with two nodes! The following summarizes the above algorithm:
多類分類
爲了將數據分類到多個類中,咱們假設函數返回值的向量。說咱們想將咱們的數據分爲四類。咱們將使用下面的例子來看看這個分類是如何完成的。該算法將圖像做爲輸入並進行相應的分類:
咱們能夠將咱們的結果類定義爲y:
代價函數
注意:
反向傳播算法
https://www.coursera.org/learn/machine-learning/supplement/pjdBA/backpropagation-algorithm
https://www.coursera.org/learn/machine-learning/supplement/v5Bu8/backpropagation-intuition
梯度檢驗
gradApprox矢量計算方法:
A small value for ϵ (epsilon) such as ϵ=10^−4^, guarantees that the math works out properly.
epsilon = 1e-4;
for i = 1:n,
thetaPlus = theta;
thetaPlus(i) += epsilon;
thetaMinus = theta;
thetaMinus(i) -= epsilon;
gradApprox(i) = (J(thetaPlus) - J(thetaMinus))/(2*epsilon)
end;
複製代碼
一旦咱們計算咱們的gradApprox矢量,咱們能夠檢查gradApprox≈deltaVector。
一旦你已經驗證**,一旦**你的BP算法是正確的,則不須要再次計算gradApprox。計算gradApprox的代碼可能很是慢。
隨機初始化
將全部的權重初始化爲零不適用於神經網絡。反向傳播時,全部節點將重複更新爲相同的值。相反,咱們能夠隨機初始化咱們的權重釷 矩陣使用如下方法:
培訓一個神經網絡
cat
注:只能建立新文件,不能編輯已有文件.
將幾個文件合併爲一個文件: $cat file1 file2 > file
參數:
-n 或 --number 由 1 開始對全部輸出的行數編號 -b 或 --number-nonblank 和 -n 類似,只不過對於空白行不編號 -s 或 --squeeze-blank 當遇到有連續兩行以上的空白行,就代換爲一行的空白行
建立文件,建立文件後,要以EOF或STOP結束;如:$ cat > linuxsir.org.txt << EOF
追加內容 如:$ cat >> linuxsir.txt << EOF
一個或多個已存在的文件內容,追加到一個已存在的文件中
如:$ cat sir01.txt sir02.txt sir03.txt >> sir00.txt (與 cat sir01.txt sir02.txt sir03.txt > sir04.txt 區分,此爲合併)
文件屬性
- rwx r-x r--
1 234 567 890
1表明問文件名或者目錄, 234表明擁有者的權限,可讀、可寫、可執行(rwx),567表明同用戶組權限,890表明其餘用戶權限
chgrp: 改變文件所屬用戶組
chown:改變文件全部者 例如: chown [-R] 帳號名稱 文件或者目錄 (-R 遞歸)
chmod:改變文件權限
權限分數: r : 4 w: 2 x:1
例如: chmod 777 filename (4+2+1=7)
chmod u =rwx, g=rx, 0=r filename (u-user, g-group, o-others)
chmod a+x filename 增長權限
a-x 減小權限
應用層就定義了位於不一樣主機中的多個應用進程之間通訊的協議。應用層的許多協議都是基於客戶-服務器模式,客戶是服務的請求方,服務器是服務提供方。
非持續鏈接
定義:每一個請求/相應對是結果一個單獨的TCP鏈接發送
咱們看看在非持續鏈接狀況下,從服務器向客戶傳送一個Web頁面的步驟。假設該頁面含有一個HTML基本文件和10個JPEG圖形,而且這11個對象位於同一臺服務器上。該HTML文件的URL爲:http://www.someSchool.edu/someDepartment/home.index。
咱們看看發生了什麼狀況:
對每一個引用的JPEG圖形對象重複前4個步驟。
往返時間計算:粗略地講,總的響應時間就是兩個RTT加上服務器傳輸HTML文件的時間。
持續鏈接
定義:全部請求及其響應通過相同的TCP鏈接發送
非持續鏈接有一些缺點。首先,必須爲每個請求的對象創建和維護一個全新的鏈接。對於每一個這樣的鏈接,在客戶和服務器中都要分配TCP的緩衝區和保持TCP變量,這給Web服務器帶來了嚴重的負擔,由於一臺Web服務器可能同時服務於數以百計不一樣的客戶的請求。第二,就像咱們剛描述的那樣,每個對象經受兩倍RTT的交付時延,即一個RTT用於建立TCP,另外一個RTT用於請求和接收一個對象。
在採用持續鏈接的狀況下,服務器在發送響應後保持該TCP鏈接打開。在相同的客戶與服務器之間的後續請求和響應報文可以經過相同的鏈接進行傳送。特別是,一個完整的Web頁面(上例中的HTML基本文件加上10個圖形)能夠用單個持續TCP鏈接進行傳送。更有甚者,位於同一臺服務器的多個Web頁面在從該服務器發送給同一個客戶時,能夠在單個持續TCP鏈接上進行。能夠一個接一個地發出對對象的這些請求,而沒必要等待對未決請求(流水線)的回答。通常來講,若是一條鏈接通過必定時間間隔(一個可配置的超時間隔)仍未被使用,HTTP服務器就關閉該鏈接。HTTP的默認模式是使用帶流水線的持續鏈接。
HTTP請求報文
GET /somedir/page.html HTTP/1.1
HOST: www.someschool.edu
Connetion: close
User-agent: Mozilla/5.0
Accept-agent: fr
複製代碼
HTTP響應報文
HTTP/1.1 200 OK
Date: Sat, 31 Dec 2005 23:59:59 GMT
Content-Type: text/html;charset=ISO-8859-1
Content-Length: 122
<html>
<head>
<title>Wrox Homepage</title>
</head>
<body>
<!-- body goes here -->
</body>
</html>
複製代碼
用戶與服務器交互:cookie
因爲HTTP是無狀態的,咱們能夠使用cookie來對用戶進行認證。
web緩存器(代理服務器)
請求過程:
瀏覽器創建一個到Web緩存器的TCP鏈接,並向Web緩存器中的對象發送一個HTTP請求。
Web緩存器進行檢查,看看本地是否存儲了該對象副本。若是有,Web緩存器就向客戶瀏覽器用HTTP響應報文返回該對象。
若是Web緩存器中沒有該對象,它就打開一個與該對象的初始服務器(如www.someschool.edu)的TCP鏈接。Web緩存器則在這個緩存器到服務器的TCP鏈接上發送一個對該對象的HTTP請求。在收到該請求後,初始服務器向該Web緩存器發送具備該對象的HTTP響應。
當Web緩存器接收到該對象時,它在本地存儲空間存儲一份副本,並向客戶的瀏覽器用HTTP響應報文發送該副本(經過現有的客戶瀏覽器和Web緩存器之間的TCP鏈接)。
web緩存器能夠大大減小對客戶端請求的響應時間
時延計算:
由於客戶和緩存鏈接在一個相同的高速局域網上,這樣40%的請求將幾乎當即會由緩存器獲得響應,時延約在10ms之內。然而,剩下的60%的請求仍然要由初始服務器來知足。可是隻有60%的被請求對象經過接入鏈路,在接入鏈路上的流量強度從1.0減少到0.6。通常而言,在15Mbps鏈路上,當流量強度小於0.8時對應的時延較小,約爲幾十毫秒。這個時延與2秒因特網時延相比是微不足道的。考慮這些以後,平均時延所以爲0.4×(0.010秒)+0.6×(2.01秒) 圖2-13 爲機構網絡添加一臺緩存器這略大於1.2秒。
複製代碼
FTP相關 一些較爲常見的命令以下:
一些典型的回答連同它們可能的報文以下所示:
DNS相關
DNS層次結構
+ 各類DNS服務器交互
遞歸查詢是一種DNS 服務器的查詢模式,在該模式下DNS 服務器接收到客戶機請求,必須使用一個準確的查詢結果回覆客戶機。若是DNS 服務器本地沒有存儲查詢DNS 信息,那麼該服務器會詢問其餘服務器,並將返回的查詢結果提交給客戶機。
客戶機和服務器之間的查詢是遞歸查詢
是遞歸查詢告訴客戶機IP
DNS 服務器另一種查詢方式爲迭代查詢,DNS 服務器會向客戶機提供其餘可以解析查詢請求的DNS 服務器地址,當客戶機發送查詢請求時,DNS 服務器並不直接回複查詢結果,而是告訴客戶機另外一臺DNS 服務器地址,客戶機再向這臺DNS 服務器提交請求,依次循環直到返回查詢的結果爲止。
服務器之間的查詢是迭代查詢
DNS緩存
DNS服務器在一段時間後(一般設置爲兩天)將丟棄緩存的信息。
多路複用和分解
將運輸層報文段中的數據交付到正確的套接字的工做稱爲多路分解(demultiplexing),在源主機當中從不一樣的套接字中收集數據塊,併爲每個數據塊封裝上首部信息(用於分解)從而生成報文段,而後將此報文段傳遞到網絡層。全部的這些工做稱爲多路複用(multiplexing)。
運輸層多路複用的要求:
套接字有惟一的標識符; 每個報文段有特殊的字段來指示該報文段所要交付到的套接字。(這些特殊的字段是源端口字段和目的端口字段,端口號是一個16bit的數範圍是0-65535.其中0-1023是周知端口)。
TCP的首部開銷爲20個字節,而UDP的首部開銷爲8字節 無鏈接的多路複用與多路分解(UDP)
一個UDP套接字是由一個二元組來全面標誌的,該二元組包含一個目的IP地址和一個目的端口號,所以若是兩個UDP報文段有不一樣的源IP地址和/或源端口號,可是具備相同的目的IP地址和目的端口號,那麼這兩個報文段將經過相同的套接字被定向到相同的進程。
UDP報文格式:
長度字段:指示了在UDP報文段中的字節數(首部加數據,以字節爲單位) 檢驗和:接收方使用檢驗和來檢查在該報文段中是否出現差錯
UDP雖然實現了檢驗和,可是對恢復差錯無能爲力,要麼它丟棄受損的報文段,要麼將受損的報文段交給應用程序並給出警告。
面向鏈接的多路複用和多路分解(TCP)
TCP套接字和UDP套接字的細微的差異是,TCP套接字是由一個四元組(源IP地址,源端口號,目的IP地址,目的端口號)來標識的。這樣當一個TCP報文段從網絡到達另一臺主機時,該主機使用所有的4個值來將報文段定向(分解)到相應的套接字。特別與UDP不一樣的是,兩個具備不一樣的IP地址的或者是源端口號的到達TCP報文段將被定向到兩個不一樣的套接字,除非TCP報文段攜帶了初始建立鏈接的請求 。服務器主機能夠支持不少並行的TCP套接字,每個套接字和一個進程相聯繫,並由其四元組來標識每個套接字。當一個TCP報文段到達主機時,全部的四個字段(源IP、源端口、目的IP、目的端口)被用來將報文段定向(分解)到相應的套接字。 TCP鏈接老是點對點的,所謂的「多播」,即在一次的發送操做當中從一個發送方將數據傳輸給多個接收方,對於TCP來講是不可能的。
4位首部長度:指示TCP頭部大小(以32bit爲單位),指示何處數據開始,因爲TCP選項的緣由,TCP首部長度是可變的。(可是一般選項爲空,TCP頭部典型長度爲20字節,因此首部長度一般爲5,即1001). 16位窗口大小:用來表示想要收到的每一個TCP數據段的大小。TCP的流量控制由鏈接的每一端經過聲明窗口的大小來提供。窗口的大小爲字節數,起始於確認序號字段指明的值,這個值是接收端正指望接收到的字節。窗口的大小是一個16字節字段,於是窗口大小最大爲65535字節。 16位檢驗和:16位TCP頭部檢驗和。源主機基於數據內容計算一個數值,目的主機要和源主機計算的結果一致,從而驗證數據的有效性。檢驗和覆蓋的是整個的TCP報文段:這是一個強制性的字段,必定是由發送端計算和存儲,並由接收端進行驗證。
URG:緊急標誌,爲1時表示有效,緊急數據的最後一個字節由16bit的緊急數據指針字段指出。當緊急數據存在時,
ACK:確認標誌。代表確認編號欄有效,大多數狀況下該標識位是置位的。TCP報頭內的確認編號欄內包含的確認編號(W+1)爲下一個預期接收到的序列編號,同時提示遠端系統已經成功的接收到了全部數據。
PSH:推標誌。該標誌置位時,接收端不將該數據進行隊列處理,而是儘量快地將數據轉由應用處理(接收方當即將數據交給上層)。在處理Telnet或rlogin等交互模式的鏈接時,該標誌老是置位的。
RST:復位標誌。用於復位(重置)相應的TCP鏈接。
SYN:同步標誌。代表同步序列編號欄有效。該標誌僅在三次握手創建TCP鏈接時有效。它提示TCP鏈接的服務端檢查序列編號,該序列編號爲TCP鏈接初始端(通常是客戶端)的初始序列編號。
FIN:結束標誌。
TCP三次握手創建鏈接
三次握手(Three-Way Handshake)即創建TCP鏈接時,須要客戶端和服務端總共發送3個包確認鏈接的創建。在socket編程中,這一過程由客戶端執行connect()來觸發。流程以下:
TCP三次握手
第一次握手:Client將標誌位SYN置爲1,隨機產生一個值seq=J,並將該數據包發送給Server,Client進入SYN_SENT狀態,等待Server確認。
第二次握手:Server收到數據包後由標誌位SYN=1知道Client請求創建鏈接,Server將標誌位SYN和ACK都置爲1,ack=J+1,隨機產生一個值seq=K,並將該數據包發送給Client以確認鏈接請求,Server進入SYN_RCVD狀態。 第三次握手:Client收到確認後,檢查ack是否爲J+1,ACK是否爲1,若是正確則將標誌位ACK置爲1,ack=K+1,並將該數據包發送給Server,Server檢查ack是否爲K+1,ACK是否爲1,若是正確則鏈接創建成功,Client和Server進入ESTABLISHED狀態,完成三次握手,隨後Client與Server之間能夠開始傳輸數據了。(前兩次握手是是不承載"有效載荷"的,而第三次握手是能夠承載"有效載荷"的。)
其中有一個半鏈接狀態:服務器維護一個半鏈接隊列,該隊列爲每一個客戶端SYN包開設一個條目,標明服務器已經接到SYN包,並向客戶端發出確認,這些條目表示的鏈接處於SYN_RECV狀態,獲得客戶端的確認後進入ESTABLISHED狀態。
TCP四次揮手斷開鏈接
四次揮手(Four-Way Wavehand)是指斷開一個TCP鏈接時須要客戶端和服務器總共發送四個包以確認鏈接的斷開。在socket()編程中,這一個過程由客戶端或者服務器端的任意一方執行close來觸發。整個流程圖以下:
第一次揮手:Client發送一個FIN,用來關閉Client到Server的數據傳送,Client進入FIN_WAIT_1狀態。
第二次揮手:Server收到FIN後,發送一個ACK給Client,確認序號爲收到序號+1(與SYN相同,一個FIN佔用一個序號),Server進入CLOSE_WAIT狀態。
第三次揮手:Server發送一個FIN,用來關閉Server到Client的數據傳送,Server進入LAST_ACK狀態。
第四次揮手:Client收到FIN後,Client進入TIME_WAIT狀態,接着發送一個ACK給Server,確認序號爲收到序號+1,Server進入CLOSED狀態,完成四次揮手。
連接:http://www.jianshu.com/p/37d132327724
TCP擁塞控制機制
擁塞控制(congestion control)是TCP協議的一項重要功能,TCP的擁塞控制機制是從端到端的角度,推測網絡是否發生擁塞,若是推斷網絡發生擁塞,則當即將數據發送速率降下來,以便緩解網絡擁塞。 TCP的擁塞控制採用的是窗口機制,經過調節窗口的大小實現對數據發送速率的調整。TCP的發送端維持一個稱爲擁塞窗口cwnd的變量,單位爲字節,用於表示在未收到接收端確認的狀況下,能夠連續發送的數據字節數。cwnd的大小取決於網絡的擁塞程度,而且動態地發生變化。擁塞窗口調整的原則是:只要網絡沒有出現擁塞,就能夠增大擁塞窗口,以便將更多的數據發送出去,至關於提升發送速率;一旦網絡出現擁塞,擁塞窗口就減少一些,減小注入網絡的數據量,從而緩解網絡的擁塞。 發送端判斷網絡發生擁塞的依據是:發送端設置一個重傳計時器RTO,對於某個已發出的數據報文段,若是在RTO計時到期後,尚未收到來自接收端的確認,則認爲此時網絡發生了擁塞。 TCP的擁塞控制算法包括了慢啓動(slow start)、擁塞避免(congestion avoidance)、快速重傳(fast retransmit)和快速恢復(fast recovery)四部分。 慢啓動算法做用在TCP數據傳輸的開始階段,當主機開始發送數據時,由於不知道網絡中的負荷狀況,若是當即發送大量的數據,有可能會引發網絡的擁塞。所以,TCP採用試探的方法,逐漸增大擁塞窗口。一般在剛開始發送數據報文段時,先將擁塞窗口cwnd設置爲一個TCP最大段長度MSS的值。而在每收到一個數據報文段的確認後,cwnd就增長一個MSS的數值。這樣就能夠逐漸增大發送端的擁塞窗口,使數據注入網絡的速率比較合理。若是定義從發送端發出一個數據報文段到收到這個數據報文段的確認的時間間隔爲往返時間RTT,則在慢啓動階段,每通過一個RTT,cwnd的值就加倍。 爲了防止擁塞窗口增加過快而引發網絡擁塞,TCP還須要設置一個慢啓動閾值ssthresh,當擁塞窗口的值增長到ssthresh時,就要減緩擁塞窗口的增加速度,具體的作法是每通過一個RTT,擁塞窗口cwnd的值加1(單位爲MSS),這樣就能夠使cwnd按線性規律緩慢增加,這個過程稱之爲「加性增長」(Additive Increase)算法。一般狀況下,擁塞窗口cwnd的初值被設置爲1,慢啓動閾值ssthresh的初值被設置爲16。當擁塞避免算法執行到某個時刻,發送端在規定時間內沒有收到接收端的確認,即發生了網絡超時,則意味着網絡發生了擁塞。此時,發送端首先將ssthresh的值變爲發生超時時cwnd值的一半,同時將cwnd的值置爲1,從新執行慢啓動算法。這樣作的好處是,當網絡頻繁出現擁塞時,ssthresh降低得很快,能夠大大減小注入網絡中的數據報文段。一般稱這個過程爲「乘性減少」(MultiplicativeDecrease)算法。TCP中的「加性增長」和「乘性減少」算法合起來稱爲AIMD算法。 慢啓動和擁塞避免是1988年提出的擁塞控制算法,1990年在此基礎上又增長了快速重傳和快速恢復兩個算法。 快速重傳算法的基本思想是:接收端每收到一個失序的數據報文段後就當即發出重複確認,以便更早地通知發送端有丟包的狀況發生。假設在某個TCP數據傳輸過程當中,接收端依次收到發送端發出的1號和2號數據報文段,並對這兩個數據報文段發送確認後,沒有按次序收到3號數據報文段,而是收到了4號,這時就須要當即向發送端發送一個2號數據報文段的確認,稱爲重複確認。同理,若是繼續收到5號、6號數據報文段,接收端仍然要向發送端發出2號數據報文段的重複確認。此時,發送端會收到多個2號數據報文段的重複確認,則認爲3號數據報文段發生了丟包,須要當即向接收端重傳3號數據報文段,而不須要等待重傳計時器到期再重傳。快速重傳算法中規定若是收到某數據報文段的三個重複確認,則當即重傳下一個數據報文段。 快速恢復是配合快速重傳使用的算法,其基本思想是:當發送端連續收到三個重複確認時,就將慢啓動閾值ssthresh減半,以預防網絡擁塞的發生,而且將擁塞窗口cwnd的值置爲減半後的ssthresh,而後開始執行擁塞避免算法,使得cwnd緩慢地加性增大。
TCP擁塞控制算法描述以下:
SlowStartPhase( ) //慢啓動算法
{
CongWin=1; //擁塞窗口cwnd的初值爲1個MSS
while (CongWin<Threshold&& 無數據丟失)
//當擁塞窗口小於慢啓動閾值且沒有發生丟包時
{
for each ACK
CongWin++; //每收到一個確認數據報,擁塞窗口加1
}
if (CongWin>=Threshold) then
CongestionAvoidancePhase( );
//當擁塞窗口大於等於慢啓動閾值時,啓動擁塞避免算法;
if (數據丟失) then
DataLoss( ); // 丟包後的處理方法
}
CongestionAvoidancePhase( ) // 擁塞避免算法
{
while (無數據丟失)
{
for each RTT
CongWin=CongWin+1; //每通過一個RTT,擁塞窗口加1
}
DataLoss( );
}
DataLoss( ) //丟包後的處理方法
{
if (超時) then
{
Threshold=CongWin/2;
CongWin=1;
SlowStartPhase( );
//若是發生超時,慢啓動閾值置爲當前擁塞窗口的一半,而後將擁塞窗口置1,開始執行擁塞避免算法。
}
if (3次重複確認) then
{
Threshold=CongWin/2;
CongWin=CongWin/2;
CongestionAvoidancePhase();
//若是收到3個重複的確認,則執行快速重傳和快速恢復算法,慢啓動閾值減少爲擁塞窗口的一半,同時將擁塞窗口減半,開始擁塞避免算法
複製代碼
連接:http://www.jianshu.com/p/7d59f9292b03
示例:
sql_login = "SELECT COUNT(*) FROM login WHERE userName= %s AND password=%s" %(userName, password)
此時若 username = 'admin--' 則不論password爲何都能登陸
解決方案:sql_login = "SELECT COUNT(*) FROM login WHERE userName= %s AND password=%s"
values = (username, password)
cur.execute(sql_login, values)
學習簡單SQL注入:
AND
and 1=1 and 1=2
猜表
and 0 < (select count(*) from admin) ---判斷是否存在admin這張表
此類相似,經過使用 and來達到本身的查詢目的
; ;結束以前的SQL語句
-- 忽略後面的語句
OR 使前面的判斷失效 解決方案: 綁定變量使用預編譯語句是預防SQL注入的最佳方式,使用預編譯的SQL語句語義不會發生改變,在SQL語句中,變量用問號?表示,黑客即便本事再大,也沒法改變SQL語句的結構,像上面例子中,username變量傳遞的plhwin' AND 1=1-- hack參數,也只會看成username字符串來解釋查詢,從根本上杜絕了SQL注入攻擊的發生。
DDOS的表現形式主要有兩種,一種爲流量攻擊,主要是針對網絡帶寬的攻擊,即大量攻擊包致使網絡帶寬被阻塞,合法網絡包被虛假的攻擊包淹沒而沒法到達主機;另外一種爲資源耗盡攻擊,主要是針對服務器主機的攻擊,即經過大量攻擊包致使主機的內存被耗盡或CPU被內核及應用程序佔完而形成沒法提供網絡服務。
@main_view.route('/test')
def test():
test_data = "zhaonan=guest<script>alert('attacked')</script>"
return ''' <html> <head> <title>Home Page</title> </head> <body> <h1>Hello, ''' + test_data + '''</h1> </body> </html> '''
複製代碼
在網站上輸入 localhost/test以後會顯示一個attacked的彈窗
參考網站:http://www.cnblogs.com/hyddd/archive/2009/04/09/1432744.html
防護:服務端的CSRF方式方法不少樣,但總的思想都是一致的,就是在客戶端頁面增長僞隨機數。
這多是最簡單的解決方案了,由於攻擊者不能得到第三方的Cookie(理論上),因此表單中的數據也就構造失敗了:>
這個方法我的以爲已經能夠杜絕99%的CSRF攻擊了,那還有1%呢....因爲用戶的Cookie很容易因爲網站的XSS漏洞而被盜取,這就另外的1%。通常的攻擊者看到有須要算Hash值,基本都會放棄了,某些除外,因此若是須要100%的杜絕,這個不是最好的方法。
這個方案的思路是:每次的用戶提交都須要用戶在表單中填寫一個圖片上的隨機字符串,厄....這個方案能夠徹底解決CSRF,但我的以爲在易用性方面彷佛不是太好,還有聽聞是驗證碼圖片的使用涉及了一個被稱爲MHTML的Bug,可能在某些版本的微軟IE中受影響。
+ One-Time Tokens(不一樣的表單包含一個不一樣的僞隨機值)
在實現One-Time Tokens時,須要注意一點:就是「並行會話的兼容」。若是用戶在一個站點上同時打開了兩個不一樣的表單,CSRF保護措施不該該影響到他對任何表單的提交。考慮一下若是每次表單被裝入時站點生成一個僞隨機值來覆蓋之前的僞隨機值將會發生什麼狀況:用戶只能成功地提交他最後打開的表單,由於全部其餘的表單都含有非法的僞隨機值。必須當心操做以確保CSRF保護措施不會影響選項卡式的瀏覽或者利用多個瀏覽器窗口瀏覽一個站點。
亂穿馬路
目標:存儲多值屬性
反模式:格式化的逗號分隔列表
壞處:
解決方案:建立一張交叉表
示例:
一個產品可能有多個聯繫人。 能夠這樣設計:
CREATE TABLE Contacts (
product_id BIGINT UNSIGNED NOT NULL,
account_id BIGINT UNSIGNED NOT NULL ,
PRIMARY KEY (product_id,account_id),
FOREIGN KEY (product_id) REFERENCES Products(product_id),
FOREIGN KEY (account_id) REFERENCES Accounts(account_id)
);
複製代碼
好處:
單純的樹
在樹形結構中,實例被稱爲節點。每一個節點都有多個子節點與一個父節點。
最上層的節點叫作**根(root)節點,**它沒有父節點。
最底層的沒有子節點的節點叫作葉(leaf)。
中間的節點簡單地稱爲非葉節點(nonleaf)。
**目標:**分層存儲與查詢,好比:系統字典、組織機構、省份區域等樹形結構數據或者以層級方式組織的數據。
**反模式:**老是依賴父節點(使用鄰接表)。
最簡單的實現方式是添加ParentId字段,引用同一張表的主鍵ID。
鄰接表維護樹比較方便,可是查詢很笨拙,若是要找一個節點下的全部子節點,要關聯不少次,這個關聯次數取決於樹的深度,
因此,鄰接表不能用於存儲比較深的樹。
**如何識別反模式:**當出現如下狀況時,多是反模式
(1)咱們的數結構要支持多少層
(2)咱們老是很懼怕接觸那些管理樹結構的代碼
(3)我須要一個腳原本按期的清理樹中的孤立節點數據
**解決方案:**使用其餘樹模型
用一個path字段保存當前節點的最頂層的祖先到本身的序列(路徑)
優勢:查詢方便;
缺點:一、不能保證存儲的值的有效性。
二、增、刪時,要考慮對原位置下的子節點如何處理,比較麻煩。
三、若是還要維護一個排序path,那就更麻煩了。
存儲子孫節點的相關信息,而不是節點的直接祖先。用nsleft存儲全部後臺的nsleft中最小的數-1,
用nsright存儲全部後臺的nsright中最大的數+1。
CREATE TABLE Comments (
comment_id
SERIAL PRIMARY KEY,
nsleft
INTEGER NOT NULL,
nsright
INTEGER NOT NULL,
bug_id
BIGINT UNSIGNED NOT NULL,
author
BIGINT UNSIGNED NOT NULL,
comment_date DATETIME NOT NULL,
comment
TEXT NOT NULL,
FOREIGN KEY (bug_id) REFERENCES Bugs (bug_id),
FOREIGN KEY (author) REFERENCES Accounts(account_id)
);
複製代碼
優勢:刪除時,原來子節點的關係自動上移。
缺點:一、查詢一個節點的直接上級或下級,很困難。
二、增、刪,困難。
將樹中任何具備**「祖先-後代」關係的節點對**都存儲在TreePath表中的一行,同時增長一行指向節點本身。
CREATE TABLE Comments (
comment_id
SERIAL PRIMARY KEY,
bug_id
BIGINT UNSIGNED NOT NULL,
author
BIGINT UNSIGNED NOT NULL,
comment_date DATETIME NOT NULL,
comment
TEXT NOT NULL,
FOREIGN KEY (bug_id) REFERENCES Bugs(bug_id),
FOREIGN KEY (author) REFERENCES Accounts(account_id)
);
CREATE TABLE TreePaths (
ancestor
BIGINT UNSIGNED NOT NULL,
descendant BIGINT UNSIGNED NOT NULL,
PRIMARY KEY(ancestor, descendant),
FOREIGN KEY (ancestor) REFERENCES Comments(comment_id),
FOREIGN KEY (descendant) REFERENCES Comments(comment_id)
);
複製代碼
優勢:一、能快速的查詢給定節點的祖先與後代;
二、能更加簡單的維護分層信息;
三、若是刪除了TreePath表中的一條記錄,那麼並非真正的刪除具體信息表中的記錄。這樣設計有時候頗有用:
好比在產品目錄的分類或者員工組織架構的圖標中,當你改變了節點關係的時候,並非真的想要刪除一個節點。
咱們把關係路徑存儲在一個分開獨立的表中,使得設計更加靈活。
缺點:查詢直接父節點或子節點,須要在表中增長Path_Length字段來維護。
結論:
每種設計各有優劣,如何選擇設計依賴於應用程序中的哪一種操做最須要性能上的優化。
鄰接表:簡單,但不適用於很深的表;
枚舉路徑:沒法保證引用完整性;
嵌套集:沒法保證引用完整性,太複雜;
閉包:須要一個額外的表存儲關係;
須要ID
目標:創建主鍵規範
反模式:以不變應萬變,即每一個數據庫中的表都須要一個僞主鍵Id
在表中,須要引入一個對於表的域模型無心義的新列來存儲一個僞值,這一列被用做這張表的主鍵,從而經過它來肯定表中的一條記錄,即使其餘的列容許出現適當的重複項。這種類型的主鍵列咱們一般稱其爲「僞主鍵」或者「代理鍵」。
**如何識別反模式:**當出現如下狀況時,多是反模式
解決方案:
直接了當的描述設計,主鍵名應該便於理解,因此建議用XxxId,而不都是用Id;
擁抱天然鍵和組合鍵。
不用鑰匙的入口(外鍵約束)
**目標:**簡化數據庫架構
**反模式:**無視約束,即不使用約束
**如何識別反模式:**當出現如下狀況時,多是反模式
一、我要怎麼寫這個查詢來檢查一個值是否沒有被同時存在2張表中?
(一般這樣的需求是爲了查找那些孤立的行數據)
二、有沒有一種簡單的方法來判斷在一張表中的數據是否也在第二張表中存在?
(這麼作是用來確認父記錄切實存在。外鍵會自動完成這些,而且外鍵會使用這父表的索引儘量的高效完成)
三、有人說不要用外鍵,外鍵影響數據庫效率。
**解決方案:**聲明約束
一、經過使用外鍵來確保應用完整性;
使用約束時:
(1)數據庫自己會拒絕全部不合理的改變,不管這個改變是經過什麼方式形成的。
(2)可以避免編寫沒必要要的代碼,同時還能確保一旦修改了數據庫中的內容,全部的代碼依舊可以用一樣的方式執行。
(3)外鍵的特性:級聯更新,好比:On Update Cascade、On Delete Restrict等。 在執行更新和刪除2個操做中的任意1個是,數據庫都會自動修改多張表中的數據, 外鍵的引用狀態在操做以前和以後都保持無缺。
二、外鍵約束的確須要多那麼一點額外的系統開銷,但相比於其餘的一些選擇,外鍵確實更高效一點:
(1)不須要在更新或刪除記錄前執行Select檢查;
(2)在同步修改時不須要再鎖住整張表;
(3)再也不須要執行按期監控腳原本修正不可避免的孤立數據。
實體-屬性-值
**目標:**支持可變屬性
**反模式:**使用泛型屬性表。這種設計成爲實體-屬性-值(EAV),也可叫作開放架構、名-值對。
優勢:經過增長一張額外的表,能夠有如下好處
(1)表中的列不多;
(2)新增屬性時,不須要新增列。不會影響現有表的結構;
(3)存儲的字段內容不會爲空值。
缺點:(1)查詢語句變得更加複雜;
(2)使用EAV設計後,須要放棄傳統的數據庫設計所帶來的方便之處,好比:沒法保障數據完整性;
(3)沒法使用SQL的數據類型,好比對日期、金錢等格式內容都只能保持爲字符串類型;
(4)沒法確保引用完整性;
(5)沒法配置屬性名。好比,有可能表中存在兩條記錄,
一條的attr_name是sex,一條attr_name是gender,都是表示性別;
(6)查詢結果中有多個屬性時,查詢很是困難,且查詢性能沒法控制。
**如何識別反模式:**當出現如下狀況時,多是反模式
(1)數據庫不須要修改元數據庫(表中的列屬性)就能夠擴展。還能夠在運行時定義新的屬性。
(2)查詢是鏈接數量很是多,且鏈接的數量可能會達到數據庫的限制時,你的數據庫的設計多是有問題的。
(3)普通的報表查詢變的及其複雜甚至不且實際。
**解決方案:**模型化子類型
一、單表繼承:全部屬性都在一個單表上保存,增長屬性時就擴充這個表。
缺點:
(1)當程序須要加入新對象時,必須修改數據庫來適應這些新對象。又因爲這些新對象具備一些和老對象 不用的屬性, 於是必須在原有表裏增長新的屬性列,可能會遇到一個實際的問題,就是每張表的列的數量是有限制的。
(2)沒有任何的元信息來記錄哪一個屬性屬於哪一個子類型。
當數據的子類型不多,以及子類型特殊屬性不多,就能夠使用單表繼承。
二、實體表繼承:爲每一個子類型建立一張獨立的表,每一個表包含哪些屬於基類的共有屬性,同時也包含了子類型特殊化的屬性。
優勢:
(1)實體繼承類設計相比於但表繼承設計的優點在於提供了一種方法, 讓你能組織在一行內存儲一些和當前子類型無關的屬性。若是你引用一個並不存在於這張表中的屬性列,數據庫會自動提示你錯誤。
(2)不用像在單表繼承設計裏那樣使用額外的屬性來標記子類型。
缺點:很難將通用屬性和子類特有屬性區分開來。所以,若是將一個新的屬性增長到通用屬性中,必須爲每一個子類表都添加一遍。
當你不多須要一次性查詢多有子類型時,實體繼承表設計是最好的選擇。
三、類表繼承:把表當成面向對象裏的類。
建立一張基類表,包含全部子類型的公共屬性。對於每一個子類型,建立一個獨立的表,經過外鍵和基類表相連。
四、半結構化數據模型:若是有不少子類型或者必須常常增長新的屬性支持,那麼能夠用一個BLOB列來存儲數據,用XML或者JSON格式——同事包含了屬性的名字和值。這叫作序列化大對象塊。
這個設計的優點是擴展性,缺點是,這樣的結構中sql沒法獲取某個指定的屬性。你必須或者整個blob字段並經過程序去解釋這些屬性。
當你須要絕對的靈活性時,能夠使用這個方案。
若是使用了EAV,那麼能夠先將所有屬性取出,而後再作其餘處理。
多態關聯
多列屬性
元數據分裂
商品表 goods:
id | price | color |
---|---|---|
1 | 10 | red |
2 | 20 | blue |
3 | 30 | red, blue |
能夠拆分爲兩個表,價格表和顏色表: 價格表 goods_price:
id | price |
---|---|
1 | 10 |
2 | 20 |
3 | 30 |
顏色表 goods_color
id | color |
---|---|
1 | red |
2 | blue |
3 | red |
3 | blue |
2NF:屬性徹底依賴於主鍵 以下表所示:學分依賴於課程,不依賴於主鍵學號 考試成績表 exam:
學號 | 姓名 | 課程 | 學分 | 成績 |
---|---|---|---|---|
1 | Tom | 數學 | 4 | 80 |
2 | Kate | 數學 | 4 | 90 |
能夠拆分爲三個表,學生信息表,課程表和考試成績表:
學生信息表 student:
學號 | 姓名 |
---|---|
1 | Tom |
2 | Kate |
課程表 course:
課程編號 | 課程名 | 學分 |
---|---|---|
101 | 數學 | 4 |
102 | 語文 | 2 |
考試成績表 exam:
學號 | 課程編號 | 成績 |
---|---|---|
1 | 101 | 80 |
2 | 101 | 90 |
3NF:屬性不依賴於其餘非主屬性,即不能有冗餘
以下表所示:班主任手機依賴於班主任姓名 這個非主屬性
學生信息表 student:
學號 | 姓名 | 班主任姓名 | 班主任手機 |
---|---|---|---|
1 | Tom | Lily | 138 |
2 | Kate | Lily | 138 |
能夠拆分爲兩個表,學生信息表,班主任信息表:
學生信息表 student:
學號 | 姓名 | 班主任姓名 |
---|---|---|
1 | Tom | Lily |
2 | Kate | Lily |
班主任信息表 teacher:
班主任姓名 | 班主任手機 |
---|---|
Lily | 138 |
Cat | 139 |
連接:http://www.jianshu.com/p/841573f02f8e