美团点评面试20190515

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结束。

  数据库引擎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. 全部字段 
全部字段:和待更新字段相似,只是使用全部字段作版本控制信息,只有全部字段都没变化才会执行更新。

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大问题:
    脏读:当一个事务正再访问数据,而且对数据进行修改,而这种修改还没提交到数据库,这是另一个事务也能够访问这个数据,并进行使用,由于前一个事务还未提交到数据库,那么另一个事务读到这个数据是脏数据。
    不可重复读:在一个事务内,屡次读同一数据,在这个事务尚未结束时,另一个事务也访问该数据,那么在第一事务中的两次读取数据之间,因为第二个事务的修改,那么第一个事务两次读到的数据可能不同。
    幻读:当事务不是独立执行发生,例:当第一个事务对表中数据进行修改,而这种修改是针对全表数据行,同事第二个事务向表中插入一条新数据,那么在操做发生后,第一个事务用户发现表中还有没有修改的数据行。

  配置方式 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代码是多此一举。

7. mysql建索引的几大原则

索引的优势
    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、若是既有单字段索引,又有这几个字段上的复合索引,通常能够删除复合索引;
        

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报文,所以不能一块儿发送。故须要四步握手。

算法

 
 

 若是已经创建了链接,可是客户端忽然出现故障了怎么办?spring

  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的三次握手与四次挥手理解及面试题(很全面)
相关文章
相关标签/搜索