ArrayList,LinkedList,Vector都屬於List List:元素是有順序的,元素能夠重複由於每一個元素有本身的角標(索引) |-- ArrayList:底層的數據結構是數組結構,特色是:查詢很快,增 刪 稍微慢點,線程不一樣步 |-- LinkedList:底層使用的是鏈表數據結構,特色是:增 刪很快,查詢慢。 |--Vector:底層是數組數據結構,線程同步,被ArrayList代替了,如今用的只有他的枚舉。 Set:元素是無序的,且不能夠重複(存入和取出的順序不必定一致),線程不一樣步。 |--HashSet:底層是哈希表數據結構。根據hashCode和equals方法來肯定元素的惟一性 |--TreeSet:能夠對Set集合中的元素進行排序(天然循序),底層的數據結構是二叉樹, 也能夠本身寫個類實現Comparable 或者 Comparator 接口,定義本身的比較器,將其做爲參數傳遞給TreeSet的構造函數。 Map:這個集合是存儲鍵值對的,一對一對往裏存,並且要確保鍵的惟一性(01,張三)這樣的形式打印出來就是 01=張三 |--HashTable:底層是哈希表數據結構,不能夠存入null鍵和null值,該集合線程是同步的,效率比較低。出現於JDK1.0 |--HashMap:底層是哈希表數據結構,能夠存入null鍵和null值,線程不一樣步,效率較高,代替了HashTable,出現於JDK 1.2 |--TreeMap:底層是二叉樹數據結構,線程不一樣步,能夠用於個map集合中的鍵進行排序
在Java編程語言中,最基本的結構就是兩種,一個是數組,另一個是模擬指針(引用),全部的數據結構均可以用這兩個基本結構來構造的,HashMap也不例外。 HashMap其實是一個「鏈表的數組」的數據結構,每一個元素存放鏈表頭結點的數組,即數組和鏈表的結合體。HashMap底層就是一個數組,數組中的每一項又是一個鏈表。當新建一個HashMap的時候,就會初始化一個數組 ConcurrentHashMap是由Segment數組結構和HashEntry數組結構組成。Segment是一種可重入鎖ReentrantLock,在ConcurrentHashMap裏扮演鎖的角色,HashEntry則用於存儲鍵值對數據。 一個ConcurrentHashMap裏包含一個Segment數組,Segment的結構和HashMap相似,是一種數組和鏈表結構, 一個Segment裏包含一個HashEntry數組, 每一個HashEntry是一個鏈表結構的元素, 每一個Segment守護者一個HashEntry數組裏的元素,當對HashEntry數組的數據進行修改時,必須首先得到它對應的Segment鎖
方法一: 維護一張表,存儲數據插入的順序,能夠使用vector。可是若是刪除數據呢,首先得在vector裏面找到那個數據,再刪除,而刪除又要移動大量數據。性能效率很低。 使用list,移動問題能夠解決,可是查找數據的O(n)時間消耗,若是刪除m次,那查找數據的性能就是0(n*m),那整體性能也是 O(n2)。性能仍是無法接受。 方法二: 能夠在hashmap裏面維護插入順序的id, 在value建一個字段存儲id值,再維護一張表vector,而且id對應vector裏面的值。 插入的時候,id+=1, hashmap.insert,vector.push_back. 刪除的時候,先hashmap.find(key), 獲得value, 並從value中獲得id, 經過id把對應vector值置爲無效。 更新:刪除+插入。 維護工做OK了,輸出的時候直接輸出vector裏面的值就能夠了, 無效的就continue。 算法複雜度爲O(n) 方法三: Java裏面有個容器LinkedHashMap, 它能實現按照插入的順序輸出結果。 它的原理也是維護一張表,但它是鏈表,而且hashmap中維護指向鏈表的指針,這樣能夠快速定位鏈表中的元素進行刪除。 它的時間複雜度也是O(n), 空間上要比上面少些
HashTable容器使用synchronized來保證線程安全,但在線程競爭激烈的狀況下HashTable的效率很是低下。 由於當一個線程訪問HashTable的同步方法時,其餘線程訪問HashTable的同步方法時,可能會進入阻塞或輪詢狀態。 如線程1使用put進行添加元素,線程2不但不能使用put方法添加元素,而且也不能使用get方法來獲取元素,因此競爭越激烈效率越低 ConcurrentHashMap使用的鎖分段技術,首先將數據分紅一段一段的存儲,而後給每一段數據配一把鎖,當一個線程佔用鎖訪問其中一個段數據的時候,其餘段的數據也能被其餘線程訪問
1 .三者在執行速度方面的比較:StringBuilder > StringBuffer > String 2 .String <(StringBuffer,StringBuilder)的緣由 String:字符串常量 StringBuffer:字符串常量 StringBuilder:字符串常量 StringBuilder:線程非安全的 StringBuffer:線程安全的 當咱們在字符串緩衝去被多個線程使用是,JVM不能保證StringBuilder的操做是安全的,雖然他的速度最快,可是能夠保證StringBuffer是能夠正確操做的。 固然大多數狀況下就是咱們是在單線程下進行的操做,因此大多數狀況下是建議用StringBuilder而不用StringBuffer的,就是速度的緣由。 對於三者使用的總結: 1.若是要操做少許的數據用 = String 2.單線程操做字符串緩衝區 下操做大量數據 = StringBuilder 3.多線程操做字符串緩衝區 下操做大量數據 = StringBuffer
wait和notify的本質是基於條件對象的,並且只能由已經得到鎖的線程調用。java的每一個Object都有一個隱式鎖,這個隱式鎖關聯一個Condition條件對象,線程拿到這個隱式鎖(好比進入synchronized代碼區域),就能夠調用wait, 語義是在Condition條件對象上等待,其餘的線程能夠在這個Condition條件對象上等待,等知足條件以後,就能夠調用notify或者notifyAll來喚醒全部在此條件對象上等待的線程
sleep()是Thread類中的方法,而wait()則是Object類中的方法。 sleep()方法致使了程序暫停,可是他的監控狀態依然保持着,當指定的時間到了又會自動恢復運行狀態。在調用sleep()方法的過程當中,線程不會釋放對象鎖。 wait()方法會致使線程放棄對象鎖,進入等待此對象的等待鎖定池,只有針對此對象調用notify()方法後本線程才進入對象鎖定池準備獲取對象鎖進入運行狀態。
JVM在運行時將數據劃分爲了6個區域來存儲,而不只僅是你們熟知的Heap區域 一 PC Register(PC寄存器) PC寄存器是一塊很小的內存區域,主要做用是記錄當前線程所執行的字節碼的行號。字節碼解釋器工做時就是經過改變當前線程的程序計數器選取下一條字節碼指令來工做的。任何分支,循環,方法調用,判斷,異常處理,線程等待以及恢復線程,遞歸等等都是經過這個計數器來完成的。 因爲Java多線程是經過交替線程輪流切換並分配處理器時間的方式來實現的,在任何一個肯定的時間裏,在處理器的一個內核只會執行一條線程中的指令。 所以爲了線程等待結束須要恢復到正確的位置執行,每條線程都會有一個獨立的程序計數器來記錄當前指令的行號。計數器之間相互獨立互不影響,咱們稱這塊內存爲「線程私有」的內存。 若是所調用的方法爲native的,則PC寄存器中不存儲任何信息。 二 JVM棧 JVM棧是線程私有的,每一個線程建立的同時都會建立JVM棧,JVM棧中存放的爲當前線程中局部基本類型的變量(java中定義的八種基本類型:boolean、char、byte、short、int、long、float、double)、部分的返回結果以及Stack Frame, 非基本類型的對象在JVM棧上僅存放一個指向堆上的地址,所以Java中基本類型的變量是值傳遞,而非基本類型的變量是引用傳遞,Sun JDK的實現中JVM棧的空間是在物理內存上分配的,而不是從堆上分配。 因爲JVM棧是線程私有的,所以其在內存分配上很是高效,而且當線程運行完畢後,這些內存也就被自動回收。 當JVM棧的空間不足時,會拋出StackOverflowError的錯誤,在Sun JDK中能夠經過-Xss來指定棧的大小 三 堆(Heap) Heap是你們最爲熟悉的區域,它是JVM用來存儲對象實例以及數組值的區域,能夠認爲Java中全部經過new建立的對象的內存都在此分配, Heap中的對象的內存須要等待GC進行回收,Heap在32位的操做系統上最大爲2G,在64位的操做系統上則沒有限制, 其大小經過-Xms和-Xmx來控制,-Xms爲JVM啓動時申請的最小Heap內存,默認爲物理內存的1/64但小於1G,-Xmx爲JVM可申請的最大Heap內存,默認爲物理內存的1/4,默認當空餘堆內存小於40%時,JVM會增大Heap的大小到-Xmx指定的大小 ,可經過-XX:MinHeapFreeRatio=來指定這個比例,當空餘堆內存大於70%時,JVM會將Heap的大小往-Xms指定的大小調整,可經過-XX:MaxHeapFreeRatio=來指定這個比例, 但對於運行系統而言,爲了不頻繁的Heap Size的大小,一般都會將-Xms和-Xmx的值設成同樣,所以這兩個用於調整比例的參數一般是沒用的。其實jvm中對於堆內存的分配、使用、管理、收集等有更爲精巧的設計,具體能夠在JVM堆內存分析中進行詳細介紹。 當堆中須要使用的內存超過其容許的大小時,會拋出OutOfMemory的錯誤信息。 四 方法區域(MethodArea) 方法區域存放了所加載的類的信息(名稱、修飾符等)、類中的靜態變量、類中定義爲final類型的常量、類中的Field信息、類中的方法信息, 當開發人員在程序中經過Class對象中的getName、isInterface等方法來獲取信息時,這些數據都來源於方法區域,可見方法區域的重要性 。一樣,方法區域也是全局共享的,它在虛擬機啓動時在必定的條件下它也會被GC,當方法區域須要使用的內存超過其容許的大小時,會拋出OutOfMemory的錯誤信息。 在Sun JDK中這塊區域對應的爲PermanetGeneration,又稱爲持久代,默認爲64M,可經過-XX:PermSize以及-XX:MaxPermSize來指定其大小。 五 運行時常量池(RuntimeConstant Pool) 相似C中的符號表,存放的爲類中的固定的常量信息、方法和Field的引用信息等,其空間從方法區域中分配。類或接口的常量池在該類的class文件被java虛擬機成功裝載時分配。 六 本地方法堆棧(NativeMethod Stacks) JVM採用本地方法堆棧來支持native方法的執行,此區域用於存儲每一個native方法調用的狀態
在JDK 1.2以後,Java對引用的概念進行了擴充,將引用分爲強引用(Strong Reference)、軟引用(Soft Reference)、弱引用(Weak Reference)、虛引用(Phantom Reference)四種,這四種引用強度依次逐漸減弱。 強引用就是指在程序代碼之中廣泛存在的,相似「Object obj = new Object()」這類的引用,只要強引用還存在,垃圾收集器永遠不會回收掉被引用的對象。 軟引用用來描述一些還有用,但並不是必需的對象。對於軟引用關聯着的對象,在系統將要發生內存溢出異常以前,將會把這些對象列進回收範圍之中並進行第二次回收。若是此次回收仍是沒有足夠的內存,纔會拋出內存溢出異常。 在JDK 1.2以後,提供了SoftReference類來實現軟引用。 弱引用也是用來描述非必需對象的,可是它的強度比軟引用更弱一些,被弱引用關聯的對象只能生存到下一次垃圾收集發生以前。當垃圾收集器工做時,不管當前內存是否足夠,都會回收掉只被弱引用關聯的對象。 在JDK 1.2以後,提供了WeakReference類來實現弱引用。 虛引用也稱爲幽靈引用或者幻影引用,它是最弱的一種引用關係。一個對象是否有虛引用的存在,徹底不會對其生存時間構成影響,也沒法經過虛引用來取得一個對象實例。 爲一個對象設置虛引用關聯的惟一目的就是但願能在這個對象被收集器回收時收到一個系統通知。在JDK 1.2以後,提供了PhantomReference類來實現虛引用
在Java中,數組變量是引用類型的變量,同時由於Java是典型的靜態語言,所以它的數組也是靜態的,因此想要使用就必須先初始化(爲數組對象的元素分配空間)。 對於Java數組的初始化,有如下兩種方式: 靜態初始化:初始化時由程序員顯式指定每一個數組元素的初始值,由系統決定數組長度 動態初始化:初始化時由程序員顯示的指定數組的長度,由系統爲數據每一個元素分配初始值 靜態初始化方式,程序員雖然沒有指定數組長度,可是系統已經自動幫咱們給分配了,而動態初始化方式,程序員雖然沒有顯示的指定初始化值,可是由於Java數組是引用類型的變量,因此係統也爲每一個元素分配了初始化值null, 固然不一樣類型的初始化值也是不同的,假設是基本類型int類型,那麼爲系統分配的初始化值也是對應的默認值0
設計模式的分類 整體來講設計模式分爲三大類: 建立型模式,共五種:工廠方法模式、抽象工廠模式、單例模式、建造者模式、原型模式。 結構型模式,共七種:適配器模式、裝飾器模式、代理模式、外觀模式、橋接模式、組合模式、享元模式。 行爲型模式,共十一種:策略模式、模板方法模式、觀察者模式、迭代子模式、責任鏈模式、命令模式、備忘錄模式、狀態模式、訪問者模式、中介者模式、解釋器模式。 其實還有兩類:併發型模式和線程池模式
springmvc是基於servlet的前端控制框架,核心是ioc和aop(基於spring實現) 核心架構的具體流程步驟以下: 一、首先用戶發送請求——>DispatcherServlet,前端控制器收到請求後本身不進行處理,而是委託給其餘的解析器進行 處理,做爲統一訪問點,進行全局的流程控制; 二、DispatcherServlet——>HandlerMapping, HandlerMapping 將會把請求映射爲HandlerExecutionChain 對象(包含一個Handler 處理器(頁面控制器)對象、多個HandlerInterceptor 攔截器)對象,經過這種策略模式,很容易添加新的映射策略; 三、DispatcherServlet——>HandlerAdapter,HandlerAdapter 將會把處理器包裝爲適配器,從而支持多種類型的處理器,即適配器設計模式的應用,從而很容易支持不少類型的處理器; 四、HandlerAdapter——>處理器功能處理方法的調用,HandlerAdapter 將會根據適配的結果調用真正的處理器的功能處 理方法,完成功能處理;並返回一個ModelAndView 對象(包含模型數據、邏輯視圖名); 五、ModelAndView的邏輯視圖名——> ViewResolver, ViewResolver 將把邏輯視圖名解析爲具體的View,經過這種策 略模式,很容易更換其餘視圖技術; 六、View——>渲染,View會根據傳進來的Model模型數據進行渲染,此處的Model實際是一個Map數據結構,所以 很容易支持其餘視圖技術; 七、返回控制權給DispatcherServlet,由DispatcherServlet返回響應給用戶,到此一個流程結束 IOC控制反轉的實現是基於spring的bean工廠,經過獲取要建立的類的class全限定名稱,反射建立對象
實現AOP的技術,主要分爲兩大類:一是採用動態代理技術,利用截取消息的方式,對該消息進行裝飾,以取代原有對象行爲的執行;二是採用靜態織入的方式,引入特定的語法建立「方面」,從而使得編譯器能夠在編譯期間織入有關「方面」的代碼。 經過反射建立動態代理對象,攔截方法執行,在將本身須要額外執行的代碼加塞進來執行
經過獲取到全部的返回的列名字,反射獲取目標對象中這個名字對應的屬性,調用set方法,進行賦值
,多態要有動態綁定,不然就不是多態,方法重載也不是多態(由於方法重載是編譯期決定好的,沒有後期也就是運行期的動態綁定) 當知足這三個條件 1.有繼承 2. 有重寫 3. 要有父類引用指向子類對象
一、重要性:在Java語言中, abstract class 和interface 是支持抽象類定義的兩種機制。正是因爲這兩種機制的存在,才賦予了Java強大的 面向對象能力。 二、簡單、規範性:若是一個項目比較龐大,那麼就須要一個能理清全部業務的架構師來定義一些主要的接口,這些接口不只告訴開發人員你須要實現那些業務,並且也將命名規範限制住了(防止一些開發人員隨便命名致使別的程序員沒法看明白)。 三、維護、拓展性:好比你要作一個畫板程序,其中裏面有一個面板類,主要負責繪畫功能,而後你就這樣定義了這個類。 但是在不久未來,你忽然發現這個類知足不了你了,而後你又要從新設計這個類,更糟糕是你可能要放棄這個類,那麼其餘地方可能有引用他,這樣修改起來很麻煩。 若是你一開始定義一個接口,把繪製功能放在接口裏,而後定義類時實現這個接口,而後你只要用這個接口去引用實現它的類就好了,之後要換的話只不過是引用另外一個類而已,這樣就達到維護、拓展的方便性。 四、安全、嚴密性:接口是實現軟件鬆耦合的重要手段,它描敘了系統對外的全部服務,而不涉及任何具體的實現細節。這樣就比較安全、嚴密一些(通常軟件服務商考慮的比較多)。
HTTPS(Secure Hypertext Transfer Protocol)安全超文本傳輸協議: 它是一個安全通訊通道,它基於HTTP開發,用於在客戶計算機和服務器之間交換信息,它使用安全套接字層(SSL)進行信息交換,簡單來講它是HTTP的安全版。 它是由Netscape開發並內置於其瀏覽器中,用於對數據進行壓縮和解壓操做,並返回網絡上傳送回的結果。HTTPS實際上應用了Netscape的安全全套接字層(SSL)做爲HTTP應用層的子層。 (HTTPS使用端口443,而不是象HTTP那樣使用端口80來和TCP/IP進行通訊。)SSL使用40 位關鍵字做爲RC4流加密算法,這對於商業信息的加密是合適的。 HTTPS和SSL支持使用X.509數字認證,若是須要的話用戶能夠確認發送者是誰。總的來講,HTTPS協議是由SSL+HTTP協議構建的可進行加密傳輸、身份認證的網絡協議要比http協議安全。 在URL前加https://前綴代表是用SSL加密的,你的電腦與服務器之間收發的信息傳輸將更加安全。 Web服務器啓用SSL須要得到一個服務器證書並將該證書與要使用SSL的服務器綁定。 HTTPS和HTTP的區別: https協議須要到ca申請證書,通常免費證書不多,須要交費。 http是超文本傳輸協議,信息是明文傳輸,https 則是具備安全性的ssl加密傳輸協議。 http和https使用的是徹底不一樣的鏈接方式用的端口也不同,前者是80,後者是443。 http的鏈接很簡單,是無狀態的。 HTTPS協議是由SSL+HTTP協議構建的可進行加密傳輸、身份認證的網絡協議 要比http協議安全。 HTTPS解決的問題: 1 . 信任主機的問題. 採用https 的server 必須從CA 申請一個用於證實服務器用途類型的證書. 改證書只有用於對應的server 的時候,客戶度纔信任此主機. 因此目前全部的銀行系統網站,關鍵部分應用都是https 的. 客戶經過信任該證書,從而信任了該主機. 其實這樣作效率很低,可是銀行更側重安全. 這一點對咱們沒有任何意義,咱們的server ,採用的證書無論本身issue 仍是從公衆的地方issue, 客戶端都是本身人,因此咱們也就確定信任該server. 2 . 通信過程當中的數據的泄密和被竄改 1) 通常意義上的https, 就是 server 有一個證書. a) 主要目的是保證server 就是他聲稱的server. 這個跟第一點同樣. b) 服務端和客戶端之間的全部通信,都是加密的. i. 具體講,是客戶端產生一個對稱的密鑰,經過server 的證書來交換密鑰. 通常意義上的握手過程. ii. 全部的信息往來就都是加密的. 第三方即便截獲,也沒有任何意義.由於他沒有密鑰. 固然竄改也就沒有什麼意義了. 2). 少量對客戶端有要求的狀況下,會要求客戶端也必須有一個證書. a) 這裏客戶端證書,其實就相似表示我的信息的時候,除了用戶名/密碼, 還有一個CA 認證過的身份. 應爲我的證書通常來講別人沒法模擬的,全部這樣可以更深的確認本身的身份. b) 目前少數我的銀行的專業版是這種作法,具體證書多是拿U盤做爲一個備份的載體. 3 .HTTPS 必定是繁瑣的. a) 原本簡單的http協議,一個get一個response. 因爲https 要還密鑰和確認加密算法的須要.單握手就須要6/7 個往返. i. 任何應用中,過多的round trip 確定影響性能. b) 接下來纔是具體的http協議,每一次響應或者請求, 都要求客戶端和服務端對會話的內容作加密/解密. i. 儘管對稱加密/解密效率比較高,但是仍然要消耗過多的CPU,爲此有專門的SSL 芯片. 若是CPU 信能比較低的話,確定會下降性能,從而不能serve 更多的請求. ii. 加密後數據量的影響. 因此,纔會出現那麼多的安全認證提示
TCP/IP協議簇是Internet的基礎,也是當今最流行的組網形式。TCP/IP是一組協議的代名詞,包括許多別的協議,組成了TCP/IP協議簇。 其中比較重要的有SLIP協議、PPP協議、IP協議、ICMP協議、ARP協議、TCP協議、UDP協議、FTP協議、DNS協議、SMTP協議等。TCP/IP協議並不徹底符合OSI的七層參考模型。 傳統的開放式系統互連參考模型,是一種通訊協議的7層抽象的參考模型,其中每一層執行某一特定任務。該模型的目的是使各類硬件在相同的層次上相互通訊。 而TCP/IP通信協議採用了4層的層級結構,每一層都呼叫它的下一層所提供的網絡來完成本身的需求 SLIP協議編輯 SLIP提供在串行通訊線路上封裝IP分組的簡單方法,使遠程用戶經過電話線和MODEM能方便地接入TCP/IP網絡。SLIP是一種簡單的組幀方式,但使用時還存在一些問題。 首先,SLIP不支持在鏈接過程當中的動態IP地址分配,通訊雙方必須事先告知對方IP地址,這給沒有固定IP地址的我的用戶上INTERNET網帶來了很大的不便。 其次,SLIP幀中無校驗字段,所以鏈路層上沒法檢測出差錯,必須由上層實體或具備糾錯能力MODEM來解決傳輸差錯問題。 PPP協議編輯 爲了解決SLIP存在的問題,在串行通訊應用中又開發了PPP協議。PPP協議是一種有效的點對點通訊協議,它由串行通訊線路上的組幀方式,用於創建、配製、測試和拆除數據鏈路的鏈路控制協議LCP及一組用以支持不一樣網絡層協議的網絡控制協議NCPs三部分組成。 PPP中的LCP協議提供了通訊雙方進行參數協商的手段,而且提供了一組NCPs協議,使得PPP能夠支持多種網絡層協議,如IP,IPX,OSI等。另外,支持IP的NCP提供了在創建連接時動態分配IP地址的功能,解決了我的用戶上INTERNET網的問題。 IP協議編輯 即互聯網協議(Internet Protocol),它將多個網絡連成一個互聯網,能夠把高層的數據以多個數據包的形式經過互聯網分發出去。IP的基本任務是經過互聯網傳送數據包,各個IP數據包之間是相互獨立的。 ICMP協議編輯 即互聯網控制報文協議。從IP互聯網協議的功能,能夠知道IP 提供的是一種不可靠的無鏈接報文分組傳送服務。 若路由器或主機發生故障時網絡阻塞,就須要通知發送主機採起相應措施。爲了使互聯網能報告差錯,或提供有關意外狀況的信息,在IP層加入了一類特殊用途的報文機制,即ICMP。 分組接收方利用ICMP來通知IP模塊發送方,進行必需的修改。ICMP一般是由發現報文有問題的站產生的,例如可由目的主機或中繼路由器來發現問題併產生的ICMP。 若是一個分組不能傳送,ICMP即可以被用來警告分組源,說明有網絡,主機或端口不可達。ICMP也能夠用來報告網絡阻塞。 ARP協議編輯 即地址轉換協議。在TCP/IP網絡環境下,每一個主機都分配了一個32位的IP地址,這種互聯網地址是在網際範圍標識主機的一種邏輯地址。爲了讓報文在物理網上傳送,必須知道彼此的物理地址。 這樣就存在把互聯網地址變換成物理地址的轉換問題。這就須要在網絡層有一組服務將 IP地址轉換爲相應物理網絡地址,這組協議即ARP。 TCP協議編輯 即傳輸控制協議,它提供的是一種可靠的數據流服務。當傳送受差錯干擾的數據,或舉出網絡故障,或網絡負荷過重而使網際基本傳輸系統不能正常工做時,就須要經過其餘的協議來保證通訊的可靠。 TCP就是這樣的協議。TCP採用「帶重傳的確定確認」技術來實現傳輸的可靠性。並使用「滑動窗口」的流量控制機制來提升網絡的吞吐量。TCP通訊創建實現了一種「虛電路」的概念。 雙方通訊以前,先創建一條連接而後雙方就能夠在其上發送數據流。這種數據交換方式能提升效率,但事先創建鏈接和過後拆除鏈接須要開銷。 UDP協議編輯 即用戶數據包協議,它是對IP協議組的擴充,它增長了一種機制,發送方能夠區分一臺計算機上的多個接收者。每一個UDP報文除了包含數據外還有報文的目的端口的編號和報文源端口的編號,從而使UDP軟件能夠把報文遞送給正確的接收者,而後接收者要發出一個應答。 因爲UDP的這種擴充,使得在兩個用戶進程之間遞送數據包成爲可能。咱們頻繁使用的OICQ軟件正是基於UDP協議和這種機制。 FTP協議編輯 即文件傳輸協議,它是網際提供的用於訪問遠程機器的協議,它使用戶能夠在本地機與遠程機之間進行有關文件的操做。FTP工做時創建兩條TCP連接,分別用於傳送文件和用於傳送控制。 FTP採用客戶/服務器模式?它包含客戶FTP和服務器FTP。客戶FTP啓動傳送過程,而服務器FTP對其做出應答。 DNS協議編輯 即域名服務協議,它提供域名到IP地址的轉換,容許對域名資源進行分散管理。DNS最初設計的目的是使郵件發送方知道郵件接收主機及郵件發送主機的IP地址,後來發展成可服務於其餘許多目標的協議。 SMTP協議編輯 即簡單郵件傳送協議互聯網標準中的電子郵件是一個簡單的基於文本的協議,用於可靠、有效地數據傳輸。SMTP做爲應用層的服務,並不關心它下面採用的是何種傳輸服務, 它可經過網絡在TXP連接上傳送郵件,或者簡單地在同一機器的進程之間經過進程通訊的通道來傳送郵件,這樣,郵件傳輸就獨立於傳輸子系統,可在TCP/IP環境或X.25協議環境中傳輸郵件。
TCP(Transmission Control Protocol,傳輸控制協議)是面向鏈接的協議,也就是說,在收發數據前,必須和對方創建可靠的鏈接。一個TCP鏈接必需要通過三次「對話」才能創建起來,其中的過程很是複雜,只簡單的描述下這三次對話的簡單過程: 主機A向主機B發出鏈接請求數據包:「我想給你發數據,能夠嗎?」,這是第一次對話;主機B向主機A發送贊成鏈接和要求同步(同步就是兩臺主機一個在發送,一個在接收,協調工做)的數據包:「能夠,你何時發?」,這是第二次對話; 主機A再發出一個數據包確認主機B的要求同步:「我如今就發,你接着吧!」,這是第三次對話。三次「對話」的目的是使數據包的發送和接收同步,通過三次「對話」以後,主機A才向主機B正式發送數據 UDP(User Data Protocol,用戶數據報協議) (1) UDP是一個非鏈接的協議,傳輸數據以前源端和終端不創建鏈接,當它想傳送時就簡單地去抓取來自應用程序的數據,並儘量快地把它扔到網絡上。 在發送端,UDP傳送數據的速度僅僅是受應用程序生成數據的速度、計算機的能力和傳輸帶寬的限制;在接收端,UDP把每一個消息段放在隊列中,應用程序每次從隊列中讀一個消息段。 (2) 因爲傳輸數據不創建鏈接,所以也就不須要維護鏈接狀態,包括收發狀態等,所以一臺服務機可同時向多個客戶機傳輸相同的消息。 (3) UDP信息包的標題很短,只有8個字節,相對於TCP的20個字節信息包的額外開銷很小。 (4) 吞吐量不受擁擠控制算法的調節,只受應用軟件生成數據的速率、傳輸帶寬、源端和終端主機性能的限制。 (5)UDP使用盡最大努力交付,即不保證可靠交付,所以主機不須要維持複雜的連接狀態表(這裏面有許多參數)。 (6)UDP是面向報文的。發送方的UDP對應用程序交下來的報文,在添加首部後就向下交付給IP層。既不拆分,也不合並,而是保留這些報文的邊界,所以,應用程序須要選擇合適的報文大小。 咱們常用「ping」命令來測試兩臺主機之間TCP/IP通訊是否正常,其實「ping」命令的原理就是向對方主機發送UDP數據包,而後對方主機確認收到數據包,若是數據包是否到達的消息及時反饋回來,那麼網絡就是通的。 UDP的包頭結構: 源端口 16位 目的端口 16位 長度 16位 校驗和 16位 小結TCP與UDP的區別: 1 .基於鏈接與無鏈接; 2 .對系統資源的要求(TCP較多,UDP少); 3 .UDP程序結構較簡單; 4 .流模式與數據報模式 ; 5 .TCP保證數據正確性,UDP可能丟包,TCP保證數據順序,UDP不保證。
對稱加密是最快速、最簡單的一種加密方式,加密(encryption)與解密(decryption)用的是一樣的密鑰(secret key)。對稱加密有不少種算法,因爲它效率很高,因此被普遍使用在不少加密協議的核心當中。 對稱加密一般使用的是相對較小的密鑰,通常小於256 bit。由於密鑰越大,加密越強,但加密與解密的過程越慢。若是你只用1 bit來作這個密鑰,那黑客們能夠先試着用0來解密,不行的話就再用1解; 但若是你的密鑰有1 MB大,黑客們可能永遠也沒法破解,但加密和解密的過程要花費很長的時間。密鑰的大小既要照顧到安全性,也要照顧到效率,是一個trade-off 常見對稱加密算法 DES算法,3DES算法,TDEA算法,Blowfish算法,RC5算法,IDEA算法 非對稱加密爲數據的加密與解密提供了一個很是安全的方法,它使用了一對密鑰,公鑰(public key)和私鑰(private key)。 私鑰只能由一方安全保管,不能外泄,而公鑰則能夠發給任何請求它的人。非對稱加密使用這對密鑰中的一個進行加密,而解密則須要另外一個密鑰。 好比,你向銀行請求公鑰,銀行將公鑰發給你,你使用公鑰對消息加密,那麼只有私鑰的持有人--銀行才能對你的消息解密。與對稱加密不一樣的是,銀行不須要將私鑰經過網絡發送出去,所以安全性大大提升。 目前最經常使用的非對稱加密算法是RSA算法 Elgamal、揹包算法、Rabin、HD,ECC(橢圓曲線加密算法)
TCP三次握手過程 1 主機A經過向主機B 發送一個含有同步序列號的標誌位的數據段給主機B ,向主機B 請求創建鏈接,經過這個數據段, 主機A告訴主機B 兩件事:我想要和你通訊;你能夠用哪一個序列號做爲起始數據段來回應我. 2 主機B 收到主機A的請求後,用一個帶有確認應答(ACK)和同步序列號(SYN)標誌位的數據段響應主機A,也告訴主機A兩件事: 我已經收到你的請求了,你能夠傳輸數據了;你要用哪佧序列號做爲起始數據段來回應我 3 主機A收到這個數據段後,再發送一個確認應答,確認已收到主機B 的數據段:"我已收到回覆,我如今要開始傳輸實際數據了 這樣3次握手就完成了,主機A和主機B 就能夠傳輸數據了. 3次握手的特色 沒有應用層的數據 SYN這個標誌位只有在TCP建產鏈接時纔會被置1 握手完成後SYN標誌位被置0 TCP創建鏈接要進行3次握手,而斷開鏈接要進行4次 1 當主機A完成數據傳輸後,將控制位FIN置1,提出中止TCP鏈接的請求 2 主機B收到FIN後對其做出響應,確認這一方向上的TCP鏈接將關閉,將ACK置1 3 由B 端再提出反方向的關閉請求,將FIN置1 4 主機A對主機B的請求進行確認,將ACK置1,雙方向的關閉結束. 由TCP的三次握手和四次斷開能夠看出,TCP使用面向鏈接的通訊方式,大大提升了數據通訊的可靠性,使發送數據端 和接收端在數據正式傳輸前就有了交互,爲數據正式傳輸打下了可靠的基礎 名詞解釋 ACK TCP報頭的控制位之一,對數據進行確認.確認由目的端發出,用它來告訴發送端這個序列號以前的數據段 都收到了.好比,確認號爲X,則表示前X-1個數據段都收到了,只有當ACK=1時,確認號纔有效,當ACK=0時,確認號無效,這時會要求重傳數據,保證數據的完整性. SYN 同步序列號,TCP創建鏈接時將這個位置1 FIN 發送端完成發送任務位,當TCP完成數據傳輸須要斷開時,提出斷開鏈接的一方將這位置1 TCP的包頭結構: 源端口 16位 目標端口 16位 序列號 32位 迴應序號 32位 TCP頭長度 4位 reserved 6位 控制代碼 6位 窗口大小 16位 偏移量 16位 校驗和 16位 選項 32位(可選) 這樣咱們得出了TCP包頭的最小長度,爲20字節。
一、session保存在服務器,客戶端不知道其中的信息;cookie保存在客戶端,服務器可以知道其中的信息。 二、session中保存的是對象,cookie中保存的是字符串。 三、session不能區分路徑,同一個用戶在訪問一個網站期間,全部的session在任何一個地方均可以訪問到。而cookie中若是設置了路徑參數,那麼同一個網站中不一樣路徑下的cookie互相是訪問不到的。 四、session須要藉助cookie才能正常。若是客戶端徹底禁止cookie,session將失效。 分佈式Session的幾種實現方式 1 .基於數據庫的Session共享 2 .基於NFS共享文件系統 3 .基於memcached 的session,如何保證 memcached 自己的高可用性? 4 . 基於resin/tomcat web容器自己的session複製機制 5 . 基於TT/Redis 或 jbosscache 進行 session 共享。 6 . 基於cookie 進行session共享
GIT是分佈式的,SVN不是:這是GIT和其它非分佈式的版本控制系統,例如SVN,CVS等,最核心的區 GIT把內容按元數據方式存儲,而SVN是按文件 GIT分支和SVN的分支不一樣: 分支在SVN中一點不特別,就是版本庫中的另外的一個目錄。若是你想知道是否合併了一個分支,你須要手工運行像這樣的命令svn propget svn:mergeinfo,來確認代碼是否被合併。 然而,處理GIT的分支倒是至關的簡單和有趣。你能夠從同一個工做目錄下快速的在幾個分支間切換。你很容易發現未被合併的分支,你能簡單而快捷的合併這些文件 GIT沒有一個全局的版本號,而SVN有 GIT的內容完整性要優於SVN
ThreadLocal是基於線程對象的,相似於一個map ,key爲當前線程對象,因此它能夠在同線程內共享數據
IO的方式一般分爲幾種,同步阻塞的BIO、同步非阻塞的NIO、異步非阻塞的AIO BIO 在JDK1.4出來以前,咱們創建網絡鏈接的時候採用BIO模式,須要先在服務端啓動一個ServerSocket,而後在客戶端啓動Socket來對服務端進行通訊, 默認狀況下服務端須要對每一個請求創建一堆線程等待請求,而客戶端發送請求後,先諮詢服務端是否有線程相應,若是沒有則會一直等待或者遭到拒絕請求,若是有的話,客戶端會線程會等待請求結束後才繼續執行。 2、NIO NIO自己是基於事件驅動思想來完成的,其主要想解決的是BIO的大併發問題: 在使用同步I/O的網絡應用中,若是要同時處理多個客戶端請求,或是在客戶端要同時和多個服務器進行通信,就必須使用多線程來處理。 也就是說,將每個客戶端請求分配給一個線程來單獨處理。這樣作雖然能夠達到咱們的要求,但同時又會帶來另一個問題。因爲每建立一個線程,就要爲這個線程分配必定的內存空間(也叫工做存儲器),並且操做系統自己也對線程的總數有必定的限制。 若是客戶端的請求過多,服務端程序可能會由於不堪重負而拒絕客戶端的請求,甚至服務器可能會所以而癱瘓。 NIO基於Reactor,當socket有流可讀或可寫入socket時,操做系統會相應的通知引用程序進行處理,應用再將流讀取到緩衝區或寫入操做系統。 也就是說,這個時候,已經不是一個鏈接就要對應一個處理線程了,而是有效的請求,對應一個線程,當鏈接沒有數據時,是沒有工做線程來處理的。 BIO與NIO一個比較重要的不一樣,是咱們使用BIO的時候每每會引入多線程,每一個鏈接一個單獨的線程;而NIO則是使用單線程或者只使用少許的多線程,每一個鏈接共用一個線程。 NIO的最重要的地方是當一個鏈接建立後,不須要對應一個線程,這個鏈接會被註冊到多路複用器上面,因此全部的鏈接只須要一個線程就能夠搞定, 當這個線程中的多路複用器進行輪詢的時候,發現鏈接上有請求的話,纔開啓一個線程進行處理,也就是一個請求一個線程模式。 在NIO的處理方式中,當一個請求來的話,開啓線程進行處理,可能會等待後端應用的資源(JDBC鏈接等),其實這個線程就被阻塞了,當併發上來的話,仍是會有BIO同樣的問題。 HTTP/1.1出現後,有了Http長鏈接,這樣除了超時和指明特定關閉的http header外,這個連接是一直打開的狀態的,這樣在NIO處理中能夠進一步的進化,在後端資源中能夠實現資源池或者隊列, 當請求來的話,開啓的線程把請求和請求數據傳送給後端資源池或者隊列裏面就返回,而且在全局的地方保持住這個現場(哪一個鏈接的哪一個請求等),這樣前面的線程仍是能夠去接受其餘的請求, 然後端的應用的處理只須要執行隊列裏面的就能夠了,這樣請求處理和後端應用是異步的.當後端處理完,到全局地方獲得現場,產生響應,這個就實現了異步處理。 3、AIO 與NIO不一樣,當進行讀寫操做時,只須直接調用API的read或write方法便可。這兩種方法均爲異步的,對於讀操做而言,當有流可讀取時,操做系統會將可讀的流傳入read方法的緩衝區,並通知應用程序; 對於寫操做而言,當操做系統將write方法傳遞的流寫入完畢時,操做系統主動通知應用程序。 便可以理解爲,read/write方法都是異步的,完成後會主動調用回調函數。 在JDK1.7中,這部份內容被稱做NIO.2,主要在Java.nio.channels包下增長了下面四個異步通道: AsynchronousSocketChannel AsynchronousServerSocketChannel AsynchronousFileChannel AsynchronousDatagramChannel 其中的read/write方法,會返回一個帶回調函數的對象,當執行完讀取/寫入操做後,直接調用回調函數。 BIO是一個鏈接一個線程。 NIO是一個請求一個線程。 AIO是一個有效請求一個線程。 先來個例子理解一下概念,以銀行取款爲例: 同步 : 本身親自出馬持銀行卡到銀行取錢(使用同步IO時,Java本身處理IO讀寫); 異步 : 委託一小弟拿銀行卡到銀行取錢,而後給你(使用異步IO時,Java將IO讀寫委託給OS處理,須要將數據緩衝區地址和大小傳給OS(銀行卡和密碼),OS須要支持異步IO操做API); 阻塞 : ATM排隊取款,你只能等待(使用阻塞IO時,Java調用會一直阻塞到讀寫完成才返回); 非阻塞 : 櫃檯取款,取個號,而後坐在椅子上作其它事,等號廣播會通知你辦理,沒到號你就不能去,你能夠不斷問大堂經理排到了沒有,大堂經理若是說還沒到你就不能去(使用非阻塞IO時,若是不能讀寫Java調用會立刻返回,當IO事件分發器會通知可讀寫時再繼續進行讀寫,不斷循環直到讀寫完成) Java對BIO、NIO、AIO的支持: Java BIO : 同步並阻塞,服務器實現模式爲一個鏈接一個線程,即客戶端有鏈接請求時服務器端就須要啓動一個線程進行處理,若是這個鏈接不作任何事情會形成沒必要要的線程開銷,固然能夠經過線程池機制改善。 java NIO : 同步非阻塞,服務器實現模式爲一個請求一個線程,即客戶端發送的鏈接請求都會註冊到多路複用器上,多路複用器輪詢到鏈接有I/O請求時才啓動一個線程進行處理。 Java AIO(NIO.2) : 異步非阻塞,服務器實現模式爲一個有效請求一個線程,客戶端的I/O請求都是由OS先完成了再通知服務器應用去啓動線程進行處理, BIO、NIO、AIO適用場景分析: BIO方式適用於鏈接數目比較小且固定的架構,這種方式對服務器資源要求比較高,併發侷限於應用中,JDK1.4之前的惟一選擇,但程序直觀簡單易理解。 NIO方式適用於鏈接數目多且鏈接比較短(輕操做)的架構,好比聊天服務器,併發侷限於應用中,編程比較複雜,JDK1.4開始支持。 AIO方式使用於鏈接數目多且鏈接比較長(重操做)的架構,好比相冊服務器,充分調用OS參與併發操做,編程比較複雜,JDK7開始支持。 另外,I/O屬於底層操做,須要操做系統支持,併發也須要操做系統的支持,因此性能方面不一樣操做系統差別會比較明顯。 在高性能的I/O設計中,有兩個比較著名的模式Reactor和Proactor模式,其中Reactor模式用於同步I/O,而Proactor運用於異步I/O操做。 在比較這兩個模式以前,咱們首先的搞明白幾個概念,什麼是阻塞和非阻塞,什麼是同步和異步,同步和異步是針對應用程序和內核的交互而言的, 同步指的是用戶進程觸發IO操做並等待或者輪詢的去查看IO操做是否就緒,而異步是指用戶進程觸發IO操做之後便開始作本身的事情,而當IO操做已經完成的時候會獲得IO完成的通知。 而阻塞和非阻塞是針對於進程在訪問數據的時候,根據IO操做的就緒狀態來採起的不一樣方式,說白了是一種讀取或者寫入操做函數的實現方式,阻塞方式下讀取或者寫入函數將一直等待,而非阻塞方式下,讀取或者寫入函數會當即返回一個狀態值。 通常來講I/O模型能夠分爲:同步阻塞,同步非阻塞,異步阻塞,異步非阻塞IO 同步阻塞IO:在此種方式下,用戶進程在發起一個IO操做之後,必須等待IO操做的完成,只有當真正完成了IO操做之後,用戶進程才能運行。JAVA傳統的IO模型屬於此種方式! 同步非阻塞IO:在此種方式下,用戶進程發起一個IO操做之後邊可返回作其它事情,可是用戶進程須要時不時的詢問IO操做是否就緒,這就要求用戶進程不停的去詢問,從而引入沒必要要的CPU資源浪費。其中目前JAVA的NIO就屬於同步非阻塞IO。 異步阻塞IO:此種方式下是指應用發起一個IO操做之後,不等待內核IO操做的完成,等內核完成IO操做之後會通知應用程序,這其實就是同步和異步最關鍵的區別,同步必須等待或者主動的去詢問IO是否完成,那麼爲何說是阻塞的呢?由於此時是經過select系統調用來完成的,而select函數自己的實現方式是阻塞的,而採用select函數有個好處就是它能夠同時監聽多個文件句柄,從而提升系統的併發性! 異步非阻塞IO:在此種模式下,用戶進程只須要發起一個IO操做而後當即返回,等IO操做真正的完成之後,應用程序會獲得IO操做完成的通知,此時用戶進程只須要對數據進行處理就行了,不須要進行實際的IO讀寫操做, 由於真正的IO讀取或者寫入操做已經由內核完成了。目前Java中尚未支持此種IO模型
client一個線程調用遠程接口,生成一個惟一的ID(好比一段隨機字符串,UUID等),Dubbo是使用AtomicLong從0開始累計數字的 將打包的方法調用信息(如調用的接口名稱,方法名稱,參數值列表等),和處理結果的回調對象callback,所有封裝在一塊兒,組成一個對象object 向專門存放調用信息的全局ConcurrentHashMap裏面put(ID, object) 將ID和打包的方法調用信息封裝成一對象connRequest,使用IoSession.write(connRequest)異步發送出去 當前線程再使用callback的get()方法試圖獲取遠程返回的結果,在get()內部,則使用synchronized獲取回調對象callback的鎖, 再先檢測是否已經獲取到結果,若是沒有,而後調用callback的wait()方法,釋放callback上的鎖,讓當前線程處於等待狀態。 服務端接收到請求並處理後,將結果(此結果中包含了前面的ID,即回傳)發送給客戶端,客戶端socket鏈接上專門監聽消息的線程收到消息,分析結果,取到ID,再從前面的ConcurrentHashMap裏面get(ID),從而找到callback,將方法調用結果設置到callback對象裏。 監聽線程接着使用synchronized獲取回調對象callback的鎖(由於前面調用過wait(),那個線程已釋放callback的鎖了),再notifyAll(),喚醒前面處於等待狀態的線程繼續執行(callback的get()方法繼續執行就能拿到調用結果了),至此,整個過程結束。 當前線程怎麼讓它「暫停」,等結果回來後,再向後執行? 答:先生成一個對象obj,在一個全局map裏put(ID,obj)存放起來,再用synchronized獲取obj鎖,再調用obj.wait()讓當前線程處於等待狀態,而後另外一消息監聽線程等到服 務端結果來了後,再map.get(ID)找到obj,再用synchronized獲取obj鎖,再調用obj.notifyAll()喚醒前面處於等待狀態的線程。 正如前面所說,Socket通訊是一個全雙工的方式,若是有多個線程同時進行遠程方法調用,這時創建在client server之間的socket鏈接上會有不少雙方發送的消息傳遞,先後順序也多是亂七八糟的,server處理完結果後,將結果消息發送給client,client收到不少消息,怎麼知道哪一個消息結果是原先哪一個線程調用的? 答:使用一個ID,讓其惟一,而後傳遞給服務端,再服務端又回傳回來,這樣就知道結果是原先哪一個線程的了。
在函數中定義的一些基本類型的變量和對象的引用變量都在函數的棧內存中分配。當在一段代碼塊定義一個變量時,Java就在棧中爲這個變量分配內存空間,當超過變量的做用域後,Java會自動釋放掉爲該變量所分配的內存空間,該內存空間能夠當即被另做他用。 堆內存用來存放由new建立的對象和數組。在堆中分配的內存,由Java虛擬機的自動垃圾回收器來管理。在堆中產生了一個數組或對象後,還能夠在棧中定義一個特殊的變量,讓棧中這個變量的取值等於數組或對象在堆內存中的首地址,棧中的這個變量就成了數組或對象的引用變量。引用變量就至關因而爲數組或對象起的一個名稱,之後就能夠在程序中使用棧中的引用變量來訪問堆中的數組或對象。
在TreeMap的put()的實現方法中主要分爲兩個步驟,第一:構建排序二叉樹,第二:平衡二叉樹。 對於排序二叉樹的建立,其添加節點的過程以下: 一、以根節點爲初始節點進行檢索。 二、與當前節點進行比對,若新增節點值較大,則以當前節點的右子節點做爲新的當前節點。不然以當前節點的左子節點做爲新的當前節點。 三、循環遞歸2步驟知道檢索出合適的葉子節點爲止。 四、將新增節點與3步驟中找到的節點進行比對,若是新增節點較大,則添加爲右子節點;不然添加爲左子節點 左旋:rotateLeft() 所謂左旋轉,就是將新增節點(N)當作其父節點(P),將其父節點P當作新增節點(N)的左子節點。即:G.left ---> N ,N.left ---> P。 右旋:rotateRight()所謂右旋轉即,P.right ---> G、G.parent ---> P。
若是隊列是空的,消費者會一直等待,當生產者添加元素時候,消費者是如何知道當前隊列有元素的呢?若是讓你來設計阻塞隊列你會如何設計,讓生產者和消費者可以高效率的進行通信呢?讓咱們先來看看JDK是如何實現的。 使用通知模式實現。所謂通知模式,就是當生產者往滿的隊列裏添加元素時會阻塞住生產者,當消費者消費了一個隊列中的元素後,會通知生產者當前隊列可用。經過查看JDK源碼發現ArrayBlockingQueue使用了Condition來實現 當咱們往隊列裏插入一個元素時,若是隊列不可用,阻塞生產者主要經過LockSupport.park(this);來實現 繼續進入源碼,發現調用setBlocker先保存下將要阻塞的線程,而後調用unsafe.park阻塞當前線程 unsafe.park是個native方法,park這個方法會阻塞當前線程,只有如下四種狀況中的一種發生時,該方法纔會返回。 與park對應的unpark執行或已經執行時。注意:已經執行是指unpark先執行,而後再執行的park。 線程被中斷時。 若是參數中的time不是零,等待了指定的毫秒數時。 發生異常現象時。這些異常事先沒法肯定。 咱們繼續看一下JVM是如何實現park方法的,park在不一樣的操做系統使用不一樣的方式實現,在linux下是使用的是系統方法pthread_cond_wait實現。實現代碼在JVM源碼路徑src/os/linux/vm/os_linux.cpp裏的 os::PlatformEvent::park方法 pthread_cond_wait是一個多線程的條件變量函數,cond是condition的縮寫,字面意思能夠理解爲線程在等待一個條件發生,這個條件是一個全局變量。這個方法接收兩個參數,一個共享變量_cond,一個互斥量_mutex。而unpark方法在linux下是使用pthread_cond_signal實現的。park 在windows下則是使用WaitForSingleObject實現的。 當隊列滿時,生產者往阻塞隊列裏插入一個元素,生產者線程會進入WAITING (parking)狀態
1 無名管道( pipe ):管道是一種半雙工的通訊方式,數據只能單向流動,並且只能在具備親緣關係的進程間使用。進程的2親緣關係一般是指父子進程關係。 2 高級管道(popen):將另外一個程序當作一個新的進程在當前程序進程中啓動,則它算是當前程序的子進程,這種方式咱們成爲高級管道方式。 3 有名管道 (named pipe) : 有名管道也是半雙工的通訊方式,可是它容許無親緣關係進程間的通訊。 4 消息隊列( message queue ) : 消息隊列是由消息的鏈表,存放在內核中並由消息隊列標識符標識。消息隊列克服了信號傳遞信息少、管道只能承載無格式字節流以及緩衝區大小受限等缺點。 5 信號量( semophore ) : 信號量是一個計數器,能夠用來控制多個進程對共享資源的訪問。它常做爲一種鎖機制,防止某進程正在訪問共享資源時,其餘進程也訪問該資源。所以,主要做爲進程間以及同一進程內不一樣線程之間的同步手段。 6信號 ( sinal ) : 信號是一種比較複雜的通訊方式,用於通知接收進程某個事件已經發生。 7共享內存( shared memory ) :共享內存就是映射一段能被其餘進程所訪問的內存,這段共享內存由一個進程建立,但多個進程均可以訪問。共享內存是最快的 IPC 方式,它是針對其餘進程間通訊方式運行效率低而專門設計的。它每每與其餘通訊機制,如信號兩,配合使用,來實現進程間的同步和通訊。 8 套接字( socket ) : 套解口也是一種進程間通訊機制,與其餘通訊機制不一樣的是,它可用於不一樣機器間的進程通訊
一、newCachedThreadPool:用來建立一個可緩存線程池,該線程池沒有長度限制,對於新的任務,若是有空閒的線程,則使用空閒的線程執行,若是沒有,則新建一個線程來執行任務。若是線程池長度超過處理須要,可靈活回收空閒線程 二、newFixedThreadPool :用來建立一個定長線程池,可控制線程最大併發數,超出的線程會在隊列中等待。定長線程池的大小一般根據系統資源進行設置:Runtime.getRuntime().availableProcessors() 三、newScheduledThreadPool:用來建立一個定長線程池,而且支持定時和週期性的執行任務 四、newSingleThreadExecutor:用來建立一個單線程化的線程池,它只用惟一的工做線程來執行任務,一次只支持一個,全部任務按照指定的順序執行
在Java中,若是每當一個請求到達就建立一個新線程,開銷是至關大的。在實際使用中,每一個請求建立新線程的服務器在建立和銷燬線程上花費的時間和消耗的系統資源,甚至可能要比花在處理實際的用戶請求的時間和資源要多得多。 除了建立和銷燬線程的開銷以外,活動的線程也須要消耗系統資源。若是在一個JVM裏建立太多的線程,可能會致使系統因爲過分消耗內存或「切換過分」而致使系統資源不足。 爲了防止資源不足,服務器應用程序須要一些辦法來限制任何給定時刻處理的請求數目,儘量減小建立和銷燬線程的次數,特別是一些資源耗費比較大的線程的建立和銷燬,儘可能利用已有對象來進行服務,這就是「池化資源」技術產生的緣由。 線程池主要用來解決線程生命週期開銷問題和資源不足問題。經過對多個任務重用線程,線程建立的開銷就被分攤到了多個任務上了,並且因爲在請求到達時線程已經存在,因此消除了線程建立所帶來的延遲。 這樣,就能夠當即爲請求服務,使應用程序響應更快。另外,經過適當地調整線程池中的線程數目能夠防止出現資源不足的狀況
volatile關鍵字,做用是強制線程去公共堆棧中訪問isContinuePrint的值。 使用volatile關鍵字增長了實例變量在多個線程之間的可見性,但volatile關鍵字有一個致命的缺陷是不支持原子性 synchronized與volatile關鍵字之間的比較 關鍵字volatile是線程同步的輕量實現,因此volatile關鍵字性能比synchronized好。volatile只能修飾變量,synchronized能夠修飾方法,代碼塊 volatile不會阻塞線程,synchronized會阻塞線程 volatile能保證數據的可見性,不保證原子性,synchronized能夠保證原子性,能夠間接保證可見性,它會將公共內存和私有內存的數據作同步處理。 volatile解決的是變量在多個線程之間的可見性,synchronized解決的是多個線程之間訪問資源的同步性 請記住Java的同步機制都是圍繞兩點:原子性,線程之間的可見性.只有知足了這兩點才能稱得上是同步的。Java中的synchronized和volatile兩個關鍵字分別執行的是原子性和線程之間的可見性
爲查詢緩存優化你的查詢 EXPLAIN 你的 SELECT 查詢 當只要一行數據時使用 LIMIT 1 爲搜索字段建索引 在Join表的時候使用至關類型的例,並將其索引 千萬不要 ORDER BY RAND() 避免 SELECT * 永遠爲每張表設置一個ID,使用數字自增 使用 ENUM 而不是 VARCHAR 從 PROCEDURE ANALYSE() 取得建議 儘量的使用 NOT NULL Prepared Statements很像存儲過程,是一種運行在後臺的SQL語句集合,咱們能夠從使用 prepared statements 得到不少好處,不管是性能問題仍是安全問題 無緩衝的查詢 把IP地址存成 UNSIGNED INT 固定長度的表會更快 垂直分割 拆分大的 DELETE 或 INSERT 語句 越小的列會越快 選擇正確的存儲引擎 MyISAM 適合於一些須要大量查詢的應用,但其對於有大量寫操做並非很好。甚至你只是須要update一個字段,整個表都會被鎖起來,而別的進程,就算是讀進程都沒法操做直到讀操做完成。另外,MyISAM 對於 SELECT COUNT(*) 這類的計算是超快無比的。 InnoDB 的趨勢會是一個很是複雜的存儲引擎,對於一些小的應用,它會比 MyISAM 還慢。他是它支持「行鎖」 ,因而在寫操做比較多的時候,會更優秀。而且,他還支持更多的高級應用,好比:事務 使用一個對象關係映射器(Object Relational Mapper) 當心「永久連接」 「永久連接」的目的是用來減小從新建立MySQL連接的次數。當一個連接被建立了,它會永遠處在鏈接的狀態,就算是數據庫操做已經結束了。並且,自從咱們的Apache開始重用它的子進程後——也就是說,下一次的HTTP請求會重用Apache的子進程,並重用相同的 MySQL 連接
1 .對查詢進行優化,應儘可能避免全表掃描,首先應考慮在 where 及 order by 涉及的列上創建索引。 2 .應儘可能避免在 where 子句中使用!=或<>操做符,不然將引擎放棄使用索引而進行全表掃描。 3 .應儘可能避免在 where 子句中對字段進行 null 值判斷,不然將致使引擎放棄使用索引而進行全表掃描,如: select id from t where num is null 能夠在num上設置默認值0,確保表中num列沒有null值,而後這樣查詢: select id from t where num=0 4 .應儘可能避免在 where 子句中使用 or 來鏈接條件,不然將致使引擎放棄使用索引而進行全表掃描,如: select id from t where num=10 or num=20 能夠這樣查詢: select id from t where num=10 union all select id from t where num=20 5 .下面的查詢也將致使全表掃描: select id from t where name like '%abc%' 若要提升效率,能夠考慮全文檢索。 6 .in 和 not in 也要慎用,不然會致使全表掃描,如: select id from t where num in(1,2,3) 對於連續的數值,能用 between 就不要用 in 了: select id from t where num between 1 and 3 7 .若是在 where 子句中使用參數,也會致使全表掃描。由於SQL只有在運行時纔會解析局部變量,但優化程序不能將訪問計劃的選擇推遲到運行時;它必須在編譯時進行選擇。然而,若是在編譯時創建訪問計劃,變量的值仍是未知的,於是沒法做爲索引選擇的輸入項。以下面語句將進行全表掃描: select id from t where num=@num 能夠改成強制查詢使用索引: select id from t with(index(索引名)) where num=@num 8 .應儘可能避免在 where 子句中對字段進行表達式操做,這將致使引擎放棄使用索引而進行全表掃描。如: select id from t where num/2=100 應改成: select id from t where num=100*2 9 .應儘可能避免在where子句中對字段進行函數操做,這將致使引擎放棄使用索引而進行全表掃描。如: select id from t where substring(name,1,3)='abc'--name以abc開頭的id select id from t where datediff(day,createdate,'2005-11-30')=0--'2005-11-30'生成的id 應改成: select id from t where name like 'abc%' select id from t where createdate>='2005-11-30' and createdate<'2005-12-1' 10 .不要在 where 子句中的「=」左邊進行函數、算術運算或其餘表達式運算,不然系統將可能沒法正確使用索引。 11 .在使用索引字段做爲條件時,若是該索引是複合索引,那麼必須使用到該索引中的第一個字段做爲條件時才能保證系統使用該索引,不然該索引將不會被使用,而且應儘量的讓字段順序與索引順序相一致。 12 .不要寫一些沒有意義的查詢,如須要生成一個空表結構: select col1,col2 into #t from t where 1=0 這類代碼不會返回任何結果集,可是會消耗系統資源的,應改爲這樣: create table #t(...) 13 .不少時候用 exists 代替 in 是一個好的選擇: select num from a where num in(select num from b) 用下面的語句替換: select num from a where exists(select 1 from b where num=a.num) 14 .並非全部索引對查詢都有效,SQL是根據表中數據來進行查詢優化的,當索引列有大量數據重複時,SQL查詢可能不會去利用索引,如一表中有字段sex,male、female幾乎各一半,那麼即便在sex上建了索引也對查詢效率起不了做用。 15 .索引並非越多越好,索引當然能夠提升相應的 select 的效率,但同時也下降了 insert 及 update 的效率,由於 insert 或 update 時有可能會重建索引,因此怎樣建索引須要慎重考慮,視具體狀況而定。一個表的索引數最好不要超過6個,若太多則應考慮一些不常使用到的列上建的索引是否有必要。 16 .應儘量的避免更新 clustered 索引數據列,由於 clustered 索引數據列的順序就是表記錄的物理存儲順序,一旦該列值改變將致使整個表記錄的順序的調整,會耗費至關大的資源。若應用系統須要頻繁更新 clustered 索引數據列,那麼須要考慮是否應將該索引建爲 clustered 索引。 17 .儘可能使用數字型字段,若只含數值信息的字段儘可能不要設計爲字符型,這會下降查詢和鏈接的性能,並會增長存儲開銷。這是由於引擎在處理查詢和鏈接時會逐個比較字符串中每個字符,而對於數字型而言只須要比較一次就夠了。 18 .儘量的使用 varchar/nvarchar 代替 char/nchar ,由於首先變長字段存儲空間小,能夠節省存儲空間,其次對於查詢來講,在一個相對較小的字段內搜索效率顯然要高些。 19 .任何地方都不要使用 select * from t ,用具體的字段列表代替「*」,不要返回用不到的任何字段。 20 .儘可能使用表變量來代替臨時表。若是表變量包含大量數據,請注意索引很是有限(只有主鍵索引)。 21 .避免頻繁建立和刪除臨時表,以減小系統表資源的消耗。 22 .臨時表並非不可以使用,適當地使用它們能夠使某些例程更有效,例如,當須要重複引用大型表或經常使用表中的某個數據集時。可是,對於一次性事件,最好使用導出表。 23 .在新建臨時表時,若是一次性插入數據量很大,那麼能夠使用 select into 代替 create table,避免形成大量 log ,以提升速度;若是數據量不大,爲了緩和系統表的資源,應先create table,而後insert。 24 .若是使用到了臨時表,在存儲過程的最後務必將全部的臨時表顯式刪除,先 truncate table ,而後 drop table ,這樣能夠避免系統表的較長時間鎖定。 25 .儘可能避免使用遊標,由於遊標的效率較差,若是遊標操做的數據超過1萬行,那麼就應該考慮改寫。 26 .使用基於遊標的方法或臨時表方法以前,應先尋找基於集的解決方案來解決問題,基於集的方法一般更有效。 27 .與臨時表同樣,遊標並非不可以使用。對小型數據集使用 FAST_FORWARD 遊標一般要優於其餘逐行處理方法,尤爲是在必須引用幾個表才能得到所需的數據時。在結果集中包括「合計」的例程一般要比使用遊標執行的速度快。若是開發時間容許,基於遊標的方法和基於集的方法均可以嘗試一下,看哪種方法的效果更好。 28 .在全部的存儲過程和觸發器的開始處設置 SET NOCOUNT ON ,在結束時設置 SET NOCOUNT OFF 。無需在執行存儲過程和觸發器的每一個語句後向客戶端發送 DONE_IN_PROC 消息。 29 .儘可能避免向客戶端返回大數據量,若數據量過大,應該考慮相應需求是否合理。 30 .儘可能避免大事務操做,提升系統併發能力
MySQL目前主要有如下幾種索引類型:FULLTEXT,HASH,BTREE,RTREE。 那麼,這幾種索引有什麼功能和性能上的不一樣呢? FULLTEXT 即爲全文索引,目前只有MyISAM引擎支持。其能夠在CREATE TABLE ,ALTER TABLE ,CREATE INDEX 使用,不過目前只有 CHAR、VARCHAR ,TEXT 列上能夠建立全文索引。 值得一提的是,在數據量較大時候,現將數據放入一個沒有全局索引的表中,而後再用CREATE INDEX建立FULLTEXT索引,要比先爲一張表創建FULLTEXT而後再將數據寫入的速度快不少。 全文索引並非和MyISAM一塊兒誕生的,它的出現是爲了解決WHERE name LIKE 「%word%"這類針對文本的模糊查詢效率較低的問題。在沒有全文索引以前,這樣一個查詢語句是要進行遍歷數據表操做的, 可見,在數據量較大時是極其的耗時的,若是沒有異步IO處理,進程將被挾持,很浪費時間,固然這裏不對異步IO做進一步講解,想了解的童鞋,自行谷哥。 全文索引的使用方法並不複雜: 建立ALTER TABLE table ADD INDEX `FULLINDEX` USING FULLTEXT(`cname1`[,cname2…]); 使用SELECT * FROM table WHERE MATCH(cname1[,cname2…]) AGAINST ('word' MODE ); 其中, MODE爲搜尋方式(IN BOOLEAN MODE ,IN NATURAL LANGUAGE MODE ,IN NATURAL LANGUAGE MODE WITH QUERY EXPANSION / WITH QUERY EXPANSION)。 關於這三種搜尋方式,愚安在這裏也很少作交代,簡單地說,就是,布爾模式,容許word裏含一些特殊字符用於標記一些具體的要求,如+表示必定要有,-表示必定沒有,*表示通用匹配符,是否是想起了正則,相似吧; 天然語言模式,就是簡單的單詞匹配;含表達式的天然語言模式,就是先用天然語言模式處理,對返回的結果,再進行表達式匹配。 對搜索引擎稍微有點了解的同窗,確定知道分詞這個概念,FULLTEXT索引也是按照分詞原理創建索引的。西文中,大部分爲字母文字,分詞能夠很方便的按照空格進行分割。 但很明顯,中文不能按照這種方式進行分詞。那又怎麼辦呢?這個向你們介紹一個mysql的中文分詞插件Mysqlcft,有了它,就能夠對中文進行分詞,想了解的同窗請移步Mysqlcft,固然還有其餘的分詞插件能夠使用。 HASH Hash這個詞,能夠說,自打咱們開始碼的那一天起,就開始不停地見到和使用到了。其實,hash就是一種(key=>value)形式的鍵值對,如數學中的函數映射,容許多個key對應相同的value,但不容許一個key對應多個value。 正是因爲這個特性,hash很適合作索引,爲某一列或幾列創建hash索引,就會利用這一列或幾列的值經過必定的算法計算出一個hash值,對應一行或幾行數據(這裏在概念上和函數映射有區別,不要混淆)。 在Java語言中,每一個類都有本身的hashcode()方法,沒有顯示定義的都繼承自object類,該方法使得每個對象都是惟一的,在進行對象間equal比較,和序列化傳輸中起到了很重要的做用。 hash的生成方法有不少種,足能夠保證hash碼的惟一性,例如在MongoDB中,每個document都有系統爲其生成的惟一的objectID(包含時間戳,主機散列值,進程PID,和自增ID)也是一種hash的表現。 因爲hash索引能夠一次定位,不須要像樹形索引那樣逐層查找,所以具備極高的效率。那爲何還須要其餘的樹形索引呢? 在這裏愚安就不本身總結了。引用下園子裏其餘大神的文章:來自 14的路 的MySQL的btree索引和hash索引的區別 (1)Hash 索引僅僅能知足"=","IN"和"<=>"查詢,不能使用範圍查詢。 因爲 Hash 索引比較的是進行 Hash 運算以後的 Hash 值,因此它只能用於等值的過濾,不能用於基於範圍的過濾,由於通過相應的 Hash 算法處理以後的 Hash 值的大小關係,並不能保證和Hash運算前徹底同樣。 (2)Hash 索引沒法被用來避免數據的排序操做。 因爲 Hash 索引中存放的是通過 Hash 計算以後的 Hash 值,並且Hash值的大小關係並不必定和 Hash 運算前的鍵值徹底同樣,因此數據庫沒法利用索引的數據來避免任何排序運算; (3)Hash 索引不能利用部分索引鍵查詢。 對於組合索引,Hash 索引在計算 Hash 值的時候是組合索引鍵合併後再一塊兒計算 Hash 值,而不是單獨計算 Hash 值,因此經過組合索引的前面一個或幾個索引鍵進行查詢的時候,Hash 索引也沒法被利用。 (4)Hash 索引在任什麼時候候都不能避免表掃描。 前面已經知道,Hash 索引是將索引鍵經過 Hash 運算以後,將 Hash運算結果的 Hash 值和所對應的行指針信息存放於一個 Hash 表中,因爲不一樣索引鍵存在相同 Hash 值,因此即便取知足某個 Hash 鍵值的數據的記錄條數, 也沒法從 Hash 索引中直接完成查詢,仍是要經過訪問表中的實際數據進行相應的比較,並獲得相應的結果。 (5)Hash 索引遇到大量Hash值相等的狀況後性能並不必定就會比B-Tree索引高。 對於選擇性比較低的索引鍵,若是建立 Hash 索引,那麼將會存在大量記錄指針信息存於同一個 Hash 值相關聯。這樣要定位某一條記錄時就會很是麻煩,會浪費屢次表數據的訪問,而形成總體性能低下。 愚安我稍做補充,講一下HASH索引的過程,順便解釋下上面的第4,5條: 當咱們爲某一列或某幾列創建hash索引時(目前就只有MEMORY引擎顯式地支持這種索引),會在硬盤上生成相似以下的文件: hash值 存儲地址 1db54bc745a1 77#45b5 4bca452157d4 76#4556,77#45cc… … hash值即爲經過特定算法由指定列數據計算出來,磁盤地址即爲所在數據行存儲在硬盤上的地址(也有多是其餘存儲地址,其實MEMORY會將hash表導入內存)。 這樣,當咱們進行WHERE age = 18 時,會將18經過相同的算法計算出一個hash值==>在hash表中找到對應的儲存地址==>根據存儲地址取得數據。 因此,每次查詢時都要遍歷hash表,直到找到對應的hash值,如(4),數據量大了以後,hash表也會變得龐大起來,性能降低,遍歷耗時增長,如(5)。 BTREE BTREE索引就是一種將索引值按必定的算法,存入一個樹形的數據結構中,相信學過數據結構的童鞋都對當初學習二叉樹這種數據結構的經歷記憶猶新,反正愚安我當時爲了軟考但是被這玩意兒好好地折騰了一番,不過那次考試好像沒怎麼考這個。 如二叉樹同樣,每次查詢都是從樹的入口root開始,依次遍歷node,獲取leaf。 BTREE在MyISAM裏的形式和Innodb稍有不一樣 在 Innodb裏,有兩種形態:一是primary key形態,其leaf node裏存放的是數據,並且不只存放了索引鍵的數據,還存放了其餘字段的數據。二是secondary index,其leaf node和普通的BTREE差很少,只是還存放了指向主鍵的信息. 而在MyISAM裏,主鍵和其餘的並無太大區別。不過和Innodb不太同樣的地方是在MyISAM裏,leaf node裏存放的不是主鍵的信息,而是指向數據文件裏的對應數據行的信息. RTREE RTREE在mysql不多使用,僅支持geometry數據類型,支持該類型的存儲引擎只有MyISAM、BDb、InnoDb、NDb、Archive幾種。 相對於BTREE,RTREE的優點在於範圍查找. 各類索引的使用狀況 (1)對於BTREE這種Mysql默認的索引類型,具備廣泛的適用性 (2)因爲FULLTEXT對中文支持不是很好,在沒有插件的狀況下,最好不要使用。其實,一些小的博客應用,只須要在數據採集時,爲其創建關鍵字列表,經過關鍵字索引,也是一個不錯的方法,至少愚安我是常常這麼作的。 (3)對於一些搜索引擎級別的應用來講,FULLTEXT一樣不是一個好的處理方法,Mysql的全文索引創建的文件仍是比較大的,並且效率不是很高,即使是使用了中文分詞插件,對中文分詞支持也只是通常。真要碰到這種問題,Apache的Lucene或許是你的選擇。 (4)正是由於hash表在處理較小數據量時具備無可比擬的素的優點,因此hash索引很適合作緩存(內存數據庫)。如mysql數據庫的內存版本Memsql,使用量很普遍的緩存工具Mencached,NoSql數據庫Redis等,都使用了hash索引這種形式。 固然,不想學習這些東西的話Mysql的MEMORY引擎也是能夠知足這種需求的。 (5)至於RTREE,愚安我至今尚未使用過,它具體怎麼樣,我就不知道了。有RTREE使用經歷的同窗,到時能夠交流下!
MySQL5.5之後默認使用InnoDB存儲引擎,其中InnoDB和BDB提供事務安全表,其它存儲引擎都是非事務安全表。 若要修改默認引擎,能夠修改配置文件中的default-storage-engine。能夠經過:show variables like 'default_storage_engine';查看當前數據庫到默認引擎。 命令:show engines和show variables like 'have%'能夠列出當前數據庫所支持到引擎。其中Value顯示爲disabled的記錄表示數據庫支持此引擎,而在數據庫啓動時被禁用。 在MySQL5.1之後,INFORMATION_SCHEMA數據庫中存在一個ENGINES的表,它提供的信息與show engines;語句徹底同樣,能夠使用下面語句來查詢哪些存儲引擎支持事物處理:select engine from information_chema.engines where transactions = 'yes'; 能夠經過engine關鍵字在建立或修改數據庫時指定所使用到引擎。 主要存儲引擎:MyISAM、InnoDB、MEMORY和MERGE介紹: 在建立表到時候經過engine=...或type=...來指定所要使用到引擎。show table status from DBname來查看指定表到引擎。 (一)MyISAM 它不支持事務,也不支持外鍵,尤爲是訪問速度快,對事務完整性沒有要求或者以SELECT、INSERT爲主的應用基本均可以使用這個引擎來建立表。 每一個MyISAM在磁盤上存儲成3個文件,其中文件名和表名都相同,可是擴展名分別爲: .frm(存儲表定義) MYD(MYData,存儲數據) MYI(MYIndex,存儲索引) 數據文件和索引文件能夠放置在不一樣的目錄,平均分配IO,獲取更快的速度。要指定數據文件和索引文件的路徑,須要在建立表的時候經過DATA DIRECTORY和INDEX DIRECTORY語句指定,文件路徑須要使用絕對路徑。 每一個MyISAM表都有一個標誌,服務器或myisamchk程序在檢查MyISAM數據表時會對這個標誌進行設置。MyISAM表還有一個標誌用來代表該數據表在上次使用後是否是被正常的關閉了。 若是服務器覺得當機或崩潰,這個標誌能夠用來判斷數據表是否須要檢查和修復。若是想讓這種檢查自動進行,能夠在啓動服務器時使用--myisam-recover現象。這會讓服務器在每次打開一個MyISAM數據表是自動檢查數據表的標誌並進行必要的修復處理。 MyISAM類型的表可能會損壞,能夠使用CHECK TABLE語句來檢查MyISAM表的健康,並用REPAIR TABLE語句修復一個損壞到MyISAM表。 MyISAM的表還支持3種不一樣的存儲格式: 靜態(固定長度)表 動態表 壓縮表 其中靜態表是默認的存儲格式。靜態表中的字段都是非變長字段,這樣每一個記錄都是固定長度的,這種存儲方式的優勢是存儲很是迅速,容易緩存,出現故障容易恢復; 缺點是佔用的空間一般比動態表多。靜態表在數據存儲時會根據列定義的寬度定義補足空格,可是在訪問的時候並不會獲得這些空格,這些空格在返回給應用以前已經去掉。 同時須要注意:在某些狀況下可能須要返回字段後的空格,而使用這種格式時後面到空格會被自動處理掉。 動態表包含變長字段,記錄不是固定長度的,這樣存儲的優勢是佔用空間較少,可是頻繁到更新刪除記錄會產生碎片,須要按期執行OPTIMIZE TABLE語句或myisamchk -r命令來改善性能,而且出現故障的時候恢復相對比較困難。 壓縮表由myisamchk工具建立,佔據很是小的空間,由於每條記錄都是被單獨壓縮的,因此只有很是小的訪問開支。 (二)InnoDB InnoDB存儲引擎提供了具備提交、回滾和崩潰恢復能力的事務安全。可是對比MyISAM的存儲引擎,InnoDB寫的處理效率差一些而且會佔用更多的磁盤空間以保留數據和索引。 1)自動增加列: InnoDB表的自動增加列能夠手工插入,可是插入的若是是空或0,則實際插入到則是自動增加後到值。能夠經過"ALTER TABLE...AUTO_INCREMENT=n;"語句強制設置自動增加值的起始值,默認爲1,可是該強制到默認值是保存在內存中,數據庫重啓後該值將會丟失。 能夠使用LAST_INSERT_ID()查詢當前線程最後插入記錄使用的值。若是一次插入多條記錄,那麼返回的是第一條記錄使用的自動增加值。 對於InnoDB表,自動增加列必須是索引。若是是組合索引,也必須是組合索引的第一列,可是對於MyISAM表,自動增加列能夠是組合索引的其餘列,這樣插入記錄後,自動增加列是按照組合索引到前面幾列排序後遞增的。 2)外鍵約束: MySQL支持外鍵的存儲引擎只有InnoDB,在建立外鍵的時候,父表必須有對應的索引,子表在建立外鍵的時候也會自動建立對應的索引。 在建立索引的時候,能夠指定在刪除、更新父表時,對子表進行的相應操做,包括restrict、cascade、set null和no action。其中restrict和no action相同,是指限制在子表有關聯的狀況下,父表不能更新; casecade表示父表在更新或刪除時,更新或者刪除子表對應的記錄;set null 則表示父表在更新或者刪除的時候,子表對應的字段被set null。 當某個表被其它表建立了外鍵參照,那麼該表對應的索引或主鍵被禁止刪除。 能夠使用set foreign_key_checks=0;臨時關閉外鍵約束,set foreign_key_checks=1;打開約束。 (三)MEMORY memory使用存在內存中的內容來建立表。每一個MEMORY表實際對應一個磁盤文件,格式是.frm。MEMORY類型的表訪問很是快,由於它到數據是放在內存中的,而且默認使用HASH索引,可是一旦服務器關閉,表中的數據就會丟失,但表還會繼續存在。 默認狀況下,memory數據表使用散列索引,利用這種索引進行「相等比較」很是快,可是對「範圍比較」的速度就慢多了。 所以,散列索引值適合使用在"="和"<=>"的操做符中,不適合使用在"<"或">"操做符中,也一樣不適合用在order by字句裏。若是確實要使用"<"或">"或betwen操做符,能夠使用btree索引來加快速度。 存儲在MEMORY數據表裏的數據行使用的是長度不變的格式,所以加快處理速度,這意味着不能使用BLOB和TEXT這樣的長度可變的數據類型。VARCHAR是一種長度可變的類型,但由於它在MySQL內部看成長度固定不變的CHAR類型,因此能夠使用。 create table tab_memory engine=memory select id,name,age,addr from man order by id; 使用USING HASH/BTREE來指定特定到索引。 create index mem_hash using hash on tab_memory(city_id); 在啓動MySQL服務的時候使用--init-file選項,把insert into...select或load data infile 這樣的語句放入到這個文件中,就能夠在服務啓動時從持久穩固的數據源中裝載表。 服務器須要足夠的內存來維持所在的在同一時間使用的MEMORY表,當再也不使用MEMORY表時,要釋放MEMORY表所佔用的內存,應該執行DELETE FROM或truncate table或者刪除整個表。 每一個MEMORY表中放置到數據量的大小,受到max_heap_table_size系統變量的約束,這個系統變量的初始值是16M,同時在建立MEMORY表時能夠使用MAX_ROWS子句來指定表中的最大行數。 (四)MERGE merge存儲引擎是一組MyISAM表的組合,這些MyISAM表結構必須徹底相同,MERGE表中並無數據,對MERGE類型的表能夠進行查詢、更新、刪除的操做,這些操做其實是對內部的MyISAM表進行操做。 對於對MERGE表進行的插入操做,是根據INSERT_METHOD子句定義的插入的表,能夠有3個不一樣的值,first和last值使得插入操做被相應的做用在第一個或最後一個表上,不定義這個子句或者爲NO,表示不能對這個MERGE表進行插入操做。能夠對MERGE表進行drop操做, 這個操做只是刪除MERGE表的定義,對內部的表沒有任何影響。MERGE在磁盤上保留2個以MERGE表名開頭文件:.frm文件存儲表的定義;.MRG文件包含組合表的信息,包括MERGE表由哪些表組成,插入數據時的依據。 能夠經過修改.MRG文件來修改MERGE表,可是修改後要經過flush table刷新。 create table man_all(id int,name varchar(20))engine=merge union=(man1,man2) insert_methos=last;
MySQL數據庫爲咱們提供的四種隔離級別: ① Serializable (串行化):可避免髒讀、不可重複讀、幻讀的發生。 ② Repeatable read (可重複讀):可避免髒讀、不可重複讀的發生。 ③ Read committed (讀已提交):可避免髒讀的發生。 ④ Read uncommitted (讀未提交):最低級別,任何狀況都沒法保證。 以上四種隔離級別最高的是Serializable級別,最低的是Read uncommitted級別,固然級別越高,執行效率就越低。像Serializable這樣的級別,就是以鎖表的方式(相似於Java多線程中的鎖)使得其餘的線程只能在鎖外等待,因此平時選用何種隔離級別應該根據實際狀況。 在MySQL數據庫中默認的隔離級別爲Repeatable read (可重複讀)。 在MySQL數據庫中,支持上面四種隔離級別,默認的爲Repeatable read (可重複讀);而在Oracle數據庫中,只支持Serializable (串行化)級別和Read committed (讀已提交)這兩種級別,其中默認的爲Read committed級別。
在數據庫中,悲觀鎖的流程以下: 在對任意記錄進行修改前,先嚐試爲該記錄加上排他鎖(exclusive locking)。 若是加鎖失敗,說明該記錄正在被修改,那麼當前查詢可能要等待或者拋出異常。 具體響應方式由開發者根據實際須要決定。 若是成功加鎖,那麼就能夠對記錄作修改,事務完成後就會解鎖了。 其間若是有其餘對該記錄作修改或加排他鎖的操做,都會等待咱們解鎖或直接拋出異常。 MySQL InnoDB中使用悲觀鎖 要使用悲觀鎖,咱們必須關閉mysql數據庫的自動提交屬性,由於MySQL默認使用autocommit模式,也就是說,當你執行一個更新操做後,MySQL會馬上將結果進行提交。 set autocommit=0; //0.開始事務 begin;/begin work;/start transaction; (三者選一就能夠) //1.查詢出商品信息 select status from t_goods where id=1 for update; //2.根據商品信息生成訂單 insert into t_orders (id,goods_id) values (null,1); //3.修改商品status爲2 update t_goods set status=2; //4.提交事務 commit;/commit work; 上面的查詢語句中,咱們使用了 select…for update 的方式,這樣就經過開啓排他鎖的方式實現了悲觀鎖。此時在t_goods表中,id爲1的 那條數據就被咱們鎖定了,其它的事務必須等本次事務提交以後才能執行。這樣咱們能夠保證當前的數據不會被其它事務修改。 上面咱們提到,使用 select…for update 會把數據給鎖住,不過咱們須要注意一些鎖的級別,MySQL InnoDB默認行級鎖。行級鎖都是基於索引的,若是一條SQL語句用不到索引是不會使用行級鎖的,會使用表級鎖把整張表鎖住,這點須要注意。 優勢與不足 悲觀併發控制其實是「先取鎖再訪問」的保守策略,爲數據處理的安全提供了保證。可是在效率方面,處理加鎖的機制會讓數據庫產生額外的開銷,還有增長產生死鎖的機會; 另外,在只讀型事務處理中因爲不會產生衝突,也不必使用鎖,這樣作只能增長系統負載;還有會下降了並行性,一個事務若是鎖定了某行數據,其餘事務就必須等待該事務處理完才能夠處理那行數 樂觀鎖 在關係數據庫管理系統裏,樂觀併發控制(又名「樂觀鎖」,Optimistic Concurrency Control,縮寫「OCC」)是一種併發控制的方法。 它假設多用戶併發的事務在處理時不會彼此互相影響,各事務可以在不產生鎖的狀況下處理各自影響的那部分數據。在提交數據更新以前,每一個事務會先檢查在該事務讀取數據後,有沒有其餘事務又修改了該數據。 若是其餘事務有更新的話,正在提交的事務會進行回滾。樂觀事務控制最先是由孔祥重(H.T.Kung)教授提出。 樂觀鎖( Optimistic Locking ) 相對悲觀鎖而言,樂觀鎖假設認爲數據通常狀況下不會形成衝突,因此在數據進行提交更新的時候,纔會正式對數據的衝突與否進行檢測,若是發現衝突了,則讓返回用戶錯誤的信息,讓用戶決定如何去作。 相對於悲觀鎖,在對數據庫進行處理的時候,樂觀鎖並不會使用數據庫提供的鎖機制。通常的實現樂觀鎖的方式就是記錄數據版本。 數據版本,爲數據增長的一個版本標識。當讀取數據時,將版本標識的值一同讀出,數據每更新一次,同時對版本標識進行更新。 當咱們提交更新的時候,判斷數據庫表對應記錄的當前版本信息與第一次取出來的版本標識進行比對,若是數據庫表當前版本號與第一次取出來的版本標識值相等,則予以更新,不然認爲是過時數據。 實現數據版本有兩種方式,第一種是使用版本號,第二種是使用時間戳。 使用版本號實現樂觀鎖 使用版本號時,能夠在數據初始化時指定一個版本號,每次對數據的更新操做都對版本號執行+1操做。並判斷當前版本號是否是該數據的最新的版本號。 1 .查詢出商品信息 select (status,status,version) from t_goods where id=#{id} 2 .根據商品信息生成訂單 3 .修改商品status爲2 update t_goods set status=2,version=version+1 where id=#{id} and version=#{version}; 優勢與不足 樂觀併發控制相信事務之間的數據競爭(data race)的機率是比較小的,所以儘量直接作下去,直到提交的時候纔去鎖定,因此不會產生任何鎖和死鎖。但若是直接簡單這麼作,仍是有可能會遇到不可預期的結果,例如兩個事務都讀取了數據庫的某一行,通過修改之後寫回數據庫,這時就遇到了問題
消息隊列技術是分佈式應用間交換信息的一種技術。消息隊列可駐留在內存或磁盤上,隊列存儲消息直到它們被應用程序讀走。經過消息隊列,應用程序可獨立地執行--它們不須要知道彼此的位置、或在繼續執行前不須要等待接收程序接收此消息。 在分佈式計算環境中,爲了集成分佈式應用,開發者須要對異構網絡環境下的分佈式應用提供有效的通訊手段。爲了管理須要共享的信息,對應用提供公共的信息交換機制是重要的。 消息隊列爲構造以同步或異步方式實現的分佈式應用提供了鬆耦合方法。消息隊列的API調用被嵌入到新的或現存的應用中,經過消息發送到內存或基於磁盤的隊列或從它讀出而提供信息交換。消息隊列可用在應用中以執行多種功能,好比要求服務、交換信息或異步處理等。 中間件是一種獨立的系統軟件或服務程序,分佈式應用系統藉助這種軟件在不一樣的技術之間共享資源,管理計算資源和網絡通信。它在計算機系統中是一個關鍵軟件,它能實現應用的互連和互操做性,能保證系統的安全、可靠、高效的運行。 中間件位於用戶應用和操做系統及網絡軟件之間,它爲應用提供了公用的通訊手段,而且獨立於網絡和操做系統。 中間件爲開發者提供了公用於全部環境的應用程序接口,當應用程序中嵌入其函數調用,它即可利用其運行的特定操做系統和網絡環境的功能,爲應用執行通訊功能。 若是沒有消息中間件完成信息交換,應用開發者爲了傳輸數據,必需要學會如何用網絡和操做系統軟件的功能,編寫相應的應用程序來發送和接收信息,且交換信息沒有標準方法,每一個應用必須進行特定的編程從而和多平臺、不一樣環境下的一個或多個應用通訊。 例如,爲了實現網絡上不一樣主機系統間的通訊,將要求具有在網絡上如何交換信息的知識(好比用TCP/IP的socket程序設計);爲了實現同一主機內不一樣進程之間的通信,將要求具有操做系統的消息隊列或命名管道(Pipes)等知識。 MQ的通信模式 1) 點對點通信:點對點方式是最爲傳統和常見的通信方式,它支持一對1、一對多、多對多、多對一等多種配置方式,支持樹狀、網狀等多種拓撲結構。 2) 多點廣播:MQ適用於不一樣類型的應用。其中重要的,也是正在發展中的是"多點廣播"應用,即可以將消息發送到多個目標站點(Destination List)。 能夠使用一條MQ指令將單一消息發送到多個目標站點,並確保爲每一站點可靠地提供信息。MQ不只提供了多點廣播的功能,並且還擁有智能消息分發功能,在將一條消息發送到同一系統上的多個用戶時,MQ將消息的一個複製版本和該系統上接收者的名單發送到目標MQ系統。 目標MQ系統在本地複製這些消息,並將它們發送到名單上的隊列,從而儘量減小網絡的傳輸量。 3) 發佈/訂閱(Publish/Subscribe)模式:發佈/訂閱功能使消息的分發能夠突破目的隊列地理指向的限制,使消息按照特定的主題甚至內容進行分發,用戶或應用程序能夠根據主題或內容接收到所須要的消息。 發佈/訂閱功能使得發送者和接收者之間的耦合關係變得更爲鬆散,發送者沒必要關心接收者的目的地址,而接收者也沒必要關心消息的發送地址,而只是根據消息的主題進行消息的收發。 在MQ家族產品中,MQ Event Broker是專門用於使用發佈/訂閱技術進行數據通信的產品,它支持基於隊列和直接基於TCP/IP兩種方式的發佈和訂閱。 4) 羣集(Cluster):爲了簡化點對點通信模式中的系統配置,MQ提供Cluster(羣集)的解決方案。羣集相似於一個域(Domain),羣集內部的隊列管理器之間通信時,不須要兩兩之間創建消息通道,而是採用羣集(Cluster)通道與其它成員通信,從而大大簡化了系統配置。 此外,羣集中的隊列管理器之間可以自動進行負載均衡,當某一隊列管理器出現故障時,其它隊列管理器能夠接管它的工做,從而大大提升系統的高可靠性
以ActiveMq爲例 ActiveMQ消息持久化方式,分別是:文件、mysql數據庫、oracle數據庫 a.文件持久化: ActiveMQ默認的消息保存方式,通常若是沒有修改過其餘持久化方式的話能夠不用修改配置文件。 若是是修改過的,打開盤符:\apache-activemq-版本號\conf\activemq.xml,而後找到<persistenceAdapter>節點,將其替換成如下代碼段 <persistenceAdapter> <kahaDB directory="${activemq.base}/data/kahadb"/> </persistenceAdapter> 而後修改配置文件(此處演示爲spring+ActiveMQ),找到消息發送者所對應的JmsTemplate配置代碼塊,增長如下配置 <!-- 是否持久化 DeliveryMode.NON_PERSISTENT=1:非持久 ; DeliveryMode.PERSISTENT=2:持久 --> <property name="deliveryMode" value="2" /> 如下是JmsTemplate配置完整版 <!-- Spring提供的JMS工具類,它能夠進行消息發送、接收等 --> <bean id="jmsTemplateOne" class="org.springframework.jms.core.JmsTemplate"> <property name="connectionFactory" ref="connectionFactory"/> <!-- 設置默認的消息目的地--> <property name="defaultDestination" ref="queueDestination"/> <property name="receiveTimeout" value="10000" /> <!-- 是否持久化 DeliveryMode.NON_PERSISTENT=1:非持久 ; DeliveryMode.PERSISTENT=2:持久 --> <property name="deliveryMode" value="2" /> </bean> 這樣就算配置完成了文件持久化方式了,重啓項目和ActiveMQ,發送必定消息隊列以後關閉ActiveMQ服務,再啓動,你能夠看到以前發送的消息未消費的依然保持在文件裏面,繼續讓監聽者消費。 b.MySQL持久化 首先須要把MySql的驅動放到ActiveMQ的Lib目錄下,我用的文件名字是:mysql-connector-java-5.1.27.jar 而後打開盤符:\apache-activemq-版本號\conf\activemq.xml,而後找到<persistenceAdapter>節點,將其替換成如下代碼段 <persistenceAdapter> <jdbcPersistenceAdapter dataDirectory="${activemq.base}/data" dataSource="#derby-ds"/> </persistenceAdapter> 在配置文件中的broker節點外增長如下代碼 <bean id="derby-ds" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost/activemq?relaxAutoCommit=true"/> <property name="username" value="root"/> <property name="password" value="123456"/> <property name="maxActive" value="200"/> <property name="poolPreparedStatements" value="true"/> </bean> 這樣就算完成了mysql持久化配置了,驗證方式同a,打開mysql數據庫你能看到三張表,分別是:activemq_acks,activemq_lock,activemq_msgs。 c.Oracle持久化 oracle的配置和mysql同樣,在Lib目錄下,放入oracle的驅動包,而後配置一下配置文件便可。 <bean id="derby-ds" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/> <property name="url" value="jdbc:oracle:thin:@localhsot:1521:orcl"/> <property name="username" value="activemq"/> <property name="password" value="amqadmin"/> <property name="maxActive" value="200"/> <property name="poolPreparedStatements" value="true"/> </bean>
1 . Redis中,並非全部的數據都一直存儲在內存中的,這是和Memcached相比一個最大的區別。 2 . redis不只僅支持簡單的k/v類型的數據,同時還提供list,set,hash等數據結構的存儲。 3 . Redis支持數據的備份,即master-slave模式的數據備份。 4 . Redis支持數據的持久化,能夠將內存中的數據保持在磁盤中,重啓的時候能夠再次加載進行使用。 Redis在不少方面具有數據庫的特徵,或者說就是一個數據庫系統,而Memcached只是簡單的K/V緩存
緩存,作key-value型數據庫
RDB持久化配置 Redis會將數據集的快照dump到dump.rdb文件中。此外,咱們也能夠經過配置文件來修改Redis服務器dump快照的頻率,在打開6379.conf文件以後,咱們搜索save,能夠看到下面的配置信息: save 900 1 #在900秒(15分鐘)以後,若是至少有1個key發生變化,則dump內存快照。 save 300 10 #在300秒(5分鐘)以後,若是至少有10個key發生變化,則dump內存快照。 save 60 10000 #在60秒(1分鐘)以後,若是至少有10000個key發生變化,則dump內存快照。 AOF持久化配置 在Redis的配置文件中存在三種同步方式,它們分別是: appendfsync always #每次有數據修改發生時都會寫入AOF文件。 appendfsync everysec #每秒鐘同步一次,該策略爲AOF的缺省策略。 appendfsync no #從不一樣步。高效可是數據不會被持久化。
配置主從服務器 Redis主從服務器的搭建很簡單,只要少量配置便可,爲了演示的方便,咱們就在一臺服務器上配置: 前提是你已經有了一臺redis服務器,若是沒有能夠參考我之前的文章安裝。 下面看看如何配置從服務器: 假設主服務器的配置文件是:/etc/redis.conf,咱們複製一份做爲從服務器的配置文件: cp /etc/redis.conf /etc/redis_slave.conf 並做修改: # vi /etc/redis_slave.conf port 6380 dbfilename dump_slave.rdb slaveof 127.0.0.1 6379 主服務器的端口使用的是缺省的6379,從服務器的端口咱們設置成6380。 而後插入一些測試數據: redis-benchmark 因爲咱們沒有設定任何參數,因此使用的是缺省端口(6379),在本例中就是主服務器。 而後啓動從服務器: redis-server /etc/redis_slave.conf 確認一下是否都正常啓動了: ps -ef | grep redis 進入數據目錄,查一下數據文件的散列: md5sum *.rdb 你會發現數據文件散列都同樣,自動同步了。 而後咱們關閉一下從服務器(不關也行,我就是爲了告訴你如何正確關閉redis服務器): redis-cli -p 6380 shutdown 接着再往主服務器上寫入測試數據: redis-benchmark -l 這會循環插入測試數據,數據量的大小取決於時間的長短,你能夠在適當的時候按ctrl+c中止。 若是從服務器沒有啓動的話,接着再從新啓動從服務器: redis-server /etc/redis_slave.conf 經過觀察文件大小你會發現數據會自動同步,若是沒有重啓動從服務器,那麼數據文件的md5sum散列值可能不一樣,這是正常的,沒關係。 在操做過程當中,有時候你會發現主從服務器的數據文件大小不同,通常來講也不是問題,由於redis是異步寫入磁盤的,此時可能有部分數據還在內存中,沒有同步到磁盤,因此文件大小略顯不一樣,能夠分別在主從服務器上執行: redis-cli save(redis-cli -p 6380 save) 這條命令強制同步到磁盤,再看大小就應該同樣了。 配置文件redis.conf裏有一部分和save相關的參數,缺省以下: # Save the DB on disk: # # save <seconds> <changes> # # Will save the DB if both the given number of seconds and the given # number of write operations against the DB occurred. # # In the example below the behaviour will be to save: # after 900 sec (15 min) if at least 1 key changed # after 300 sec (5 min) if at least 10 keys changed # after 60 sec if at least 10000 keys changed save 900 1 save 300 10 save 60 10000 在主服務器上,咱們能夠去掉上面的設置,改爲相似下面的設置(只要參數值夠大便可): save 10000000000 10000000000 如此一來主服務器變成一個徹底的內存服務器,全部的操做都在內存裏完成,「永遠」不會再往磁盤上持久化保存數據,異步的也沒有。持久化則經過從服務器來完 成,這樣在操做主服務器的時候效率會更高。 不過要注意的一點是此方法不適合保存關鍵數據,不然一旦主服務器掛掉,若是你頭腦一熱簡單的重啓服務,那麼從服 務器的數據也會跟着消失,此時,必須拷貝一份備份數據到主服務器,而後再重啓服務才能夠,數據的恢復稍顯麻煩。 從服務器也能夠經過設置這個參數來調整從內存同步到磁盤的頻率。 利用主從服務器備份 能夠利用主從服務器的方便性來備份,專門作一臺從服務器用於備份功能,當須要備份的時候,在從服務器上執行下列命令: redis-cli save redis-cli shutdown 而後拷貝數據目錄下的rdb文件便可
Redis 集羣中內置了 16384 個哈希槽,當須要在 redis 集羣中放置一個 key-value 時,redis 先對 key 使用 crc16 算法算出一個結果,而後把結果對 16384 求餘數, 這樣每一個 key 都會對應一個編號在 0-16383 之間的哈希槽,redis 會根據節點數量大 致均等的將哈希槽映射到不一樣的節點
Redis內存淘汰指的是用戶存儲的一些鍵被能夠被Redis主動地從實例中刪除,從而產生讀miss的狀況,那麼Redis爲何要有這種功能?這就是咱們須要探究的設計初衷。Redis最多見的兩種應用場景爲緩存和持久存儲,首先要明確的一個問題是內存淘汰策略更適合於那種場景?是持久存儲仍是緩存? 內存的淘汰機制的初衷是爲了更好地使用內存,用必定的緩存miss來換取內存的使用效率。 做爲Redis用戶,我如何使用Redis提供的這個特性呢?看看下面配置 # maxmemory <bytes> 咱們能夠經過配置redis.conf中的maxmemory這個值來開啓內存淘汰功能,至於這個值有什麼意義,咱們能夠經過了解內存淘汰的過程來理解它的意義: 1 . 客戶端發起了須要申請更多內存的命令(如set)。 2 .Redis檢查內存使用狀況,若是已使用的內存大於maxmemory則開始根據用戶配置的不一樣淘汰策略來淘汰內存(key),從而換取必定的內存。 3 .若是上面都沒問題,則這個命令執行成功。 maxmemory爲0的時候表示咱們對Redis的內存使用沒有限制。 Redis提供了下面幾種淘汰策略供用戶選擇,其中默認的策略爲noeviction策略: · noeviction:當內存使用達到閾值的時候,全部引發申請內存的命令會報錯。 · allkeys-lru:在主鍵空間中,優先移除最近未使用的key。 · volatile-lru:在設置了過時時間的鍵空間中,優先移除最近未使用的key。 · allkeys-random:在主鍵空間中,隨機移除某個key。 · volatile-random:在設置了過時時間的鍵空間中,隨機移除某個key。 · volatile-ttl:在設置了過時時間的鍵空間中,具備更早過時時間的key優先移除。 這裏補充一下主鍵空間和設置了過時時間的鍵空間,舉個例子,假設咱們有一批鍵存儲在Redis中,則有那麼一個哈希表用於存儲這批鍵及其值,若是這批鍵中有一部分設置了過時時間,那麼這批鍵還會被存儲到另一個哈希表中,這個哈希表中的值對應的是鍵被設置的過時時間。設置了過時時間的鍵空間爲主鍵空間的子集。 咱們瞭解了Redis大概提供了這麼幾種淘汰策略,那麼如何選擇呢?淘汰策略的選擇能夠經過下面的配置指定: # maxmemory-policy noeviction 可是這個值填什麼呢?爲解決這個問題,咱們須要瞭解咱們的應用請求對於Redis中存儲的數據集的訪問方式以及咱們的訴求是什麼。同時Redis也支持Runtime修改淘汰策略,這使得咱們不須要重啓Redis實例而實時的調整內存淘汰策略。 下面看看幾種策略的適用場景: · allkeys-lru:若是咱們的應用對緩存的訪問符合冪律分佈(也就是存在相對熱點數據),或者咱們不太清楚咱們應用的緩存訪問分佈情況,咱們能夠選擇allkeys-lru策略。 · allkeys-random:若是咱們的應用對於緩存key的訪問機率相等,則能夠使用這個策略。 · volatile-ttl:這種策略使得咱們能夠向Redis提示哪些key更適合被eviction。 另外,volatile-lru策略和volatile-random策略適合咱們將一個Redis實例既應用於緩存和又應用於持久化存儲的時候,然而咱們也能夠經過使用兩個Redis實例來達到相同的效果,值得一提的是將key設置過時時間實際上會消耗更多的內存,所以咱們建議使用allkeys-lru策略從而更有效率的使用內存。
String——字符串 Hash——字典 List——列表 Set——集合 Sorted Set——有序集合
ZooKeeper 顧名思義 動物園管理員,他是拿來管大象(Hadoop) 、 蜜蜂(Hive) 、 小豬(Pig) 的管理員, Apache Hbase和 Apache Solr 以及LinkedIn sensei 等項目中都採用到了 Zookeeper。 ZooKeeper是一個分佈式的,開放源碼的分佈式應用程序協調服務,ZooKeeper是以Fast Paxos算法爲基礎,實現同步服務,配置維護和命名服務等分佈式應用
Zookeeper是針對大型分佈式系統的高可靠的協調系統。由這個定義咱們知道zookeeper是個協調系統,做用的對象是分佈式系統。爲何分佈式系統須要一個協調系統了?理由以下: 開發分佈式系統是件很困難的事情,其中的困難主要體如今分佈式系統的「部分失敗」。「部分失敗」是指信息在網絡的兩個節點之間傳送時候,若是網絡出了故障,發送者沒法知道接收者是否收到了這個信息,並且這種故障的緣由很複雜,接收者可能在出現網絡錯誤以前已經收到了信息,也可能沒有收到,又或接收者的進程死掉了。 發送者可以得到真實狀況的惟一辦法就是從新鏈接到接收者,詢問接收者錯誤的緣由,這就是分佈式系統開發裏的「部分失敗」問題。 Zookeeper就是解決分佈式系統「部分失敗」的框架。Zookeeper不是讓分佈式系統避免「部分失敗」問題,而是讓分佈式系統當碰到部分失敗時候,能夠正確的處理此類的問題,讓分佈式系統能正常的運行。 下面我要講講zookeeper的實際運用場景: 場景一:有一組服務器向客戶端提供某種服務(例如:我前面作的分佈式網站的服務端,就是由四臺服務器組成的集羣,向前端集羣提供服務),咱們但願客戶端每次請求服務端均可以找到服務端集羣中某一臺服務器,這樣服務端就能夠向客戶端提供客戶端所需的服務。 對於這種場景,咱們的程序中必定有一份這組服務器的列表,每次客戶端請求時候,都是從這份列表裏讀取這份服務器列表。那麼這分列表顯然不能存儲在一臺單節點的服務器上,不然這個節點掛掉了,整個集羣都會發生故障,咱們但願這份列表時高可用的。 高可用的解決方案是:這份列表是分佈式存儲的,它是由存儲這份列表的服務器共同管理的,若是存儲列表裏的某臺服務器壞掉了,其餘服務器立刻能夠替代壞掉的服務器,而且能夠把壞掉的服務器從列表裏刪除掉,讓故障服務器退出整個集羣的運行,而這一切的操做又不會由故障的服務器來操做,而是集羣里正常的服務器來完成。 這是一種主動的分佈式數據結構,可以在外部狀況發生變化時候主動修改數據項狀態的數據機構。Zookeeper框架提供了這種服務。這種服務名字就是:統一命名服務,它和javaEE裏的JNDI服務很像。 場景二:分佈式鎖服務。當分佈式系統操做數據,例如:讀取數據、分析數據、最後修改數據。在分佈式系統裏這些操做可能會分散到集羣裏不一樣的節點上,那麼這時候就存在數據操做過程當中一致性的問題,若是不一致,咱們將會獲得一個錯誤的運算結果,在單一進程的程序裏,一致性的問題很好解決,可是到了分佈式系統就比較困難, 由於分佈式系統裏不一樣服務器的運算都是在獨立的進程裏,運算的中間結果和過程還要經過網絡進行傳遞,那麼想作到數據操做一致性要困難的多。Zookeeper提供了一個鎖服務解決了這樣的問題,能讓咱們在作分佈式數據運算時候,保證數據操做的一致性。 場景三:配置管理。在分佈式系統裏,咱們會把一個服務應用分別部署到n臺服務器上,這些服務器的配置文件是相同的(例如:我設計的分佈式網站框架裏,服務端就有4臺服務器,4臺服務器上的程序都是同樣,配置文件都是同樣), 若是配置文件的配置選項發生變化,那麼咱們就得一個個去改這些配置文件,若是咱們須要改的服務器比較少,這些操做還不是太麻煩,若是咱們分佈式的服務器特別多,好比某些大型互聯網公司的hadoop集羣有數千臺服務器,那麼更改配置選項就是一件麻煩並且危險的事情。 這時候zookeeper就能夠派上用場了,咱們能夠把zookeeper當成一個高可用的配置存儲器,把這樣的事情交給zookeeper進行管理,咱們將集羣的配置文件拷貝到zookeeper的文件系統的某個節點上,而後用zookeeper監控全部分佈式系統裏配置文件的狀態, 一旦發現有配置文件發生了變化,每臺服務器都會收到zookeeper的通知,讓每臺服務器同步zookeeper裏的配置文件,zookeeper服務也會保證同步操做原子性,確保每一個服務器的配置文件都能被正確的更新。 場景四:爲分佈式系統提供故障修復的功能。集羣管理是很困難的,在分佈式系統里加入了zookeeper服務,能讓咱們很容易的對集羣進行管理。 集羣管理最麻煩的事情就是節點故障管理,zookeeper可讓集羣選出一個健康的節點做爲master,master節點會知道當前集羣的每臺服務器的運行情況,一旦某個節點發生故障,master會把這個狀況通知給集羣其餘服務器,從而從新分配不一樣節點的計算任務。 Zookeeper不只能夠發現故障,也會對有故障的服務器進行甄別,看故障服務器是什麼樣的故障,若是該故障能夠修復,zookeeper能夠自動修復或者告訴系統管理員錯誤的緣由讓管理員迅速定位問題,修復節點的故障。 你們也許還會有個疑問,master故障了,那怎麼辦了?zookeeper也考慮到了這點,zookeeper內部有一個「選舉領導者的算法」,master能夠動態選擇,當master故障時候,zookeeper能立刻選出新的master對集羣進行管理。 下面我要講講zookeeper的特色: zookeeper是一個精簡的文件系統。這點它和hadoop有點像,可是zookeeper這個文件系統是管理小文件的,而hadoop是管理超大文件的。 zookeeper提供了豐富的「構件」,這些構件能夠實現不少協調數據結構和協議的操做。例如:分佈式隊列、分佈式鎖以及一組同級節點的「領導者選舉」算法。 zookeeper是高可用的,它自己的穩定性是至關之好,分佈式集羣徹底能夠依賴zookeeper集羣的管理,利用zookeeper避免分佈式系統的單點故障的問題。 zookeeper採用了鬆耦合的交互模式。這點在zookeeper提供分佈式鎖上表現最爲明顯,zookeeper能夠被用做一個約會機制,讓參入的進程不在了解其餘進程的(或網絡)的狀況下可以彼此發現並進行交互,參入的各方甚至沒必要同時存在,只要在zookeeper留下一條消息,在該進程結束後,另一個進程還能夠讀取這條信息,從而解耦了各個節點之間的關係。 zookeeper爲集羣提供了一個共享存儲庫,集羣能夠從這裏集中讀寫共享的信息,避免了每一個節點的共享操做編程,減輕了分佈式系統的開發難度。 zookeeper的設計採用的是觀察者的設計模式,zookeeper主要是負責存儲和管理你們關心的數據,而後接受觀察者的註冊,一旦這些數據的狀態發生變化,Zookeeper 就將負責通知已經在 Zookeeper 上註冊的那些觀察者作出相應的反應,從而實現集羣中相似 Master/Slave 管理模式
1 . 接收投票消息。投票消息會包括id,zxid,epoch,state,這四種信息,分別表明 Id: 惟一標識一臺機器,存儲在myid文件中 Zxid: 標識了本機想要選舉誰爲leader,是本機目前所見到的最大的id值 Epoch: 邏輯時鐘。用於判斷選舉是否過時 State: 本機的狀態信息(包括looking,leading,following,observing) 2 .判斷PeerState狀態,若是是looking狀態,則繼續.若是是leading,foolowing,observing則走別的流程 3 .收到票後,會判斷髮送過來的邏輯時鐘是否大於目前的邏輯時鐘,若是是說明集羣已經進入了新一輪的投票了。 4 .清空投票箱。由於這個以前的投票都是上一次投票期間維護的。 5 . 若是等於目前的邏輯時鐘,說明是當前的,則更新最大的leader id和提案id 判斷是否須要更新當前本身的選舉狀況.在這裏是根據選舉leader id,保存的最大數據id來進行判斷的,這兩種數據之間對這個選舉結果的影響的權重關係是:首先看數據id,數據id大者勝出;其次再判斷leader id,leader id大者勝出 判讀投票結果代碼 6 . 發送通知,通知其餘的QuorumPeer更新leader信息.同時將更新後的leader信息放入投票箱 檢查是否已經接收到了全部服務器的投票代碼參考。若是是的,則設置本身的選擇結果 若是沒有接收到全部服務器的投票,那判讀這個leadId是否獲得了一半之後的服務器的投票代碼參考,若是是則返回 以上流程描述的是在zookeeper中,參考使用的算法是FastLeaderElection 在zookeeper的的選主的流程,另外還提供了LeaderElection和AuthFastLeaderElection的實現 LeaderElection的實現比較簡單。以(id,zxid)作爲投票的依據.而且它的實現是同步的,須要等待全部服務器返回後再統計結果。 而相比FastLeaderElection是每次收到回覆都會計算投票結果,效率上會比LeaderElection更好一些
Zookeeper的通訊架構 在Zookeeper整個系統中,有3中角色的服務,client、Follower、leader。其中client負責發起應用的請求,Follower接受client發起的請求,參與事務的確認過程,在leader crash後的leader選擇。 而leader主要承擔事務的協調,固然leader也能夠承擔接收客戶請求的功能,爲了方便描述,後面的描述都是client與Follower之間的通訊,若是Zookeeper的配置支持leader接收client的請求,client與leader的通訊跟client與Follower的通訊模式徹底同樣。 Follower與leader之間的角色可能在某一時刻進行轉換。一個Follower在leader crash掉之後可能被集羣(Quorum)的Follower選舉爲leader。而一個leader在crash後,再次加入集羣(Quorum)將做爲Follower角色存在。 在一個集羣(Quorum)中,除了在選舉leader的過程當中沒有Follower和leader的區分外,其餘任什麼時候刻都只有1個leader和多個Follower。Client、Follower和leader之間的通訊架構以下: Client與Follower之間 爲了使客戶端具備較高的吞吐量,Client與Follower之間採用NIO的通訊方式。當client須要與Zookeeper service打交道時,首先讀取配置文件肯定集羣內的全部server列表,按照必定的load balance算法選取一個Follower做爲一個通訊目標。 這樣client和Follower之間就有了一條由NIO模式構成的通訊通道。這條通道會一直保持到client關閉session或者由於client或Follower任一方因某種緣由異常中斷通訊鏈接。正常狀況下, client與Follower在沒有請求發起的時候都有心跳檢測 Follower與leader之間 Follower與leader之間的通訊主要是由於Follower接收到像(create, delete, setData, setACL, createSession, closeSession, sync)這樣一些須要讓leader來協調最終結果的命令,將會致使Follower與leader之間產生通訊。 因爲leader與Follower之間的關係式一對多的關係,很是適合client/server模式,所以他們之間是採用c/s模式,由leader建立一個socket server,監聽各Follower的協調請求。 集羣在選擇leader過程當中 因爲在選擇leader過程當中沒有leader,在集羣中的任何一個成員都須要與其餘全部成員進行通訊,當集羣的成員變得很大時,這個通訊量是很大的。 選擇leader的過程發生在Zookeeper系統剛剛啓動或者是leader失去聯繫後,選擇leader過程當中將不能處理用戶的請求,爲了提升系統的可用性,必定要儘可能減小這個過程的時間。選擇哪一種方式讓他們可用快速獲得選擇結果呢? Zookeeper在這個過程當中採用了策略模式,可用動態插入選擇leader的算法。系統默認提供了3種選擇算法,AuthFastLeaderElection,FastLeaderElection,LeaderElection。 其中AuthFastLeaderElection和LeaderElection採用UDP模式進行通訊,而FastLeaderElection仍然採用tcp/ip模式。在Zookeeper新的版本中,新增了一個learner角色,減小選擇leader的參與人數。使得選擇過程更快。 通常說來Zookeeper leader的選擇過程都很是快,一般<200ms。 Zookeeper的通訊流程 要詳細瞭解Zookeeper的通訊流程,咱們首先得了解Zookeeper提供哪些客戶端的接口,咱們按照具備相同的通訊流程的接口進行分組: Zookeeper系統管理命令 Zookeeper的系統管理接口是指用來查看Zookeeper運行狀態的一些命令,他們都是具備4字母構成的命令格式。主要包括: ruok:發送此命令能夠測試zookeeper是否運行正常。 dump:dump server端全部存活session的Ephemeral(臨時)node信息。 stat:獲取鏈接server的服務器端的狀態及鏈接該server的全部客服端的狀態信息。 reqs: 獲取當前客戶端已經提交但還未返回的請求。 stmk:開啓或關閉Zookeeper的trace level. gtmk:獲取當前Zookeeper的trace level是否開啓。 envi: 獲取Zookeeper的java相關的環境變量。 srst:重置server端的統計狀態 當用戶發送這些命令的到server時,因爲這些請求只與鏈接的server相關,沒有業務處理邏輯,很是簡單。Zookeeper對這些命令採用最快的效率進行處理。這些命令發送到server端只佔用一個4字節的int類型來表示不一樣命令,沒有采用字符串處理。當服務器端接收到這些命令,馬上返回結果。 Session建立 任何客戶端的業務請求都是基於session存在的前提下。Session是維持client與Follower之間的一條通訊通道,並維持他們之間從建立開始後的全部狀態。 當啓動一個Zookeeper client的時候,首先按照必定的算法查找出follower, 而後與Follower創建起NIO鏈接。當鏈接創建好後,發送create session的命令,讓server端爲該鏈接建立一個維護該鏈接狀態的對象session。當server收到create session命令,先從本地的session列表中查找看是否已經存在有相同sessionId, 則關閉原session從新建立新的session。建立session的過程將須要發送到Leader,再由leader通知其餘follower,大部分Follower都將此操做記錄到本地日誌再通知leader後,leader發送commit命令給全部Follower, 鏈接客戶端的Follower返回建立成功的session響應。Leader與Follower之間的協調過程將在後面的作詳細講解。當客戶端成功建立好session後,其餘的業務命令就能夠正常處理了。 Zookeeper查詢命令 Zookeeper查詢命令主要用來查詢服務器端的數據,不會更改服務器端的數據。全部的查詢命令均可以即刻從client鏈接的server當即返回,不須要leader進行協調,所以查詢命令獲得的數據有多是過時數據。 但因爲任何數據的修改,leader都會將更改的結果發佈給全部的Follower,所以通常說來,Follower的數據是能夠獲得及時的更新。這些查詢命令包括如下這些命令: exists:判斷指定path的node是否存在,若是存在則返回true,不然返回false. getData:從指定path獲取該node的數據 getACL:獲取指定path的ACL。 getChildren:獲取指定path的node的全部孩子結點。 全部的查詢命令均可以指定watcher,經過它來跟蹤指定path的數據變化。一旦指定的數據發生變化(create,delete,modified,children_changed),服務器將會發送命令來回調註冊的watcher. Watcher詳細的講解將在Zookeeper的Watcher中單獨講解。 Zookeeper修改命令 Zookeeper修改命令主要是用來修改節點數據或結構,或者權限信息。任何修改命令都須要提交到leader進行協調,協調完成後才返回。修改命令主要包括: 1 . createSession:請求server建立一個session 2 . create:建立一個節點 3 . delete:刪除一個節點 4 . setData:修改一個節點的數據 5 . setACL:修改一個節點的ACL 6 . closeSession:請求server關閉session 咱們根據前面的通訊圖知道,任何修改命令都須要leader協調。 在leader的協調過程當中,須要3次leader與Follower之間的來回請求響應。而且在此過程當中還會涉及事務日誌的記錄,更糟糕的狀況是還有take snapshot的操做。所以此過程可能比較耗時。但Zookeeper的通訊中最大特色是異步的,若是請求是接二連三的,Zookeeper的處理是集中處理邏輯,而後批量發送,批量的大小也是有控制的。若是請求量不大,則即刻發送。這樣當負載很大時也能保證很大的吞吐量,時效性也在必定程度上進行了保證。 zookeeper server端的業務處理-processor鏈 <!--EndFragment--> Zookeeper經過鏈式的processor來處理業務請求,每一個processor負責處理特定的功能。不一樣的Zookeeper角色的服務器processor鏈是不同的,如下分別介紹standalone Zookeeper server, leader和Follower不一樣的processor鏈。 Zookeeper中的processor AckRequestProcessor:當leader從向Follower發送proposal後,Follower將發送一個Ack響應,leader收到Ack響應後,將會調用這個Processor進行處理。它主要負責檢查請求是否已經達到了多數Follower的確認,若是知足條件,則提交commitProcessor進行commit處理 CommitProcessor:從commited隊列中處理已經由leader協調好並commit的請求或者從請求隊列中取出那些無需leader協調的請求進行下一步處理。 FinalRequestProcessor:任何請求的處理都須要通過這個processor,這是請求處理的最後一個Processor,主要負責根據不一樣的請求包裝不一樣的類型的響應包。固然Follower與leader之間協調後的請求因爲沒有client鏈接,將不須要發送響應(代碼體如今if (request.cnxn == null) {return;})。 FollowerRequestProcessor:Follower processor鏈上的第一個,主要負責將修改請求和同步請求發往leader進行協調。 PrepRequestProcessor:在leader和standalone server上做爲第一Processor,主要做用對於全部的修改命令生成changelog。 ProposalRequestProcessor:leader用來將請求包裝爲proposal向Follower請求確認。 SendAckRequestProcessor:Follower用來向leader發送Ack響應的處理。 SyncRequestProcessor:負責將已經commit的事務寫到事務日誌以及take snapshot. ToBeAppliedRequestProcessor:負責將tobeApplied隊列的中request轉移到下一個請求進行處理。
ZK的節點有5種操做權限: CREATE、READ、WRITE、DELETE、ADMIN 也就是 增、刪、改、查、管理權限,這5種權限簡寫爲crwda(即:每一個單詞的首字符縮寫) 注:這5種權限中,delete是指對子節點的刪除權限,其它4種權限指對自身節點的操做權限 身份的認證有4種方式: world:默認方式,至關於全世界都能訪問 auth:表明已經認證經過的用戶(cli中能夠經過addauth digest user:pwd 來添加當前上下文中的受權用戶) digest:即用戶名:密碼這種方式認證,這也是業務系統中最經常使用的 ip:使用Ip地址認證 設置訪問控制: 方式一:(推薦) 1)增長一個認證用戶 addauth digest 用戶名:密碼明文 eg. addauth digest user1:password1 2)設置權限 setAcl /path auth:用戶名:密碼明文:權限 eg. setAcl /test auth:user1:password1:cdrwa 3)查看Acl設置 getAcl /path 方式二: setAcl /path digest:用戶名:密碼密文:權限 注:這裏的加密規則是SHA1加密,而後base64編碼。
當不少進程須要訪問共享資源時,咱們能夠經過zk來實現分佈式鎖。主要步驟是: 1 .創建一個節點,假如名爲:lock 。節點類型爲持久節點(PERSISTENT) 2 .每當進程須要訪問共享資源時,會調用分佈式鎖的lock()或tryLock()方法得到鎖,這個時候會在第一步建立的lock節點下創建相應的順序子節點,節點類型爲臨時順序節點(EPHEMERAL_SEQUENTIAL),經過組成特定的名字name+lock+順序號。 3 .在創建子節點後,對lock下面的全部以name開頭的子節點進行排序,判斷剛剛創建的子節點順序號是不是最小的節點,假如是最小節點,則得到該鎖對資源進行訪問。 4 .假如不是該節點,就得到該節點的上一順序節點,並給該節點是否存在註冊監聽事件。同時在這裏阻塞。等待監聽事件的發生,得到鎖控制權。 5 .當調用完共享資源後,調用unlock()方法,關閉zk,進而能夠引起監聽事件,釋放該鎖。 實現的分佈式鎖是嚴格的按照順序訪問的併發鎖
pgrep -l java
netstat/lsof netstat命令用於顯示與IP、TCP、UDP和ICMP協議相關的統計數據,通常用於檢驗本機各端口的網絡鏈接狀況 -a 顯示一個全部的有效鏈接信息列表(包括已創建的鏈接,也包括監聽鏈接請求的那些鏈接) -n 顯示全部已創建的有效鏈接 -t tcp協議 -u udp協議 -l 查詢正在監聽的程序 -p 顯示正在使用socket的程序識別碼和程序名稱 例如:netstat -ntupl|grep processname 如何只查詢tomcat的鏈接? netstat -na|grep ESTAB |grep 80 |wc-l netstat -na|grep ESTAB |grep 8080 |wc-l 經常使用端口介紹: 端口:21 服務:FTP服務器所開放的端口,用於上傳、下載。 端口: 22 服務:ssh 端口: 80 服務:HTTP 用於網頁瀏覽 端口:389 服務:LDAP ILS 輕型目錄訪問協議和NetMeetingInternet Locator Server 端口:443 服務:網頁瀏覽端口 能提供加密和經過安全端口傳輸的另外一種HTTP 端口:8080 服務:代理端口 打開終端,執行以下命令,查看各進程佔用端口狀況: # ps -ef|wc -l //查看後臺運行的進程總數 # ps -fu csvn //查看csvn進程 # netstat -lntp //查看開啓了哪些端口 # netstat -r //本選項能夠顯示關於路由表的信息 # netstat -a //本選項顯示一個全部的有效鏈接信息列表 # netstat -an|grep 8080 # netstat -na|grep -i listen //能夠看到目前系統偵聽的端口號 # netstat -antup //查看已創建的鏈接進程,所佔用的端口。 netstat -anp|grep1487 lsof -i:1487 查看哪些進程打開了指定端口1487
tail -f messages
要統計一個字符串出現的次數,這裏現提供本身經常使用兩種方法: 1. 使用vim統計 用vim打開目標文件,在命令模式下,輸入 :%s/objStr//gn 便可 2. 使用grep: grep -o objStr filename|wc -l 若是是多個字符串出現次數,可以使用: grep -o ‘objStr1\|objStr2' filename|wc -l #直接用\| 連接起來便可
isAlive() 測試線程是否處於活動狀態。 isInterrupted()測試線程是否已經中斷。
物理分頁 邏輯分頁
考慮是否要取消單例採用其餘方式實現
根據異常情況和請求情況進行分類。考慮是否須要異步請求,實時數據交互考慮循環請求2次,並且通常在進行網絡通信的過程當中,對於一些比較重要的交互數據會採用日誌記錄的方式來記錄本次通信的情況。後期能夠經過日誌分析異常的主要緣由,並針對性解決。
http://blog.csdn.net/u012116457/article/details/46316063
《鳥哥的Linux私房菜》
http://blog.163.com/hcl_zjblog/blog/static/214281242201712872723221/?newFollowBlog
1.內存中加載的數據量過於龐大,如一次從數據庫取出過多數據; 2.集合類中有對對象的引用,使用完後未清空,使得JVM不能回收; 3.代碼中存在死循環或循環產生過多重複的對象實體; 4.使用的第三方軟件中的BUG; 5.啓動參數內存值設定的太小;
第一步,修改JVM啓動參數,直接增長內存。(-Xms,-Xmx參數必定不要忘記加。) 第二步,檢查錯誤日誌,查看「OutOfMemory」錯誤前是否有其它異常或錯誤。 第三步,對代碼進行走查和分析,找出可能發生內存溢出的位置。
經常使用的有 BoundsCheaker、Deleaker、Visual Leak Detector等
保證數據的原子性一致性隔離性和永久性。通常說爲了保證數據的原子性便可。
java.lang:提供利用 Java 編程語言進行程序設計的基礎類。 java.io:經過數據流、序列化和文件系統提供系統輸入和輸出。 java.lang.ref:強引用,軟引用,弱引用,虛引用。 java.math:提供用於執行任意精度整數算法 (BigInteger) 和任意精度小數算法 (BigDecimal) 的類。 java.concurrent:在併發編程中很經常使用的實用工具類(ThreadFactory)
索引:缺點是索引過多寫操做性能低,並且佔的空間大。優勢是查詢快。數據量較大的狀況下索引通常加在變化量較大的字段上。
隔離級別:http://blog.csdn.net/fg2006/article/details/6937413 事務特徵:https://zhidao.baidu.com/question/212549640.html 事物的隔離級別:髒讀(讀取未提交的記錄),讀取提交,重複讀(幻讀),序列化。 事物的特性:原子性,一致性,隔離性,永久性。 十4、MySQL行鎖、表鎖 悲觀鎖:select .... for update 行鎖,執行時其餘事物不可對此記錄進行操做。 樂觀鎖:根據時間戳,版本號,update ..set version = version + 1. where version = #{version}
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
<!-- 事務管理器配置, Hibernate單數據源事務 --> <bean id="defaultTransactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <!-- 使用annotation定義事務 --> <tx:annotation-driven transaction-manager="defaultTransactionManager" proxy-target-class="true" />
要是隻在接口上寫, 接口的實現類就會繼承下來、接口的實現類的具體方法,能夠覆蓋類聲明處的設置 @Transactional //類級的註解、適用於類中全部的public的方法 事務的傳播行爲和隔離級別 你們在使用spring的註解式事務管理時,對事務的傳播行爲和隔離級別可能有點不知所措,下邊就詳細的介紹下以備方便查閱。 事物註解方式: @Transactional 當標於類前時, 標示類中全部方法都進行事物處理 , 例子: @Transactional public class TestServiceBean implements TestService {} 當類中某些方法不須要事物時: @Transactional public class TestServiceBean implements TestService { private TestDao dao; public void setDao(TestDao dao) { this.dao = dao; } @Transactional(propagation = Propagation.NOT_SUPPORTED) public List<Object> getAll() { return null; } }
@Transactional(propagation=Propagation.REQUIRED) 若是有事務, 那麼加入事務, 沒有的話新建一個(默認狀況下) @Transactional(propagation=Propagation.NOT_SUPPORTED) 容器不爲這個方法開啓事務 @Transactional(propagation=Propagation.REQUIRES_NEW) 無論是否存在事務,都建立一個新的事務,原來的掛起,新的執行完畢,繼續執行老的事務 @Transactional(propagation=Propagation.MANDATORY) 必須在一個已有的事務中執行,不然拋出異常 @Transactional(propagation=Propagation.NEVER) 必須在一個沒有的事務中執行,不然拋出異常(與Propagation.MANDATORY相反) @Transactional(propagation=Propagation.SUPPORTS) 若是其餘bean調用這個方法,在其餘bean中聲明事務,那就用事務.若是其餘bean沒有聲明事務,那就不用事務.
@Transactional(timeout=30) //默認是30秒
@Transactional(isolation = Isolation.READ_UNCOMMITTED) 讀取未提交數據(會出現髒讀, 不可重複讀) 基本不使用 @Transactional(isolation = Isolation.READ_COMMITTED) 讀取已提交數據(會出現不可重複讀和幻讀) @Transactional(isolation = Isolation.REPEATABLE_READ) 可重複讀(會出現幻讀) @Transactional(isolation = Isolation.SERIALIZABLE) 串行化 MYSQL: 默認爲REPEATABLE_READ級別 SQLSERVER: 默認爲READ_COMMITTED 髒讀 : 一個事務讀取到另外一事務未提交的更新數據 不可重複讀 : 在同一事務中, 屢次讀取同一數據返回的結果有所不一樣, 換句話說, 後續讀取能夠讀到另外一事務已提交的更新數據. 相反, "可重複讀"在同一事務中屢次 讀取數據時, 可以保證所讀數據同樣, 也就是後續讀取不能讀到另外一事務已提交的更新數據 幻讀 : 一個事務讀到另外一個事務已提交的insert數據
該屬性用於設置當前事務是否爲只讀事務,設置爲true表示只讀,false則表示可讀寫,默認值爲false。例如:@Transactional(readOnly=true)
該屬性用於設置須要進行回滾的異常類數組,當方法中拋出指定異常數組中的異常時,則進行事務回滾。例如: 指定單一異常類:@Transactional(rollbackFor=RuntimeException.class) 指定多個異常類:@Transactional(rollbackFor={RuntimeException.class, Exception.class})
該屬性用於設置須要進行回滾的異常類名稱數組,當方法中拋出指定異常名稱數組中的異常時,則進行事務回滾。例如: 指定單一異常類名稱:@Transactional(rollbackForClassName="RuntimeException") 指定多個異常類名稱:@Transactional(rollbackForClassName={"RuntimeException","Exception"})
該屬性用於設置不須要進行回滾的異常類數組,當方法中拋出指定異常數組中的異常時,不進行事務回滾。例如: 指定單一異常類:@Transactional(noRollbackFor=RuntimeException.class) 指定多個異常類:@Transactional(noRollbackFor={RuntimeException.class, Exception.class})
該屬性用於設置不須要進行回滾的異常類名稱數組,當方法中拋出指定異常名稱數組中的異常時,不進行事務回滾。例如: 指定單一異常類名稱:@Transactional(noRollbackForClassName="RuntimeException") 指定多個異常類名稱: @Transactional(noRollbackForClassName={"RuntimeException","Exception"})
該屬性用於設置事務的傳播行爲,具體取值可參考表6-7。 例如:@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true)
該屬性用於設置底層數據庫的事務隔離級別,事務隔離級別用於處理多事務併發的狀況,一般使用數據庫的默認隔離級別便可,基本不須要進行設置
該屬性用於設置事務的超時秒數,默認值爲-1表示永不超時
1 @Transactional 只能被應用到public方法上, 對於其它非public的方法,若是標記了@Transactional也不會報錯,但方法沒有事務功能. 2用 spring 事務管理器,由spring來負責數據庫的打開,提交,回滾.默認遇到運行期例外(throw new RuntimeException("註釋");)會回滾,即遇到不受檢查(unchecked)的例外時回滾;而遇到須要捕獲的例外(throw new Exception("註釋");)不會回滾,即遇到受檢查的例外(就是非運行時拋出的異常,編譯器會檢查到的異常叫受檢查例 外或說受檢查異常)時,需咱們指定方式來讓事務回滾 要想全部異常都回滾,要加上 @Transactional( rollbackFor={Exception.class,其它異常}) .若是讓unchecked例外不回滾: @Transactional(notRollbackFor=RunTimeException.class) 以下: @Transactional(rollbackFor=Exception.class) //指定回滾,遇到異常Exception時回滾 public void methodName() { throw new Exception("註釋"); } @Transactional(noRollbackFor=Exception.class)//指定不回滾,遇到運行期例外(throw new RuntimeException("註釋");)會回滾 public ItimDaoImpl getItemDaoImpl() { throw new RuntimeException("註釋"); } 三、@Transactional 註解應該只被應用到 public 可見度的方法上。 若是你在 protected、private 或者 package-visible 的方法上使用 @Transactional 註解,它也不會報錯, 可是這個被註解的方法將不會展現已配置的事務設置。 四、@Transactional 註解能夠被應用於接口定義和接口方法、類定義和類的 public 方法上。然而,請注意僅僅 @Transactional 註解的出現不足於開啓事務行爲,它僅僅 是一種元數據,可以被能夠識別 @Transactional 註解和上述的配置適當的具備事務行爲的beans所使用。 上面的例子中,其實正是 <tx:annotation-driven/>元素的出現 開啓 了事務行爲。 五、Spring團隊的建議是你在具體的類(或類的方法)上使用 @Transactional 註解,而不要使用在類所要實現的任何接口上。 你固然能夠在接口上使用 @Transactional 註解,可是這將只能當你設置了基於接口的代理時它才生效。由於註解是 不能繼承 的,這就意味着若是你正在使用基於類的代理時,那麼事務的設置將不能被基於類的代理所識別, 並且對象也將不會被事務代理所包裝(將被確認爲嚴重的)。因 此,請接受Spring團隊的建議而且在具體的類上使用 @Transactional 註解。
Hibernate 是當前最流行的O/R mapping框架,它出身於sf.NET,如今已經成爲Jboss的一部分。 Mybatis 是另一種優秀的O/R mapping框架。目前屬於apache的一個子項目。 MyBatis 參考資料官網:http://www.mybatis.org/core/zh/index.html Hibernate參考資料: http://docs.jboss.org/hibernate/core/3.6/reference/zh-CN/html_single/
Hibernate對數據庫結構提供了較爲完整的封裝,Hibernate的O/R Mapping實現了POJO 和數據庫表之間的映射,以及SQL 的自動生成和執行。程序員每每只需定義好了POJO 到數據庫表的映射關係,便可經過Hibernate 提供的方法完成持久層操 做。程序員甚至不須要對SQL 的熟練掌握, Hibernate/OJB 會根據制定的存儲邏輯,自動生成對應的SQL 並調用JDBC 接口加以執行。
iBATIS 的着力點,則在於POJO 與SQL之間的映射關係。而後經過映射配置文件,將SQL所需的參數,以及返回的結果字段映射到指定POJO。 相對Hibernate「O/R」而言,iBATIS 是一種「Sql Mapping」的ORM實現。
Hibernate的真正掌握要比Mybatis來得難些。Mybatis框架相對簡單很容易上手,但也相對簡陋些。我的以爲要用好Mybatis仍是首先要先理解好Hibernate。
Hibernate 與Mybatis都是流行的持久層開發框架,但Hibernate開發社區相對多熱鬧些,支持的工具也多,更新也快,當前最高版本4.1.8。而Mybatis相對平靜,工具較少,當前最高版本3.2。
Hibernate和MyBatis都有相應的代碼生成工具。能夠生成簡單基本的DAO層方法。 針對高級查詢,Mybatis須要手動編寫SQL語句,以及ResultMap。而Hibernate有良好的映射機制,開發者無需關心SQL的生成與結果映射,能夠更專一於業務流程。
制定合理的緩存策略; 儘可能使用延遲加載特性; 採用合理的Session管理機制; 使用批量抓取,設定合理的批處理參數(batch_size); 進行合理的O/R映射設計
MyBatis在Session方面和Hibernate的Session生命週期是一致的,一樣須要合理的Session管理機制。MyBatis一樣具備二級緩存機制。 MyBatis能夠進行詳細的SQL優化設計。
Hibernate的查詢會將表中的全部字段查詢出來,這一點會有性能消耗。Hibernate也能夠本身寫SQL來指定須要查詢的字段,但這樣就破壞了Hibernate開發的簡潔性。而Mybatis的SQL是手動編寫的,因此能夠按需求指定查詢的字段。 Hibernate HQL語句的調優須要將SQL打印出來,而Hibernate的SQL被不少人嫌棄由於太醜了。MyBatis的SQL是本身手動寫的因此調整方便。但Hibernate具備本身的日誌統計。Mybatis自己不帶日誌統計,使用Log4j進行日誌記錄。
Hibernate與具體數據庫的關聯只需在XML文件中配置便可,全部的HQL語句與具體使用的數據庫無關,移植性很好。MyBatis項目中全部的SQL語句都是依賴所用的數據庫的,因此不一樣數據庫類型的支持很差。
Hibernate 是完整的對象/關係映射解決方案,它提供了對象狀態管理(state management)的功能,使開發者再也不須要理會底層數據庫系統的細節。也就是說,相對於常見的 JDBC/SQL 持久層方案中須要管理 SQL 語句,Hibernate採用了更天然的面向對象的視角來持久化 Java 應用中的數據。 換句話說,使用 Hibernate 的開發者應該老是關注對象的狀態(state),沒必要考慮 SQL 語句的執行。這部分細節已經由 Hibernate 掌管穩當,只有開發者在進行系統性能調優的時候才須要進行了解。而MyBatis在這一塊沒有文檔說明,用戶須要對對象本身進行詳細的管理。
Hibernate對實體關聯對象的抓取有着良好的機制。對於每個關聯關係均可以詳細地設置是否延遲加載,而且提供關聯抓取、查詢抓取、子查詢抓取、批量抓取四種模式。 它是詳細配置和處理的。 而Mybatis的延遲加載是全局配置的。
Hibernate一級緩存是Session緩存,利用好一級緩存就須要對Session的生命週期進行管理好。建議在一個Action操做中使用一個Session。一級緩存須要對Session進行嚴格管理。 Hibernate二級緩存是SessionFactory級的緩存。 SessionFactory的緩存分爲內置緩存和外置緩存。內置緩存中存放的是SessionFactory對象的一些集合屬性包含的數據(映射元素據及預約SQL語句等),對於應用程序來講,它是隻讀的。 外置緩存中存放的是數據庫數據的副本,其做用和一級緩存相似.二級緩存除了之內存做爲存儲介質外,還能夠選用硬盤等外部存儲設備。二級緩存稱爲進程級緩存或SessionFactory級緩存, 它能夠被全部session共享,它的生命週期伴隨着SessionFactory的生命週期存在和消亡。
MyBatis 包含一個很是強大的查詢緩存特性,它能夠很是方便地配置和定製。MyBatis 3 中的緩存實現的不少改進都已經實現了,使得它更增強大並且易於配置。 默認狀況下是沒有開啓緩存的,除了局部的 session 緩存,能夠加強變現並且處理循環 依賴也是必須的。要開啓二級緩存,你須要在你的 SQL 映射文件中添加一行: <cache/> 字面上看就是這樣。這個簡單語句的效果以下: 映射語句文件中的全部 select 語句將會被緩存。 映射語句文件中的全部 insert,update 和 delete 語句會刷新緩存。 緩存會使用 Least Recently Used(LRU,最近最少使用的)算法來收回。 根據時間表(好比 no Flush Interval,沒有刷新間隔), 緩存不會以任什麼時候間順序 來刷新。 緩存會存儲列表集合或對象(不管查詢方法返回什麼)的 1024 個引用。 緩存會被視爲是 read/write(可讀/可寫)的緩存,意味着對象檢索不是共享的,而 且能夠安全地被調用者修改,而不干擾其餘調用者或線程所作的潛在修改。 全部的這些屬性均可以經過緩存元素的屬性來修改。 好比: <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/> 這個更高級的配置建立了一個 FIFO 緩存,並每隔 60 秒刷新,存數結果對象或列表的 512 個引用,並且返回的對象被認爲是隻讀的,所以在不一樣線程中的調用者之間修改它們會 致使衝突。可用的收回策略有, 默認的是 LRU: LRU – 最近最少使用的:移除最長時間不被使用的對象。 FIFO – 先進先出:按對象進入緩存的順序來移除它們。 SOFT – 軟引用:移除基於垃圾回收器狀態和軟引用規則的對象。 WEAK – 弱引用:更積極地移除基於垃圾收集器狀態和弱引用規則的對象。 flushInterval(刷新間隔)能夠被設置爲任意的正整數,並且它們表明一個合理的毫秒 形式的時間段。默認狀況是不設置,也就是沒有刷新間隔,緩存僅僅調用語句時刷新。 size(引用數目)能夠被設置爲任意正整數,要記住你緩存的對象數目和你運行環境的 可用內存資源數目。默認值是1024。 readOnly(只讀)屬性能夠被設置爲 true 或 false。只讀的緩存會給全部調用者返回緩 存對象的相同實例。所以這些對象不能被修改。這提供了很重要的性能優點。可讀寫的緩存 會返回緩存對象的拷貝(經過序列化) 。這會慢一些,可是安全,所以默認是 false。
Hibernate和Mybatis的二級緩存除了採用系統默認的緩存機制外,均可以經過實現你本身的緩存或爲其餘第三方緩存方案,建立適配器來徹底覆蓋緩存行爲。
Hibernate的二級緩存配置在SessionFactory生成的配置文件中進行詳細配置,而後再在具體的表-對象映射中配置是那種緩存。 MyBatis的二級緩存配置都是在每一個具體的表-對象映射中進行詳細配置,這樣針對不一樣的表能夠自定義不一樣的緩存機制。而且Mybatis能夠在命名空間中共享相同的緩存配置和實例,經過Cache-ref來實現。
由於Hibernate對查詢對象有着良好的管理機制,用戶無需關心SQL。因此在使用二級緩存時若是出現髒數據,系統會報出錯誤並提示。 而MyBatis在這一方面,使用二級緩存時須要特別當心。若是不能徹底肯定數據更新操做的波及範圍,避免Cache的盲目使用。不然,髒數據的出現會給系統的正常運行帶來很大的隱患。
Hibernate與MyBatis均可以是經過SessionFactoryBuider由XML配置文件生成SessionFactory,而後由SessionFactory 生成Session,最後由Session來開啓執行事務和SQL語句。其中SessionFactoryBuider,SessionFactory,Session的生命週期都是差很少的。 Hibernate和MyBatis都支持JDBC和JTA事務處理。
MyBatis能夠進行更爲細緻的SQL優化,能夠減小查詢字段。 MyBatis容易掌握,而Hibernate門檻較高。
Hibernate的DAO層開發比MyBatis簡單,Mybatis須要維護SQL和結果映射。 Hibernate對對象的維護和緩存要比MyBatis好,對增刪改查的對象的維護要方便。 Hibernate數據庫移植性很好,MyBatis的數據庫移植性很差,不一樣的數據庫須要寫不一樣SQL。 Hibernate有更好的二級緩存機制,能夠使用第三方緩存。MyBatis自己提供的緩存機制不佳。
Hibernate功能強大,數據庫無關性好,O/R映射能力強,若是你對Hibernate至關精通,並且對Hibernate進行了適當的封裝,那麼你的項目整個持久層代碼會至關簡單,須要寫的代碼不多,開發速度很快,很是爽。 Hibernate的缺點就是學習門檻不低,要精通門檻更高,並且怎麼設計O/R映射,在性能和對象模型之間如何權衡取得平衡,以及怎樣用好Hibernate方面須要你的經驗和能力都很強才行。 NYBATIS入門簡單,即學即用,提供了數據庫查詢的自動對象綁定功能,並且延續了很好的SQL使用經驗,對於沒有那麼高的對象模型要求的項目來講,至關完美。 NYBATIS的缺點就是框架仍是比較簡陋,功能尚有缺失,雖然簡化了數據綁定代碼,可是整個底層數據庫查詢實際仍是要本身寫的,工做量也比較大,並且不太容易適應快速數據庫修改
答:SessionFactory對應Hibernate的一個數據存儲的概念,它是線程安全的,能夠被多個線程併發訪問。SessionFactory通常只會在啓動的時候構建。 對於應用程序,最好將SessionFactory經過單例模式進行封裝以便於訪問。Session是一個輕量級非線程安全的對象(線程間不能共享session),它表示與數據庫進行交互的一個工做單元。 Session是由SessionFactory建立的,在任務完成以後它會被關閉。Session是持久層服務對外提供的主要接口。Session會延遲獲取數據庫鏈接(也就是在須要的時候纔會獲取)。 爲了不建立太多的session,能夠使用ThreadLocal將session和當前線程綁定在一塊兒,這樣可讓同一個線程得到的老是同一個session。Hibernate 3中SessionFactory的getCurrentSession()方法就能夠作到。
答:主要有如下三項區別: ① 若是沒有找到符合條件的記錄,get方法返回null,load方法拋出異常。 ② get方法直接返回實體類對象,load方法返回實體類對象的代理。 ③ 在Hibernate 3以前,get方法只在一級緩存中進行數據查找,若是沒有找到對應的數據則越過二級緩存,直接發出SQL語句完成數據讀取;load方法則能夠從二級緩存中獲取數據;從Hibernate 3開始,get方法再也不是對二級緩存只寫不讀,它也是能夠訪問二級緩存的。 說明:對於load()方法Hibernate認爲該數據在數據庫中必定存在能夠放心的使用代理來實現延遲加載,若是沒有數據就拋出異常,而經過get()方法獲取的數據能夠不存在。
答:Hibernate的對象有三種狀態:瞬時態(transient)、持久態(persistent)和遊離態(detached),瞬時態的實例能夠經過調用save()、persist()或者saveOrUpdate()方法變成持久態; 遊離態的實例能夠經過調用 update()、saveOrUpdate()、lock()或者replicate()變成持久態。save()和persist()將會引起SQL的INSERT語句,而update()或merge()會引起UPDATE語句。 save()和update()的區別在於一個是將瞬時態對象變成持久態,一個是將遊離態對象變爲持久態。merge()方法能夠完成save()和update()方法的功能,它的意圖是將新的狀態合併到已有的持久化對象上或建立新的持久化對象。 對於persist()方法,按照官方文檔的說明: ① persist()方法把一個瞬時態的實例持久化,可是並不保證標識符被馬上填入到持久化實例中,標識符的填入可能被推遲到flush的時間;② persist()方法保證當它在一個事務外部被調用的時候並不觸發一個INSERT語句,當須要封裝一個長會話流程的時候,persist()方法是頗有必要的; ③ save()方法不保證第②條,它要返回標識符,因此它會當即執行INSERT語句,無論是在事務內部仍是外部。至於lock()方法和update()方法的區別,update()方法是把一個已經更改過的脫管狀態的對象變成持久狀態;lock()方法是把一個沒有更改過的脫管狀態的對象變成持久狀態。
答:Session加載實體對象的步驟是: ① Session在調用數據庫查詢功能以前,首先會在一級緩存中經過實體類型和主鍵進行查找,若是一級緩存查找命中且數據狀態合法,則直接返回; ② 若是一級緩存沒有命中,接下來Session會在當前NonExists記錄(至關於一個查詢黑名單,若是出現重複的無效查詢能夠迅速作出判斷,從而提高性能)中進行查找,若是NonExists中存在一樣的查詢條件,則返回null; ③ 若是一級緩存查詢失敗則查詢二級緩存,若是二級緩存命中則直接返回; ④ 若是以前的查詢都未命中,則發出SQL語句,若是查詢未發現對應記錄則將這次查詢添加到Session的NonExists中加以記錄,並返回null; ⑤ 根據映射配置和SQL語句獲得ResultSet,並建立對應的實體對象; ⑥ 將對象歸入Session(一級緩存)的管理; ⑦ 若是有對應的攔截器,則執行攔截器的onLoad方法; ⑧ 若是開啓並設置了要使用二級緩存,則將數據對象歸入二級緩存; ⑨ 返回數據對象。
答: ① list()方法沒法利用一級緩存和二級緩存(對緩存只寫不讀),它只能在開啓查詢緩存的前提下使用查詢緩存;iterate()方法能夠充分利用緩存,若是目標數據只讀或者讀取頻繁,使用iterate()方法能夠減小性能開銷。 ② list()方法不會引發N+1查詢問題,而iterate()方法可能引發N+1查詢問題
答:延遲加載就是並非在讀取的時候就把數據加載進來,而是等到使用時再加載。Hibernate使用了虛擬代理機制實現延遲加載,咱們使用Session的load()方法加載數據或者一對多關聯映射在使用延遲加載的狀況下從一的一方加載多的一方, 獲得的都是虛擬代理,簡單的說返回給用戶的並非實體自己,而是實體對象的代理。代理對象在用戶調用getter方法時纔會去數據庫加載數據。但加載數據就須要數據庫鏈接。而當咱們把會話關閉時,數據庫鏈接就同時關閉了。
① 關閉延遲加載特性。這種方式操做起來比較簡單,由於Hibernate的延遲加載特性是能夠經過映射文件或者註解進行配置的,但這種解決方案存在明顯的缺陷。 首先,出現"no session or session was closed"一般說明系統中已經存在主外鍵關聯,若是去掉延遲加載的話,每次查詢的開銷都會變得很大。 ② 在session關閉以前先獲取須要查詢的數據,能夠使用工具方法Hibernate.isInitialized()判斷對象是否被加載,若是沒有被加載則能夠使用Hibernate.initialize()方法加載對象。 ③ 使用攔截器或過濾器延長Session的生命週期直到視圖得到數據。Spring整合Hibernate提供的OpenSessionInViewFilter和OpenSessionInViewInterceptor就是這種作法。
答:繼承關係的映射策略有三種: ① 每一個繼承結構一張表(table per class hierarchy),無論多少個子類都用一張表。 ② 每一個子類一張表(table per subclass),公共信息放一張表,特有信息放單獨的表。 ③ 每一個具體類一張表(table per concrete class),有多少個子類就有多少張表。 第一種方式屬於單表策略,其優勢在於查詢子類對象的時候無需錶鏈接,查詢速度快,適合多態查詢;缺點是可能致使表很大。後兩種方式屬於多表策略,其優勢在於數據存儲緊湊,其缺點是須要進行鏈接查詢,不適合多態查詢。
答:這個問題應當挑本身使用過的優化策略回答,經常使用的有: ① 制定合理的緩存策略(二級緩存、查詢緩存)。 ② 採用合理的Session管理機制。 ③ 儘可能使用延遲加載特性。 ④ 設定合理的批處理參數。 ⑤ 若是能夠,選用UUID做爲主鍵生成器。 ⑥ 若是能夠,選用基於版本號的樂觀鎖替代悲觀鎖。 ⑦ 在開發過程當中, 開啓hibernate.show_sql選項查看生成的SQL,從而瞭解底層的情況;開發完成後關閉此選項。 ⑧ 考慮數據庫自己的優化,合理的索引、恰當的數據分區策略等都會對持久層的性能帶來可觀的提高,但這些須要專業的DBA(數據庫管理員)提供支持。
答:Hibernate的Session提供了一級緩存的功能,默認老是有效的,當應用程序保存持久化實體、修改持久化實體時,Session並不會當即把這種改變提交到數據庫,而是緩存在當前的Session中,除非顯示調用了Session的flush()方法或經過close()方法關閉Session。經過一級緩存,能夠減小程序與數據庫的交互,從而提升數據庫訪問性能。 SessionFactory級別的二級緩存是全局性的,全部的Session能夠共享這個二級緩存。不過二級緩存默認是關閉的,須要顯示開啓並指定須要使用哪一種二級緩存實現類(能夠使用第三方提供的實現)。一旦開啓了二級緩存並設置了須要使用二級緩存的實體類,SessionFactory就會緩存訪問過的該實體類的每一個對象,除非緩存的數據超出了指定的緩存空間。 一級緩存和二級緩存都是對整個實體進行緩存,不會緩存普通屬性,若是但願對普通屬性進行緩存,能夠使用查詢緩存。查詢緩存是將HQL或SQL語句以及它們的查詢結果做爲鍵值對進行緩存,對於一樣的查詢能夠直接從緩存中獲取數據。查詢緩存默認也是關閉的,須要顯示開啓。
答:DetachedCriteria和Criteria的用法基本上是一致的,但Criteria是由Session的createCriteria()方法建立的,也就意味着離開建立它的Session,Criteria就沒法使用了。 DetachedCriteria不須要Session就能夠建立(使用DetachedCriteria.forClass()方法建立),因此一般也稱其爲離線的Criteria,在須要進行查詢操做的時候再和Session綁定(調用其getExecutableCriteria(Session)方法),這也就意味着一個DetachedCriteria能夠在須要的時候和不一樣的Session進行綁定。
答:@OneToMany用來配置一對多關聯映射,但一般狀況下,一對多關聯映射都由多的一方來維護關聯關係,例如學生和班級,應該在學生類中添加班級屬性來維持學生和班級的關聯關係(在數據庫中是由學生表中的外鍵班級編號來維護學生表和班級表的多對一關係) 若是要使用雙向關聯,在班級類中添加一個容器屬性來存放學生,並使用@OneToMany註解進行映射,此時mappedBy屬性就很是重要。若是使用XML進行配置,能夠用<set>標籤的inverse="true"設置來達到一樣的效果。
答:#將傳入的數據都當成一個字符串,會對傳入的數據自動加上引號;$將傳入的數據直接顯示生成在SQL中。注意:使用$佔位符可能會致使SQL注射攻擊,能用#的地方就不要使用$,寫order by子句的時候應該用$而不是#。
答:在大型項目中,可能存在大量的SQL語句,這時候爲每一個SQL語句起一個惟一的標識(ID)就變得並不容易了。爲了解決這個問題,在MyBatis中,能夠爲每一個映射文件起一個惟一的命名空間,這樣定義在這個映射文件中的每一個SQL語句就成了定義在這個命名空間中的一個ID。 只要咱們可以保證每一個命名空間中這個ID是惟一的,即便在不一樣映射文件中的語句ID相同,也不會再產生衝突了。
答:對於一些複雜的查詢,咱們可能會指定多個查詢條件,可是這些條件可能存在也可能不存在,例如在58同城上面找房子,咱們可能會指定面積、樓層和所在位置來查找房源,也可能會指定面積、價格、戶型和所在位置來查找房源,此時就須要根據用戶指定的條件動態生成SQL語句。 若是不使用持久層框架咱們可能須要本身拼裝SQL語句,還好MyBatis提供了動態SQL的功能來解決這個問題。MyBatis中用於實現動態SQL的元素主要有: - if - choose / when / otherwise - trim - where - set - foreach
答:IoC叫控制反轉,是Inversion of Control的縮寫,DI(Dependency Injection)叫依賴注入,是對IoC更簡單的詮釋。控制反轉是把傳統上由程序代碼直接操控的對象的調用權交給容器,經過容器來實現對象組件的裝配和管理。 所謂的"控制反轉"就是對組件對象控制權的轉移,從程序代碼自己轉移到了外部容器,由容器來建立對象並管理對象之間的依賴關係。IoC體現了好萊塢原則 - "Don’t call me, we will call you"。 依賴注入的基本原則是應用組件不該該負責查找資源或者其餘依賴的協做對象。配置對象的工做應該由容器負責,查找資源的邏輯應該從應用組件的代碼中抽取出來,交給容器來完成。 DI是對IoC更準確的描述,即組件之間的依賴關係由容器在運行期決定,形象的來講,即由容器動態的將某種依賴關係注入到組件之中。 依賴注入能夠經過setter方法注入(設值注入)、構造器注入和接口注入三種方式來實現,Spring支持setter注入和構造器注入,一般使用構造器注入來注入必須的依賴關係,對於可選的依賴關係,則setter注入是更好的選擇,setter注入須要類提供無參構造器或者無參的靜態工廠方法來建立對象。
答:在Spring的早期版本中,僅有兩個做用域:singleton和prototype,前者表示Bean以單例的方式存在;後者表示每次從容器中調用Bean時,都會返回一個新的實例,prototype一般翻譯爲原型。 補充:設計模式中的建立型模式中也有一個原型模式,原型模式也是一個經常使用的模式,例如作一個室內設計軟件,全部的素材都在工具箱中,而每次從工具箱中取出的都是素材對象的一個原型,能夠經過對象克隆來實現原型模式。 Spring 2.x中針對WebApplicationContext新增了3個做用域,分別是:request(每次HTTP請求都會建立一個新的Bean)、session(同一個HttpSession共享同一個Bean,不一樣的HttpSession使用不一樣的Bean)和globalSession(同一個全局Session共享一個Bean)。 說明:單例模式和原型模式都是重要的設計模式。通常狀況下,無狀態或狀態不可變的類適合使用單例模式。在傳統開發中,因爲DAO持有Connection這個非線程安全對象於是沒有使用單例模式;但在Spring環境下,全部DAO類對能夠採用單例模式,由於Spring利用AOP和java API中的ThreadLocal對非線程安全的對象進行了特殊處理。 ThreadLocal爲解決多線程程序的併發問題提供了一種新的思路。ThreadLocal,顧名思義是線程的一個本地化對象,當工做於多線程中的對象使用ThreadLocal維護變量時,ThreadLocal爲每一個使用該變量的線程分配一個獨立的變量副本,因此每個線程均可以獨立的改變本身的副本,而不影響其餘線程所對應的副本。從線程的角度看,這個變量就像是線程的本地變量。 ThreadLocal類很是簡單好用,只有四個方法,能用上的也就是下面三個方法: - void set(T value):設置當前線程的線程局部變量的值。 - T get():得到當前線程所對應的線程局部變量的值。 - void remove():刪除當前線程中線程局部變量的值。 ThreadLocal是如何作到爲每個線程維護一份獨立的變量副本的呢?在ThreadLocal類中有一個Map,鍵爲線程對象,值是其線程對應的變量的副本
答:AOP(Aspect-Oriented Programming)指一種程序設計範型,該範型以一種稱爲切面(aspect)的語言構造爲基礎,切面是一種新的模塊化機制,用來描述分散在對象、類或方法中的橫切關注點(crosscutting concern)。
答:"橫切關注"是會影響到整個應用程序的關注功能,它跟正常的業務邏輯是正交的,沒有必然的聯繫,可是幾乎全部的業務邏輯都會涉及到這些關注功能。一般,事務、日誌、安全性等關注就是應用中的橫切關注功能。
答: a. 鏈接點(Joinpoint):程序執行的某個特定位置(如:某個方法調用前、調用後,方法拋出異常後)。一個類或一段程序代碼擁有一些具備邊界性質的特定點,這些代碼中的特定點就是鏈接點。Spring僅支持方法的鏈接點。 b. 切點(Pointcut):若是鏈接點至關於數據中的記錄,那麼切點至關於查詢條件,一個切點能夠匹配多個鏈接點。Spring AOP的規則解析引擎負責解析切點所設定的查詢條件,找到對應的鏈接點。 c. 加強(Advice):加強是織入到目標類鏈接點上的一段程序代碼。Spring提供的加強接口都是帶方位名的,如:BeforeAdvice、AfterReturningAdvice、ThrowsAdvice等。不少資料上將加強譯爲「通知」,這明顯是個詞不達意的翻譯,讓不少程序員困惑了許久。 說明: Advice在國內的不少書面資料中都被翻譯成"通知",可是很顯然這個翻譯沒法表達其本質,有少許的讀物上將這個詞翻譯爲"加強",這個翻譯是對Advice較爲準確的詮釋,咱們經過AOP將橫切關注功能加到原有的業務邏輯上,這就是對原有業務邏輯的一種加強,這種加強能夠是前置加強、後置加強、返回後加強、拋異常時加強和包圍型加強。 d. 引介(Introduction):引介是一種特殊的加強,它爲類添加一些屬性和方法。這樣,即便一個業務類本來沒有實現某個接口,經過引介功能,能夠動態的未該業務類添加接口的實現邏輯,讓業務類成爲這個接口的實現類。 e. 織入(Weaving):織入是將加強添加到目標類具體鏈接點上的過程,AOP有三種織入方式:①編譯期織入:須要特殊的Java編譯期(例如AspectJ的ajc);②裝載期織入:要求使用特殊的類加載器,在裝載類的時候對類進行加強;③運行時織入:在運行時爲目標類生成代理實現加強。Spring採用了動態代理的方式實現了運行時織入,而AspectJ採用了編譯期織入和裝載期織入的方式。 f. 切面(Aspect):切面是由切點和加強(引介)組成的,它包括了對橫切關注功能的定義,也包括了對鏈接點的定義。 補充:代理模式是GoF提出的23種設計模式中最爲經典的模式之一,代理模式是對象的結構模式,它給某一個對象提供一個代理對象,並由代理對象控制對原對象的引用。 簡單的說,代理對象能夠完成比原對象更多的職責,當須要爲原對象添加橫切關注功能時,就能夠使用原對象的代理對象。 咱們在打開Office系列的Word文檔時,若是文檔中有插圖,當文檔剛加載時,文檔中的插圖都只是一個虛框佔位符,等用戶真正翻到某頁要查看該圖片時,纔會真正加載這張圖,這其實就是對代理模式的使用,代替真正圖片的虛框就是一個虛擬代理; Hibernate的load方法也是返回一個虛擬代理對象,等用戶真正須要訪問對象的屬性時,才向數據庫發出SQL語句得到真實對象
答:能夠從如下幾個方面做答: 非侵入式:支持基於POJO的編程模式,不強制性的要求實現Spring框架中的接口或繼承Spring框架中的類。 IoC容器:IoC容器幫助應用程序管理對象以及對象之間的依賴關係,對象之間的依賴關係若是發生了改變只須要修改配置文件而不是修改代碼,由於代碼的修改可能意味着項目的從新構建和完整的迴歸測試。 有了IoC容器,程序員不再須要本身編寫工廠、單例,這一點特別符合Spring的精神"不要重複的發明輪子"。 AOP(面向切面編程):將全部的橫切關注功能封裝到切面(aspect)中,經過配置的方式將橫切關注功能動態添加到目標代碼上,進一步實現了業務邏輯和系統服務之間的分離。另外一方面,有了AOP程序員能夠省去不少本身寫代理類的工做。 MVC:Spring的MVC框架是很是優秀的,從各個方面均可以甩Struts 2幾條街,爲Web表示層提供了更好的解決方案。 事務管理:Spring以寬廣的胸懷接納多種持久層技術,而且爲其提供了聲明式的事務管理,在不須要任何一行代碼的狀況下就可以完成事務管理。 其餘:選擇Spring框架的緣由還遠不止於此,Spring爲Java企業級開發提供了一站式選擇,你能夠在須要的時候使用它的部分和所有,更重要的是,你甚至能夠在感受不到Spring存在的狀況下,在你的項目中使用Spring提供的各類優秀的功能。
答: ① Spring IoC容器找到關於Bean的定義並實例化該Bean。 ② Spring IoC容器對Bean進行依賴注入。 ③ 若是Bean實現了BeanNameAware接口,則將該Bean的id傳給setBeanName方法。 ④ 若是Bean實現了BeanFactoryAware接口,則將BeanFactory對象傳給setBeanFactory方法。 ⑤ 若是Bean實現了BeanPostProcessor接口,則調用其postProcessBeforeInitialization方法。 ⑥ 若是Bean實現了InitializingBean接口,則調用其afterPropertySet方法。 ⑦ 若是有和Bean關聯的BeanPostProcessors對象,則這些對象的postProcessAfterInitialization方法被調用。 ⑧ 當銷燬Bean實例時,若是Bean實現了DisposableBean接口,則調用其destroy方法。
答: 分層:分層是處理任何複雜系統最多見的手段之一,將系統橫向切分紅若干個層面,每一個層面只承擔單一的職責,而後經過下層爲上層提供的基礎設施和服務以及上層對下層的調用來造成一個完整的複雜的系統。 計算機網絡的開放系統互聯參考模型(OSI/RM)和Internet的TCP/IP模型都是分層結構,大型網站的軟件系統也能夠使用分層的理念將其分爲持久層(提供數據存儲和訪問服務)、業務層(處理業務邏輯,系統中最核心的部分)和表示層(系統交互、視圖展現)。 須要指出的是:(1)分層是邏輯上的劃分,在物理上能夠位於同一設備上也能夠在不一樣的設備上部署不一樣的功能模塊,這樣能夠使用更多的計算資源來應對用戶的併發訪問;(2)層與層之間應當有清晰的邊界,這樣分層纔有意義,才更利於軟件的開發和維護。 分割:分割是對軟件的縱向切分。咱們能夠將大型網站的不一樣功能和服務分割開,造成高內聚低耦合的功能模塊(單元) 。在設計初期能夠作一個粗粒度的分割,將網站分割爲若干個功能模塊,後期還能夠進一步對每一個模塊進行細粒度的分割,這樣一方面有助於軟件的開發和維護,另外一方面有助於分佈式的部署,提供網站的併發處理能力和功能的擴展。 分佈式:除了上面提到的內容,網站的靜態資源(JavaScript、CSS、圖片等)也能夠採用獨立分佈式部署並採用獨立的域名,這樣能夠減輕應用服務器的負載壓力,也使得瀏覽器對資源的加載更快。 數據的存取也應該是分佈式的,傳統的商業級關係型數據庫產品基本上都支持分佈式部署,而新生的NoSQL產品幾乎都是分佈式的。固然,網站後臺的業務處理也要使用分佈式技術,例如查詢索引的構建、數據分析等,這些業務計算規模龐大,能夠使用Hadoop以及MapReduce分佈式計算框架來處理。 集羣:集羣使得有更多的服務器提供相同的服務,能夠更好的提供對併發的支持。 緩存:所謂緩存就是用空間換取時間的技術,將數據儘量放在距離計算最近的位置。使用緩存是網站優化的第必定律。咱們一般說的CDN、反向代理、熱點數據都是對緩存技術的使用。 異步:異步是實現軟件實體之間解耦合的又一重要手段。異步架構是典型的生產者消費者模式,兩者之間沒有直接的調用關係,只要保持數據結構不變,彼此功能實現能夠隨意變化而不互相影響,這對網站的擴展很是有利。 使用異步處理還能夠提升系統可用性,加快網站的響應速度(用Ajax加載數據就是一種異步技術),同時還能夠起到削峯做用(應對瞬時高併發)。";能推遲處理的都要推遲處理"是網站優化的第二定律,而異步是踐行網站優化第二定律的重要手段。 冗餘:各類服務器都要提供相應的冗餘服務器以便在某臺或某些服務器宕機時還能保證網站能夠正常工做,同時也提供了災難恢復的可能性。冗餘是網站高可用性的重要保證。
答: ① 瀏覽器訪問優化: - 減小HTTP請求數量:合併CSS、合併javascript、合併圖片(CSS Sprite) - 使用瀏覽器緩存:經過設置HTTP響應頭中的Cache-Control和Expires屬性,將CSS、JavaScript、圖片等在瀏覽器中緩存,當這些靜態資源須要更新時,能夠更新HTML文件中的引用來讓瀏覽器從新請求新的資源 - 啓用壓縮 - CSS前置,JavaScript後置 - 減小Cookie傳輸 ② CDN加速:CDN(Content Distribute Network)的本質仍然是緩存,將數據緩存在離用戶最近的地方,CDN一般部署在網絡運營商的機房,不只能夠提高響應速度,還能夠減小應用服務器的壓力。固然,CDN緩存的一般都是靜態資源。 ③ 反向代理:反向代理至關於應用服務器的一個門面,能夠保護網站的安全性,也能夠實現負載均衡的功能,固然最重要的是它緩存了用戶訪問的熱點資源,能夠直接從反向代理將某些內容返回給用戶瀏覽器。
答: ① 分佈式緩存:緩存的本質就是內存中的哈希表,若是設計一個優質的哈希函數,那麼理論上哈希表讀寫的漸近時間複雜度爲O(1)。緩存主要用來存放那些讀寫比很高、變化不多的數據,這樣應用程序讀取數據時先到緩存中讀取,若是沒有或者數據已經失效再去訪問數據庫或文件系統,並根據擬定的規則將數據寫入緩存。 對網站數據的訪問也符合二八定律(Pareto分佈,冪律分佈),即80%的訪問都集中在20%的數據上,若是可以將這20%的數據緩存起來,那麼系統的性能將獲得顯著的改善。固然,使用緩存須要解決如下幾個問題: - 頻繁修改的數據; - 數據不一致與髒讀; - 緩存雪崩(能夠採用分佈式緩存服務器集羣加以解決,memcached是普遍採用的解決方案); - 緩存預熱; - 緩存穿透(惡意持續請求不存在的數據)。 ② 異步操做:能夠使用消息隊列將調用異步化,經過異步處理將短期高併發產生的事件消息存儲在消息隊列中,從而起到削峯做用。電商網站在進行促銷活動時,能夠將用戶的訂單請求存入消息隊列,這樣能夠抵禦大量的併發訂單請求對系統和數據庫的衝擊。目前,絕大多數的電商網站即使不進行促銷活動,訂單系統都採用了消息隊列來處理。 ③ 使用集羣。 ④ 代碼優化: - 多線程:基於Java的Web開發基本上都經過多線程的方式響應用戶的併發請求,使用多線程技術在編程上要解決線程安全問題,主要能夠考慮如下幾個方面: A. 將對象設計爲無狀態對象(這和麪向對象的編程觀點是矛盾的,在面向對象的世界中被視爲不良設計),這樣就不會存在併發訪問時對象狀態不一致的問題。 B. 在方法內部建立對象,這樣對象由進入方法的線程建立,不會出現多個線程訪問同一對象的問題。使用ThreadLocal將對象與線程綁定也是很好的作法,這一點在前面已經探討過了。C. 對資源進行併發訪問時應當使用合理的鎖機制。 - 非阻塞I/O: 使用單線程和非阻塞I/O是目前公認的比多線程的方式更能充分發揮服務器性能的應用模式,基於Node.js構建的服務器就採用了這樣的方式。Java在JDK 1.4中就引入了NIO(Non-blocking I/O),在Servlet 3規範中又引入了異步Servlet的概念,這些都爲在服務器端採用非阻塞I/O提供了必要的基礎。 - 資源複用:資源複用主要有兩種方式,一是單例,二是對象池,咱們使用的數據庫鏈接池、線程池都是對象池化技術,這是典型的用空間換取時間的策略,另外一方面也實現對資源的複用,從而避免了沒必要要的建立和釋放資源所帶來的開銷。javascript
答: XSS(Cross Site Script,跨站腳本攻擊)是向網頁中注入惡意腳本在用戶瀏覽網頁時在用戶瀏覽器中執行惡意腳本的攻擊方式。 跨站腳本攻擊分有兩種形式:反射型攻擊(誘使用戶點擊一個嵌入惡意腳本的連接以達到攻擊的目標,目前有不少攻擊者利用論壇、微博發佈含有惡意腳本的URL就屬於這種方式)和持久型攻擊(將惡意腳本提交到被攻擊網站的數據庫中,用戶瀏覽網頁時,惡意腳本從數據庫中被加載到頁面執行, QQ郵箱的早期版本就曾經被利用做爲持久型跨站腳本攻擊的平臺)。XSS雖然不是什麼新鮮玩意,可是攻擊的手法卻不斷翻新,防範XSS主要有兩方面:消毒(對危險字符進行轉義)和HttpOnly(防範XSS攻擊者竊取Cookie數據)。 SQL注入攻擊是注入攻擊最多見的形式(此外還有OS注入攻擊(Struts 2的高危漏洞就是經過OGNL實施OS注入攻擊致使的)),當服務器使用請求參數構造SQL語句時,惡意的SQL被嵌入到SQL中交給數據庫執行。 SQL注入攻擊須要攻擊者對數據庫結構有所瞭解才能進行,攻擊者想要得到表結構有多種方式: (1)若是使用開源系統搭建網站,數據庫結構也是公開的(目前有不少現成的系統能夠直接搭建論壇,電商網站,雖然方便快捷可是風險是必需要認真評估的); (2)錯誤回顯(若是將服務器的錯誤信息直接顯示在頁面上,攻擊者能夠經過非法參數引起頁面錯誤從而經過錯誤信息瞭解數據庫結構,Web應用應當設置友好的錯誤頁,一方面符合最小驚訝原則,一方面屏蔽掉可能給系統帶來危險的錯誤回顯信息); (3)盲注。防範SQL注入攻擊也能夠採用消毒的方式,經過正則表達式對請求參數進行驗證,此外,參數綁定也是很好的手段,這樣惡意的SQL會被當作SQL的參數而不是命令被執行,JDBC中的PreparedStatement就是支持參數綁定的語句對象,從性能和安全性上都明顯優於Statement。 CSRF攻擊(Cross Site Request Forgery,跨站請求僞造)是攻擊者經過跨站請求,以合法的用戶身份進行非法操做(如轉帳或發帖等)。CSRF的原理是利用瀏覽器的Cookie或服務器的Session,盜取用戶身份,其原理以下圖所示。 防範CSRF的主要手段是識別請求者的身份,主要有如下幾種方式: (1)在表單中添加令牌(token); (2)驗證碼; (3)檢查請求頭中的Referer(前面提到防圖片盜連接也是用的這種方式)。 令牌和驗證都具備一次消費性的特徵,所以在原理上一致的,可是驗證碼是一種糟糕的用戶體驗,不是必要的狀況下不要輕易使用驗證碼,目前不少網站的作法是若是在短期內屢次提交一個表單未得到成功後纔要求提供驗證碼,這樣會得到較好的用戶體驗
答:領域模型是領域內的概念類或現實世界中對象的可視化表示,又稱爲概念模型或分析對象模型,它專一於分析問題領域自己,發掘重要的業務領域概念,並創建業務領域概念之間的關係。貧血模型是指使用的領域對象中只有setter和getter方法(POJO),全部的業務邏輯都不包含在領域對象中而是放在業務邏輯層。 有人將咱們這裏說的貧血模型進一步劃分紅失血模型(領域對象徹底沒有業務邏輯)和貧血模型(領域對象有少許的業務邏輯),咱們這裏就不對此加以區分了。 充血模型將大多數業務邏輯和持久化放在領域對象中,業務邏輯(業務門面)只是完成對業務邏輯的封裝、事務和權限等的處理。