//方法1:順序(索引)傳參法 public User selectUser(String name, int deptId); <select id="selectUser" resultMap="UserResultMap"> select * from user where user_name = #{0} and dept_id = #{1} </select> // #{}裏面的數字表明你傳入參數的順序。 // 這種方法不建議使用,sql層表達不直觀,且一旦順序調整容易出錯。 // 方法2:@Param註解傳參法 public User selectUser(@Param("userName") String name, int @Param("deptId") deptId); <select id="selectUser" resultMap="UserResultMap"> select * from user where user_name = #{userName} and dept_id = #{deptId} </select> // #{}裏面的名稱對應的是註解@Param括號裏面修飾的名稱。 // 這種方法在參數很少的狀況仍是比較直觀的,推薦使用。 // 方法3:Map傳參法 public User selectUser(Map<String, Object> params); <select id="selectUser" parameterType="java.util.Map" resultMap="UserResultMap"> select * from user where user_name = #{userName} and dept_id = #{deptId} </select> // #{}裏面的名稱對應的是Map裏面的key名稱。 // 這種方法適合傳遞多個參數,且參數易變能靈活傳遞的狀況。 // 方法4:Java Bean傳參法 public User selectUser(Map<String, Object> params); <select id="selectUser" parameterType="com.test.User" resultMap="UserResultMap"> select * from user where user_name = #{userName} and dept_id = #{deptId} </select> // #{}裏面的名稱對應的是User類裏面的成員屬性。 //方法5:集合遍歷 數組,map,list(多個的話須要@Param註解傳參法) <select id="selectPostIn" resultType="domain.blog.Post"> SELECT * FROM POST P WHERE ID in <foreach item="item" index="index" collection="list" open="(" separator="," close=")"> #{item} </foreach> </select>
並行事務的四大問題: 1.更新丟失:和別的事務讀到相同的東西,各自寫,本身的寫被覆蓋了。(誰寫的快誰的更新就丟失了) 2.髒讀:讀到別的事務未提交的數據。(萬一回滾,數據就是髒的無效的了) 3.不可重複讀:兩次讀之間有別的事務修改。 4.幻讀:兩次讀之間有別的事務增刪。幻行,顧名思義就是忽然蹦出來的行數據。指的就是某個事務在讀取某個範圍的數據,可是另外一個事務又向這個範圍的數據去插入數據,致使屢次讀取的時候,數據的行數不一致。 對應隔離級別 1.READ UNCOMMITTED:讀未提交,不處理,會出現髒讀,不可重複讀,幻讀。 2.READ COMMITTED:讀已提交,只讀提交的數據,無髒讀,但這種級別會出現讀取舊數據的現象,不可重複讀,大多數數據庫系統的默認隔離級別。 3.REPEATABLE READ:可重複讀,加行鎖,兩次讀之間不會有修改,無髒讀無重複讀;保證了每行的記錄的結果是一致的。可是沒法解決幻讀 4.SERIALIZABLE: 串行化,加表鎖,強制事務串行執行,無全部問題,不會出現髒讀,不可重複讀,幻讀。因爲他大量加上鎖,致使大量的請求超時,所以性能會比較低下,在須要數據一致性且併發量不須要那麼大的時候纔可能考慮這個隔離級別。
隔離級別原理
隔離級別原理 READ_UNCOMMITED 的原理: 1,事務對當前被讀取的數據不加鎖; 2,事務在更新某數據的瞬間(就是發生更新的瞬間),必須先對其加 行級共享鎖,直到事務結束才釋放。 // 1,事務1讀取某行記錄時,事務2也能對這行記錄進行讀取、更新;當事務2對該記錄進行更新時,事務1再次讀取該記錄,能讀到事務2對該記錄的修改版本,即便該修改還沒有被提交。 // 2,事務1更新某行記錄時,事務2不能對這行記錄作更新,直到事務1結束。 READ_COMMITED 的原理: 1,事務對當前被讀取的數據加 行級共享鎖(當讀到時才加鎖),一旦讀完該行,當即釋放該行級共享鎖; 2,事務在更新某數據的瞬間(就是發生更新的瞬間),必須先對其加 行級排他鎖,直到事務結束才釋放。 // 1,事務1讀取某行記錄時,事務2也能對這行記錄進行讀取、更新;當事務2對該記錄進行更新時,事務1再次讀取該記錄,讀到的只能是事務2對其更新前的版本,要不就是事務2提交後的版本。 // 2,事務1更新某行記錄時,事務2不能對這行記錄作更新,直到事務1結束。 REPEATABLE READ 的原理: 1,事務在讀取某數據的瞬間(就是開始讀取的瞬間),必須先對其加 行級共享鎖,直到事務結束才釋放; 2,事務在更新某數據的瞬間(就是發生更新的瞬間),必須先對其加 行級排他鎖,直到事務結束才釋放。 // 1,事務1讀取某行記錄時,事務2也能對這行記錄進行讀取、更新;當事務2對該記錄進行更新時,事務1再次讀取該記錄,讀到的仍然是第一次讀取的那個版本。 // 2,事務1更新某行記錄時,事務2不能對這行記錄作更新,直到事務1結束。 SERIALIZABLE 的原理: 1,事務在讀取數據時,必須先對其加 表級共享鎖 ,直到事務結束才釋放; 2,事務在更新數據時,必須先對其加 表級排他鎖 ,直到事務結束才釋放。 // 1,事務1正在讀取A表中的記錄時,則事務2也能讀取A表,但不能對A表作更新、新增、刪除,直到事務1結束。 // 2,事務1正在更新A表中的記錄時,則事務2不能讀取A表的任意記錄,更不可能對A表作更新、新增、刪除,直到事務1結束。
數據庫引擎html
MySQL的鎖機制比較簡單,其最顯著的特色是不一樣的存儲引擎支持不一樣的鎖機制。 全部的鎖都是綁定在數據庫的索引機制上的! 1.InnoDB(MySQL默認存儲引擎 從版本5.5.5開始) 支持事務,行級鎖,以及外鍵,擁有高併發處理能力。既支持行級鎖(row-level locking),也支持表級鎖,但默認狀況下是採用行級鎖。 可是在建立索引和加載數據時,比MyISAM慢。默認的隔離級別是Repeatable Read(可重複讀) 2.MyISAM 採用的是表級鎖(table-level locking),不支持事務和行級鎖。因此速度很快,性能優秀。能夠對整張表加鎖,支持併發插入,支持全文索引。 3.MEMORY 支持Hash索引,內存表,Memory引擎將數據存儲在內存中,表結構不是存儲在內存中的,查詢時不須要執行磁盤I/O操做,因此要比MyISAM和InnoDB快不少倍,可是數據庫斷電或是重啓後,表中的數據將會丟失,表結構不會丟失。
數據庫鎖java
// 數據庫鎖出現的目的:處理併發問題 鎖分類 從數據庫系統角度分爲三種:排他鎖、共享鎖、更新鎖。 從程序員角度分爲兩種:一種是悲觀鎖,一種樂觀鎖。 悲觀鎖按使用性質劃分:排他鎖、共享鎖、更新鎖。 悲觀鎖按做用範圍劃分:行鎖、表鎖。 樂觀鎖實現方式:版本號,時間戳。 數據庫規定同一資源上不能同時共存共享鎖和排他鎖。 1、悲觀鎖(Pessimistic Lock) 顧名思義,很悲觀,每次去拿數據的時候都認爲別人會修改,因此每次在拿數據的時候都會上鎖,這樣別人拿這個數據就會block(阻塞),直到它拿鎖。傳統的關係數據庫裏用到了不少這種鎖機制,好比行鎖、表鎖、讀鎖、寫鎖等,都是在操做以前先上鎖。 1. 共享鎖(Share Lock) S鎖,也叫讀鎖,用於全部的只讀數據操做。共享鎖是非獨佔的,容許多個併發事務讀取其鎖定的資源。 性質 1. 多個事務可封鎖同一個共享頁; 2. 任何事務都不能修改該頁; 3. 一般是該頁被讀取完畢,S鎖當即被釋放。 // 在SQL Server中,默認狀況下,數據被讀取後,當即釋放共享鎖。 // 例如,執行查詢語句「SELECT * FROM my_table」時,首先鎖定第一頁,讀取以後,釋放對第一頁的鎖定,而後鎖定第二頁。這樣,就容許在讀操做過程當中,修改未被鎖定的第一頁。 // 例如,語句「SELECT * FROM my_table HOLDLOCK」就要求在整個查詢過程當中,保持對錶的鎖定,直到查詢完成才釋放鎖定。 2. 排他鎖(Exclusive Lock) X鎖,也叫寫鎖,表示對數據進行寫操做。若是一個事務對對象加了排他鎖,其餘事務就不能再給它加任何鎖了。 性質 1. 僅容許一個事務封鎖此頁; 2. 其餘任何事務必須等到X鎖被釋放才能對該頁進行訪問; 3. X鎖一直到事務結束才能被釋放。 // 產生排他鎖的SQL語句以下:select * from ad_plan for update; 3. 更新鎖 U鎖,在修改操做的初始化階段用來鎖定可能要被修改的資源,這樣能夠避免使用共享鎖形成的死鎖現象。 // 由於當使用共享鎖時,修改數據的操做分爲兩步: 1. 首先得到一個共享鎖,讀取數據, 2. 而後將共享鎖升級爲排他鎖,再執行修改操做。 這樣若是有兩個或多個事務同時對一個事務申請了共享鎖,在修改數據時,這些事務都要將共享鎖升級爲排他鎖。這時,這些事務都不會釋放共享鎖,而是一直等待對方釋放,這樣就形成了死鎖。 // 若是一個數據在修改前直接申請更新鎖,在數據修改時再升級爲排他鎖,就能夠避免死鎖。 性質 1. 用來預約要對此頁施加X鎖,它容許其餘事務讀,但不容許再施加U鎖或X鎖; 2. 當被讀取的頁要被更新時,則升級爲X鎖; 3. U鎖一直到事務結束時才能被釋放。 4. 行鎖 鎖的做用範圍是行級別。
行級鎖:開銷大,加鎖慢;會出現死鎖;鎖定粒度最小,發生鎖衝突的機率最低,併發度也最高。
5. 表鎖
鎖的做用範圍是整張表。
表級鎖:開銷小,加鎖快;不會出現死鎖;鎖定粒度大,發生鎖衝突的機率最高,併發度最低。
6. 頁面鎖
頁面鎖:開銷和加鎖時間界於表鎖和行鎖之間;會出現死鎖;鎖定粒度界於表鎖和行鎖之間,併發度通常。
// 數據庫可以肯定那些行須要鎖的狀況下使用行鎖,若是不知道會影響哪些行的時候就會使用表鎖。 // 舉個例子,一個用戶表user,有主鍵id和用戶生日birthday。 // 當你使用update … where id=?這樣的語句時,數據庫明確知道會影響哪一行,它就會使用行鎖; // 當你使用update … where birthday=?這樣的的語句時,由於事先不知道會影響哪些行就可能會使用表鎖。 2、樂觀鎖(Optimistic Lock) 顧名思義,就是很樂觀,每次去拿數據的時候都認爲別人不會修改,因此,不會上鎖。可是在更新的時候會判斷一下在此期間別人有沒有更新這個數據,可使用版本號等機制。 1. 版本號(version) 版本號(記爲version):就是給數據增長一個版本標識,在數據庫上就是表中增長一個version字段,每次更新把這個字段加1,讀取數據的時候把version讀出來,更新的時候比較version,若是仍是開始讀取的version就能夠更新了,若是如今的version比老的version大,說明有其餘事務更新了該數據,並增長了版本號,這時候獲得一個沒法更新的通知,用戶自行根據這個通知來決定怎麼處理,好比從新開始一遍。這裏的關鍵是判斷version和更新兩個動做須要做爲一個原子單元執行,不然在你判斷能夠更新之後正式更新以前有別的事務修改了version,這個時候你再去更新就可能會覆蓋前一個事務作的更新,形成第二類丟失更新,因此你可使用update … where … and version=」old version」這樣的語句,根據返回結果是0仍是非0來獲得通知,若是是0說明更新沒有成功,由於version被改了,若是返回非0說明更新成功。 2. 時間戳(使用數據庫服務器的時間戳) 時間戳(timestamp):和版本號基本同樣,只是經過時間戳來判斷而已,注意時間戳要使用數據庫服務器的時間戳不能是業務系統的時間。 3. 待更新字段 待更新字段:和版本號方式類似,只是不增長額外字段,直接使用有效數據字段作版本控制信息,由於有時候咱們可能沒法改變舊系統的數據庫表結構。假設有個待更新字段叫count,先去讀取這個count,更新的時候去比較數據庫中count的值是否是我指望的值(即開始讀的值),若是是就把我修改的count的值更新到該字段,不然更新失敗。java的基本類型的原子類型對象如AtomicInteger就是這種思想。 4. 全部字段 全部字段:和待更新字段相似,只是使用全部字段作版本控制信息,只有全部字段都沒變化纔會執行更新。
spring 7大事務傳播行爲類型: propagation_required:當前沒有事務,新建一個事務,若是存在事務,則加入到該事務。 propagation_supports:支持當前事務,若是當前沒有事務,就以非事務方式執行。 propagation_mandatory:使用當前的事務,若是當前沒有事務,則拋出異常。 propagation_requires_new:新建事務,若是當前沒有事務,把當前事務掛起。 propagation_not_supported:以非事務方式操做,若是當前存在事務,就把當前事務掛起。 propagation_never:以非事務方式執行,若是當前存在事務,則拋出異常。 propagation_nested:若是當前存在事務,則在嵌套事務內執行。若是當前沒有事務,則執行與propagation_required相似的操做。要求:底層數據源必須基於JDBC3.0,而且實現者須要支持保存點事務機制 事務熟悉: readOnly:表示對應的事務應該被最優化爲只讀事務。 Timeout:指定事務超時時間,單位:秒。xml配置寫法:Timeout_11 ---設置超時時間11秒 spring 5大事務隔離級別: 1、isolation_default:這是一個platfromTransactionManager默認隔離級別,使用數據庫默認的事務隔離級別。 2、isolation_read_uncommitted:最低事務隔離級別,容許另外一個事務讀取這個事務未提交的數據 3、isolation_read_committed:保證一個事務修改數據並提交後,另一個事務才能讀取。 4、isolation_repeatable_read:這種事務隔離級別能夠防止髒讀,不可重複讀,但可能出現幻讀 5、isolation_serializable:這是花費最高代價可是最可靠的事務隔離級別。事務被處理爲順序執行。 3大問題: 髒讀:當一個事務正再訪問數據,而且對數據進行修改,而這種修改還沒提交到數據庫,這是另一個事務也能夠訪問這個數據,並進行使用,由於前一個事務還未提交到數據庫,那麼另一個事務讀到這個數據是髒數據。 不可重複讀:在一個事務內,屢次讀同一數據,在這個事務尚未結束時,另一個事務也訪問該數據,那麼在第一事務中的兩次讀取數據之間,因爲第二個事務的修改,那麼第一個事務兩次讀到的數據可能不同。 幻讀:當事務不是獨立執行發生,例:當第一個事務對錶中數據進行修改,而這種修改是針對全表數據行,同事第二個事務向表中插入一條新數據,那麼在操做發生後,第一個事務用戶發現表中還有沒有修改的數據行。
配置方式 mysql
Spring事務的配置五種不一樣的方式: 第一種方式:每一個Bean都有一個代理 第二種方式:全部Bean共享一個代理基類 第三種方式:使用攔截器 第四種方式:使用tx標籤配置的攔截器 第五種方式:全註解,DAO上需加上@Transactional註解 第一種方式:每一個Bean都有一個代理 <bean id="userDao" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <!-- 配置事務管理器 --> <property name="transactionManager" ref="transactionManager" /> <property name="target" ref="userDaoTarget" /> <property name="proxyInterfaces" value="com.bluesky.spring.dao.GeneratorDao" /> <!-- 配置事務屬性 --> <property name="transactionAttributes"> <props> <prop key="*">PROPAGATION_REQUIRED</prop> </props> </property> </bean>
第二種方式:全部Bean共享一個代理基類 <bean id="transactionBase" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" lazy-init="true" abstract="true"> <!-- 配置事務管理器 --> <property name="transactionManager" ref="transactionManager" /> <!-- 配置事務屬性 --> <property name="transactionAttributes"> <props> <prop key="*">PROPAGATION_REQUIRED</prop> </props> </property> </bean> <!-- 配置DAO --> <bean id="userDaoTarget" class="com.bluesky.spring.dao.UserDaoImpl"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <bean id="userDao" parent="transactionBase" > <property name="target" ref="userDaoTarget" /> </bean>
第三種方式:使用攔截器 <bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor"> <property name="transactionManager" ref="transactionManager" /> <!-- 配置事務屬性 --> <property name="transactionAttributes"> <props> <prop key="*">PROPAGATION_REQUIRED</prop> </props> </property> </bean> <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> <property name="beanNames"> <list> <value>*Dao</value> </list> </property> <property name="interceptorNames"> <list> <value>transactionInterceptor</value> </list> </property> </bean>
第四種方式:使用tx標籤配置的攔截器 <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="*" propagation="REQUIRED" /> </tx:attributes> </tx:advice> <aop:config> <aop:pointcut id="interceptorPointCuts" expression="execution(* com.bluesky.spring.dao.*.*(..))" /> <aop:advisor advice-ref="txAdvice" pointcut-ref="interceptorPointCuts" /> </aop:config>
第五種方式:全註解,DAO上需加上@Transactional註解 <tx:annotation-driven transaction-manager="transactionManager"/>
補充 linux
spring 什麼狀況下進行事務回滾?
Spring、EJB的聲明式事務默認狀況下都是在拋出unchecked exception後纔會觸發事務的回滾
unchecked異常,即運行時異常runntimeException 回滾事務;
checked異常,即Exception可try{}捕獲的不會回滾.固然也可配置spring參數讓其回滾.
通常不須要在業務方法中catch異常,若是非要catch,在作完你想作的工做後(好比關閉文件等)必定要拋出runtime exception,不然spring會將你的操做commit,這樣就會產生髒數據.因此你的catch代碼是多此一舉。
索引的優勢
1.經過建立惟一索引,能夠保證數據庫每一行數據的惟一性
2.能夠大大提升查詢速度
3.能夠加速表與表的鏈接
4.能夠顯著的減小查詢中分組和排序的時間。
索引的缺點
1.建立索引和維護索引須要時間,並且數據量越大時間越長
2.建立索引須要佔據磁盤的空間,若是有大量的索引,可能比數據文件更快達到最大文件尺寸
3.當對錶中的數據進行增長,修改,刪除的時候,索引也要同時進行維護,下降了數據的維護速度
創建索引的原則
一、對於查詢頻率高(用於查詢條件)的字段建立索引;
二、對排序、分組、聯合查詢頻率高的字段建立索引,提升搜索速度;
三、索引的數目不宜太多緣由:a、每建立一個索引都會佔用相應的物理空間;b、過多的索引會致使insert、update、delete語句的執行效率下降;
四、若在實際中,須要將多個列設置索引時,能夠採用多列索引,在常常存取的多個列上創建複合索引,但要注意複合索引的創建順序要按照使用的頻度來肯定;
五、選擇惟一性索引,數據自己具有惟一性的時候,創建惟一性索引,能夠保證定義的列的數據完整性,以提升查詢熟度
六、儘可能使用數據量少的索引。若是索引的值很長,那麼查詢的速度會受到影響。
七、儘可能使用前綴來索引。若是索引字段的值很長,最好使用值的前綴來索引。例如,TEXT和BLOG類型的字段,進行全文檢索會很浪費時間。若是隻檢索字段的前面的若干個字符,這樣能夠提升檢索速度。
八、刪除再也不使用或者不多使用的索引.
9.數據量小的表最好不要創建索引;包含大量的列而且不須要搜索非空值的時候能夠考慮不建索引.數據量超過300w的表應該有索引
10. 常更新的表越少越好,對於常常存取的列避免創建索引;
11. 在不一樣值較少的字段上沒必要要創建索引,如性別字段;
12. 用於聯接的列(主健/外健)上創建索引;
13. 缺省狀況下創建的是非簇集索引,但在如下狀況下最好考慮簇集索引,如:含有有限數目(不是不多)惟一的列;進行大範圍的查詢;充分的利用索引能夠減小表掃描I/0的次數,有效的避免對整表的搜索。固然合理的索引要創建在對各類查詢的分析和預測中,也取決於DBA的所設計的數據庫結構。
14. 複合索引的創建須要進行仔細分析;儘可能考慮用單字段索引代替:
A、正確選擇複合索引中的主列字段,通常是選擇性較好的字段;
B、複合索引的幾個字段是否常常同時以AND方式出如今Where子句中?單字段查詢是否極少甚至沒有?若是是,則能夠創建複合索引;不然考慮單字段索引;
C、若是複合索引中包含的字段常常單獨出如今Where子句中,則分解爲多個單字段索引;
D、若是複合索引所包含的字段超過3個,那麼仔細考慮其必要性,考慮減小複合的字段;
E、若是既有單字段索引,又有這幾個字段上的複合索引,通常能夠刪除複合索引;
B樹 每一個節點都存儲key和data,全部節點組成這棵樹,而且葉子節點指針爲null。 B樹優勢在於,因爲B樹的每個節點都包含key和value,所以常常訪問的元素可能離根節點更近,所以訪問也更迅速。 B+樹 只有葉子節點存儲data,葉子節點包含了這棵樹的全部鍵值,葉子節點不存儲指針。全部非終端節點當作是索引,節點中僅含有其子樹根節點最大(或最小)的關鍵字,不包含查找的有效信息。B+樹中全部葉子節點都是經過指針鏈接在一塊兒。 B+ 樹的優勢在於: 因爲B+樹在內部節點上不包含數據信息,所以在內存頁中可以存放更多的key。 數據存放的更加緊密,具備更好的空間局部性。所以訪問葉子節點上關聯的數據也具備更好的緩存命中率。 B+樹的葉子結點都是相鏈的,所以對整棵樹的便利只須要一次線性遍歷葉子結點便可。並且因爲數據順序排列而且相連,因此便於區間查找和搜索。而B樹則須要進行每一層的遞歸遍歷。相鄰的元素可能在內存中不相鄰,因此緩存命中性沒有B+樹好。 B和B+樹的區別在於,B+樹的非葉子結點只包含導航信息,不包含實際的值,全部的葉子結點和相連的節點使用鏈表相連,便於區間查找和遍歷。 總結:爲何使用B+樹? 1.文件很大,不可能所有存儲在內存中,故要存儲到磁盤上 2.索引的結構組織要儘可能減小查找過程當中磁盤I/O的存取次數(爲何使用B-/+Tree,還跟磁盤存取原理有關,具體看下邊分析) 3.局部性原理與磁盤預讀,預讀的長度通常爲頁(page)的整倍數,(在許多操做系統中,頁得大小一般爲4k) 4.數據庫系統巧妙利用了磁盤預讀原理,將一個節點的大小設爲等於一個頁,這樣 每一個節點只須要一次I/O 就能夠徹底載入,(因爲節點中有兩個數組,因此地址連續)。而紅黑樹這種結構, h 明顯要深的多。因爲邏輯上很近的節點(父子)物理上可能很遠,沒法利用局部性。 爲何B+樹比B樹更適合作索引? 1.B+樹磁盤讀寫代價更低: B+的內部結點並無指向關鍵字具體信息的指針,即內部節點不存儲數據。所以其內部結點相對B 樹更小。若是把全部同一內部結點的關鍵字存放在同一盤塊中,那麼盤塊所能容納的關鍵字數量也越多。一次性讀入內存中的須要查找的關鍵字也就越多。相對來講IO讀寫次數也就下降了。 2.B+-tree的查詢效率更加穩定: 因爲非終結點並非最終指向文件內容的結點,而只是葉子結點中關鍵字的索引。因此任何關鍵字的查找必須走一條從根結點到葉子結點的路。全部關鍵字查詢的路徑長度相同,致使每個數據的查詢效率至關。 在MySQL中,最經常使用的兩個存儲引擎是MyISAM和InnoDB,它們對索引的實現方式是不一樣的。 MyISAM data存的是數據地址。索引是索引,數據是數據。 InnoDB data存的是數據自己。索引也是數據。
linux使用的進程間6大通訊方式 1.管道(pipe),流管道(s_pipe)和有名管道(FIFO) 2.信號(signal) 3.消息隊列 4.共享內存 5.信號量 6.套接字(socket)
管道( pipe )
管道這種通信方式有兩種限制,一是半雙工的通訊,數據只能單向流動,二是隻能在具備親緣關係的進程間使用。進程的親緣關係一般是指父子進程關係。
流管道s_pipe: 去除了第一種限制,能夠雙向傳輸.
命名管道:name_pipe克服了管道沒有名字的限制,所以,除具備管道所具備的功能外,它還容許無親緣關係進程間的通訊;
信號量( semophore )
信號量是一個計數器,能夠用來控制多個進程對共享資源的訪問。它常做爲一種鎖機制,防止某進程正在訪問共享資源時,其餘進程也訪問該資源。所以,主要做爲進程間以及同一進程內不一樣線程之間的同步手段。
信號 ( singal )
信號是比較複雜的通訊方式,用於通知接受進程有某種事件發生,除了用於進程間通訊外,進程還能夠發送信號給進程自己;主要做爲進程間以及同一進程內不一樣線程之間的同步手段。
消息隊列( message queue )
消息隊列是由消息的鏈表,存放在內核中並由消息隊列標識符標識。消息隊列克服了信號傳遞信息少、管道只能承載無格式字節流以及緩衝區大小受限等缺點。 消息隊列是消息的連接表,包括Posix消息隊列system V消息隊列。有足夠權限的進程能夠向隊列中添加消息,被賦予讀權限的進程則能夠讀走隊列中的消息。
共享內存( shared memory )
共享內存就是映射一段能被其餘進程所訪問的內存,這段共享內存由一個進程建立,但多個進程均可以訪問。共享內存是最快的 IPC 方式,它是針對其餘進程間通訊方式運行效率低而專門設計的。它每每與其餘通訊機制,如信號量,配合使用,來實現進程間的同步和通訊。 使得多個進程能夠訪問同一塊內存空間。
套接字( socket )
套解口也是一種進程間通訊機制,與其餘通訊機制不一樣的是,它可用於不一樣機器間的進程通訊,更爲通常的進程間通訊機制。起初是由Unix系統的BSD分支開發出來的,但如今通常能夠移植到其它類Unix系統上:Linux和System V的變種都支持套接字。
各類通訊方式的比較和優缺點
各類通訊方式的比較和優缺點
管道:速度慢。容量有限,僅僅有父子進程能通信
FIFO:不論什麼進程間都能通信,但速度慢
信號量:不能傳遞複雜消息,僅僅能用來同步
信號:假設用戶傳遞的信息較少或是需要經過信號來觸發某些行爲.前文提到的軟中斷信號機制不失爲一種簡捷有效的進程間通訊方式.
消息隊列:容量受到系統限制,且要注意第一次讀的時候,要考慮上一次沒有讀完數據的問題。但是信息的複製需要額外消耗CPU的時間,不適宜於信息量大或操做頻繁的場合。
共享內存區:可以很是easy控制容量,速度快,信息量大,高效雙向通訊,但要保持同步,比方一個進程在寫的時候。還有一個進程要注意讀寫的問題,至關於線程中的線程安全。
socket:即套接字是一種通訊機制,憑藉這種機制,客戶/服務器(即要進行通訊的進程)系統的開發工做既能夠在本地單機上進行,也能夠跨網絡進行。
1. 三次握手保證了客戶端和服務器的接受消息能力和發送消息的能力沒問題,保證可靠。 2. 三次握手保證了網絡擁塞狀況下延遲請求問題,不浪費資源。【「爲了防止已失效的鏈接請求報文段忽然又傳送到了服務端,於是產生錯誤」 OR 爲了解決「網絡中存在延遲的重複分組」的問題。】 兩次握手具體解釋: 第一種狀況: 1. 服務器收到了客戶端的消息,服務器知道了客戶端是能夠發送消息的,但因爲沒有第三次握手,因此服務器不知道客戶端是否具備接受消息的能力; 2. 客戶端從服務器接受到了消息,客戶端知道了服務器接受到了個人消息纔回復,說明服務器的接受消息能力和發送消息的能力沒問題; 3. 綜上所述,客戶端確保了服務器的接受發送沒問題,可是服務器僅僅只知道客戶端的發送消息沒問題,這並非可靠的,因此兩次握手不能夠 第二種狀況: 1. 假設客戶端和服務器進行TCP鏈接,而後第一次發送的TCP鏈接請求A發生了阻塞。 2. 因而因爲客戶端沒有收到服務器的應答報文,客戶端認爲這個TCP鏈接請求丟失了,因而從新發送了TCP鏈接請求B。此次沒有阻塞,成功鏈接了,由於是討論的兩次握手,因此只進行兩次鏈接就能夠進行通訊了。 3. 通訊結束,而後就斷開了鏈接B。 4. 阻塞一段時間網絡又暢通了,因而TCP鏈接請求A成功到達了服務器,服務器又覺得是客戶端又要進行數據傳輸,因而服務器就又對這個鏈接請求進行應答,兩次握手,因而又成功創建了TCP鏈接A。 5. 可是因爲客戶端它覺得這個鏈接請求已經丟失了,因此不會利用這個創建的鏈接請求進行數據通訊,雖然服務器分配給了資源給客戶端,可是客戶端並不進行數據傳輸,這樣就白白浪費了服務器的資源。 爲何三次握手能夠解決以上擁塞問題呢? 第三次握手若是沒有發生,服務器過了很長時間(規定好的時間和客戶端)都沒有收到回覆,因而也不會爲客戶端分配資源,此次鏈接就放棄了,就不會浪費資源。
三次握手和四次揮手程序員
三次握手: A:「喂,你聽獲得嗎?」A->SYN_SEND B:「我聽獲得呀,你聽獲得我嗎?」應答與請求同時發出 B->SYN_RCVD | A->ESTABLISHED A:「我能聽到你,今天balabala……」B->ESTABLISHED
四次揮手: A:「喂,我不說了。」A->FIN_WAIT1 B:「我知道了。等下,上一句還沒說完。Balabala…..」B->CLOSE_WAIT | A->FIN_WAIT2 B:」好了,說完了,我也不說了。」B->LAST_ACK A:」我知道了。」A->TIME_WAIT | B->CLOSED A等待2MSL,保證B收到了消息,不然重說一次」我知道了」,A->CLOSED
三次握手創建鏈接: 第一次握手:客戶端發送syn包(seq=x)到服務器,並進入SYN_SEND狀態,等待服務器確認; 第二次握手:服務器收到syn包,必須確認客戶的SYN(ack=x+1),同時本身也發送一個SYN包(seq=y),即SYN+ACK包,此時服務器進入SYN_RECV狀態; 第三次握手:客戶端收到服務器的SYN+ACK包,向服務器發送確認包ACK(ack=y+1),此包發送完畢,客戶端和服務器進入ESTABLISHED狀態,完成三次握手。 // 握手過程當中傳送的包裏不包含數據,三次握手完畢後,客戶端與服務器才正式開始傳送數據。理想狀態下,TCP鏈接一旦創建,在通訊雙方中的任何一方主動關閉鏈接以前,TCP 鏈接都將被一直保持下去。 傳輸數據過程:
a.超時重傳 超時重傳機制用來保證TCP傳輸的可靠性。每次發送數據包時,發送的數據報都有seq號,接收端收到數據後,會回覆ack進行確認,表示某一seq 號數據已經收到。發送方在發送了某個seq包後,等待一段時間,若是沒有收到對應的ack回覆,就會認爲報文丟失,會重傳這個數據包。 b.快速重傳 接受數據一方發現有數據包丟掉了。就會發送ack報文告訴發送端重傳丟失的報文。若是發送端連續收到標號相同的ack包,則會觸發客戶端的快速重 傳。比較超時重傳和快速重傳,能夠發現超時重傳是發送端在傻等超時,而後觸發重傳;而快速重傳則是接收端主動告訴發送端數據沒收到,而後觸發發送端重傳。 c.流量控制 這裏主要說TCP滑動窗流量控制。TCP頭裏有一個字段叫Window,又叫Advertised-Window,這個字段是接收端告訴發送端本身 還有多少緩衝區能夠接收數據。因而發送端就能夠根據這個接收端的處理能力來發送數據,而不會致使接收端處理不過來。 滑動窗能夠是提升TCP傳輸效率的一種機制。 d.擁塞控制 滑動窗用來作流量控制。流量控制只關注發送端和接受端自身的情況,而沒有考慮整個網絡的通訊狀況。擁塞控制,則是基於整個網絡來考慮的。考慮一下這 樣的場景:某一時刻網絡上的延時忽然增長,那麼,TCP對這個事作出的應對只有重傳數據,可是,重傳會致使網絡的負擔更重,因而會致使更大的延遲以及更多 的丟包,因而,這個狀況就會進入惡性循環被不斷地放大。試想一下,若是一個網絡內有成千上萬的TCP鏈接都這麼行事,那麼立刻就會造成「網絡風 暴」,TCP這個協議就會拖垮整個網絡。爲此,TCP引入了擁塞控制策略。 擁塞策略算法主要包括:慢啓動,擁塞避免,擁塞發生,快速恢復。 四次握手斷開鏈接: 第一次揮手:主動關閉方發送一個FIN,用來關閉主動方到被動關閉方的數據傳送,也就是主動關閉方告訴被動關閉方:我已經不會再給你發數據了(當 然,在fin包以前發送出去的數據,若是沒有收到對應的ack確認報文,主動關閉方依然會重發這些數據),但此時主動關閉方還能夠接受數據。 第二次揮手:被動關閉方收到FIN包後,發送一個ACK給對方,確認序號爲收到序號+1(與SYN相同,一個FIN佔用一個序號)。 第三次揮手:被動關閉方發送一個FIN,用來關閉被動關閉方到主動關閉方的數據傳送,也就是告訴主動關閉方,個人數據也發送完了,不會再給你發數據了。 第四次揮手:主動關閉方收到FIN後,發送一個ACK給被動關閉方,確認序號爲收到序號+1,至此,完成四次揮手。主動關閉方等待2MSL之後,沒有收到B傳來的任何消息,知道B已經收到本身的ACK了,主動關閉方就關閉連接,B也關閉連接了。 A爲何等待2MSL,從TIME_WAIT到CLOSE? 在Client發送出最後的ACK回覆,但該ACK可能丟失。Server若是沒有收到ACK,將不斷重複發送FIN片斷。因此Client不能當即關閉,它必須確認Server接收到了該ACK。Client會在發送出ACK以後進入到TIME_WAIT狀態。Client會設置一個計時器,等待2MSL的時間。若是在該時間內再次收到FIN,那麼Client會重發ACK並再次等待2MSL。所謂的2MSL是兩倍的MSL(Maximum Segment Lifetime)。 MSL指一個片斷在網絡中最大的存活時間,2MSL就是一個發送和一個回覆所需的最大時間。 若是直到2MSL,Client都沒有再次收到FIN,那麼Client推斷ACK已經被成功接收,則結束TCP鏈接。
爲何鏈接的時候是三次握手,關閉的時候倒是四次握手?面試
由於當Server端收到Client端的SYN鏈接請求報文後,能夠直接發送SYN+ACK報文。其中ACK報文是用來應答的,SYN報文是用來同步的。可是關閉鏈接時,當Server端收到FIN報文時,極可能並不會當即關閉SOCKET,因此只能先回復一個ACK報文,告訴Client端,"你發的FIN報文我收到了"。只有等到我Server端全部的報文都發送完了,我才能發送FIN報文,所以不能一塊兒發送。故須要四步握手。
算法
若是已經創建了鏈接,可是客戶端忽然出現故障了怎麼辦?spring
TCP還設有一個保活計時器,顯然,客戶端若是出現故障,服務器不能一直等下去,白白浪費資源。服務器每收到一次客戶端的請求後都會從新復位這個計時器,時間一般是設置爲2小時,若兩小時尚未收到客戶端的任何數據,服務器就會發送一個探測報文段,之後每隔75秒鐘發送一次。若一連發送10個探測報文仍然沒反應,服務器就認爲客戶端出了故障,接着就關閉鏈接。