美團點評面試題目(2019)


轉載自:https://www.cnblogs.com/haimishasha/p/10872857.html

1. 自我介紹

2. 項目介紹,項目難點

3. 筆試題研究過嗎?

4. Mybatis多參數傳遞   

復制代碼
//方法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>
復制代碼

5. 數據庫隔離級別

  

復制代碼
並行事務的四大問題:
    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結束。
復制代碼

  數據庫引擎

復制代碼
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快很多倍,但是數據庫斷電或是重啟后,表中的數據將會丟失,表結構不會丟失。
復制代碼

  數據庫鎖

復制代碼
// 數據庫鎖出現的目的:處理並發問題

鎖分類
從數據庫系統角度分為三種:排他鎖、共享鎖、更新鎖。 
從程序員角度分為兩種:一種是悲觀鎖,一種樂觀鎖。
悲觀鎖按使用性質划分:排他鎖、共享鎖、更新鎖。
悲觀鎖按作用范圍划分:行鎖、表鎖。
樂觀鎖實現方式:版本號,時間戳。

數據庫規定同一資源上不能同時共存共享鎖和排他鎖。

一、悲觀鎖(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=?這樣的的語句時,因為事先不知道會影響哪些行就可能會使用表鎖。

二、樂觀鎖(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. 所有字段 
所有字段:和待更新字段類似,只是使用所有字段做版本控制信息,只有所有字段都沒變化才會執行更新。
復制代碼

6. spring數據庫隔離級別

復制代碼
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大問題:
    臟讀:當一個事務正再訪問數據,並且對數據進行修改,而這種修改還沒提交到數據庫,這是另外一個事務也可以訪問這個數據,並進行使用,因為前一個事務還未提交到數據庫,那么另外一個事務讀到這個數據是臟數據。
    不可重復讀:在一個事務內,多次讀同一數據,在這個事務還沒有結束時,另外一個事務也訪問該數據,那么在第一事務中的兩次讀取數據之間,由於第二個事務的修改,那么第一個事務兩次讀到的數據可能不一樣。
    幻讀:當事務不是獨立執行發生,例:當第一個事務對表中數據進行修改,而這種修改是針對全表數據行,同事第二個事務向表中插入一條新數據,那么在操作發生后,第一個事務用戶發現表中還有沒有修改的數據行。
復制代碼

  配置方式 

  

復制代碼
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"/>
復制代碼

  補充 

復制代碼
spring 什么情況下進行事務回滾?

Spring、EJB的聲明式事務默認情況下都是在拋出unchecked exception后才會觸發事務的回滾

unchecked異常,即運行時異常runntimeException 回滾事務;
checked異常,即Exception可try{}捕獲的不會回滾.當然也可配置spring參數讓其回滾.

一般不需要在業務方法中catch異常,如果非要catch,在做完你想做的工作后(比如關閉文件等)一定要拋出runtime exception,否則spring會將你的操作commit,這樣就會產生臟數據.所以你的catch代碼是畫蛇添足。
復制代碼

7. mysql建索引的幾大原則

復制代碼
索引的優點
    1.通過創建唯一索引,可以保證數據庫每一行數據的唯一性
    2.可以大大提高查詢速度
    3.可以加速表與表的連接
    4.可以顯著的減少查詢中分組和排序的時間。

索引的缺點
    1.創建索引和維護索引需要時間,而且數據量越大時間越長
    2.創建索引需要占據磁盤的空間,如果有大量的索引,可能比數據文件更快達到最大文件尺寸
    3.當對表中的數據進行增加,修改,刪除的時候,索引也要同時進行維護,降低了數據的維護速度

建立索引的原則
    1、對於查詢頻率高(用於查詢條件)的字段創建索引;
    2、對排序、分組、聯合查詢頻率高的字段創建索引,提高搜索速度;
    3、索引的數目不宜太多原因:a、每創建一個索引都會占用相應的物理空間;b、過多的索引會導致insert、update、delete語句的執行效率降低;
    4、若在實際中,需要將多個列設置索引時,可以采用多列索引,在經常存取的多個列上建立復合索引,但要注意復合索引的建立順序要按照使用的頻度來確定;
    5、選擇唯一性索引,數據本身具備唯一性的時候,建立唯一性索引,可以保證定義的列的數據完整性,以提高查詢熟度
    6、盡量使用數據量少的索引。如果索引的值很長,那么查詢的速度會受到影響。
    7、盡量使用前綴來索引。如果索引字段的值很長,最好使用值的前綴來索引。例如,TEXT和BLOG類型的字段,進行全文檢索會很浪費時間。如果只檢索字段的前面的若干個字符,這樣可以提高檢索速度。
    8、刪除不再使用或者很少使用的索引.
    9.數據量小的表最好不要建立索引;包含大量的列並且不需要搜索非空值的時候可以考慮不建索引.數據量超過300w的表應該有索引
    10. 常更新的表越少越好,對於經常存取的列避免建立索引;
    11. 在不同值較少的字段上不必要建立索引,如性別字段;
    12. 用於聯接的列(主健/外健)上建立索引;
    13. 缺省情況下建立的是非簇集索引,但在以下情況下最好考慮簇集索引,如:含有有限數目(不是很少)唯一的列;進行大范圍的查詢;充分的利用索引可以減少表掃描I/0的次數,有效的避免對整表的搜索。當然合理的索引要建立在對各種查詢的分析和預測中,也取決於DBA的所設計的數據庫結構。
    14. 復合索引的建立需要進行仔細分析;盡量考慮用單字段索引代替:        
        A、正確選擇復合索引中的主列字段,一般是選擇性較好的字段;
        B、復合索引的幾個字段是否經常同時以AND方式出現在Where子句中?單字段查詢是否極少甚至沒有?如果是,則可以建立復合索引;否則考慮單字段索引;
        C、如果復合索引中包含的字段經常單獨出現在Where子句中,則分解為多個單字段索引;
        D、如果復合索引所包含的字段超過3個,那么仔細考慮其必要性,考慮減少復合的字段;
        E、如果既有單字段索引,又有這幾個字段上的復合索引,一般可以刪除復合索引;
        
復制代碼

8. B+樹和B樹

復制代碼
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存的是數據本身。索引也是數據。
復制代碼

9. 排序算法

10. linux進程間有幾種通信方式,各種方式的優缺點 

復制代碼
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:即套接字是一種通信機制,憑借這種機制,客戶/服務器(即要進行通信的進程)系統的開發工作既可以在本地單機上進行,也可以跨網絡進行。
復制代碼

  

11. TCP為什么不兩次握手而要三次握手?

復制代碼
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報文,因此不能一起發送。故需要四步握手。

 
         

 如果已經建立了連接,但是客戶端突然出現故障了怎么辦?

  TCP還設有一個保活計時器,顯然,客戶端如果出現故障,服務器不能一直等下去,白白浪費資源。服務器每收到一次客戶端的請求后都會重新復位這個計時器,時間通常是設置為2小時,若兩小時還沒有收到客戶端的任何數據,服務器就會發送一個探測報文段,以后每隔75秒鍾發送一次。若一連發送10個探測報文仍然沒反應,服務器就認為客戶端出了故障,接着就關閉連接。
復制代碼

 

抄錄網址

  1. 數據庫的隔離級別以及悲觀鎖和樂觀鎖詳解
  2. S、X、IS、IX數據庫鎖機制 很詳細的教程,簡單易懂
  3. 數據庫鎖分類和總結
  4. 為什么Java開發人員必須要了解數據庫鎖?
  5. 數據庫隔離級別及實現原理
  6. 數據庫隔離級別的原理解析
  7. Spring事務傳播機制和數據庫隔離級別 
  8. Spring事務傳播機制和數據庫隔離級別
  9. Spring 事務配置的幾種方式
  10. Spring配置事務的五種方式
  11. 淺談mysql的索引設計原則以及常見索引的區別
  12. mysql 建立索引的原則(轉)
  13. MySQL索引篇,索引的優缺點,分類及設計原則
  14. mysql創建索引的原則
  15. MySQL系列—建索引的幾大原則和使用索引優化查詢
  16. 數據庫索引以及索引的實現(B+樹介紹,和B樹,區別)
  17. 淺談算法和數據結構: 十 平衡查找樹之B樹
  18. Linux進程間通信的幾種方式總結--linux內核剖析(七)
  19. 【漫畫】TCP連接為什么是三次握手,而不是兩次握手,也不是四次握手?
  20. tcp為什么要三次握手,而不能二次握手?
  21. 6張動態圖輕松學習TCP三次握手和四次揮手
  22. 網絡TCP建立連接為什么需要三次握手而結束要四次
  23. TCP協議:三次握手過程詳解
  24. TCP的三次握手與四次揮手理解及面試題(很全面)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM