进程的上下文切换和线程的上下文切换的区别

首先我们需要知道进程和线程的上下文切换做了什么:

  1. 切换页目录以及使用新的地址空间
  2. 切换内核栈和硬件的上下文

两种的区别就是进程有第一个操作,线程是没有的,第二个线程和进程都有

关于讨论两者的区别,需要知道进程和线程的在内存地址上的区别,进程是独立的地址空间,而进程里面的线程是连续的地址空间,同一个进程内的线程地址是共享的。

其实切换进程和线程最大的区别就是虚拟地址空间的改变。对于进程由于是独立的地址空间,每一个线程都是有自己的虚拟地址空间,一旦进行切换就需要切换虚拟地址空间;线程由于是地址空间是共享的,所以不需要切换虚拟地址空间。那么问题来了,什么是虚拟地址?为什么虚拟地址空间的切换是消耗性能的?

虚拟地址空间,是一个操作系统为每一个进程提供的一块独立的、私有的、地址连续的虚拟内存,但虚拟内存终究还是虚拟的,需要真正和物理地址挂钩,那么这样的技术就叫地址空间映射技术,把虚拟地址和物理地址建立关系。对于这种关系表就叫做页表,由于每一个进程都有自己的虚拟内存,所以每一个进程也有自己的页表,页表就是查询虚拟内存和物理内存关系的,但对于线程都是共享地址空间的,不涉及到空间地址的转换。

现在我们就可以回答为什么虚拟地址的切换是消耗性能的了,进程的切换首先就需要把虚拟地址转换为物理地址,就需要查询页表,查询是很慢的,所以就会需要一个cache来缓存常用的地址转换,当虚拟地址进行切换时,就需要更新页表的信息和缓存,此时查表就很慢,表现处理的就是应用程序的慢,但线程就不需要操作了。

总的来说就是,进程由于自身内存是独立的,所以当切换线程就涉及到地址的转变,在转变的时候导致慢的原因是页表查询cache失效,需要重新cache,这样就导致程序慢。线程由于共享内存地址,所以不存在这个问题。

Mysql如何解决幻读问题

首先要知道Mysql的四类隔离级别:

  1. 读未提交,会发生脏读
  2. 读已提交,不可重复读
  3. 可重复读,可能产生幻读
  4. 串读

具体的意思我就不讲了,这里主要讲Mysql如何解决幻读问题,幻读就是事务A 按照一定条件进行数据读取,期间事务B 插入了相同搜索条件的新数据,事务A再次按照原先条件进行读取时,发现了事务B 新插入的数据。

首先要知道,我们在用SQL进行数据库操作时是有两种不同类型的读,一个是快照读,一个是当前读。

快照读就我们在执行select语句时生效,基于MVCC并发版本控制,它的底层其实也很简单就是有一个创建版本号字段和删除版本号字段,用户在进行查询时会带上一个自己拿到的版本号小于或等于当前版本号和删除版本号为空或者大于当前版本的参数。

当前读就是在数据库读取最新数据,insert,delete,update的操作默认就是当前读,还有就是当我们的select带上了共享锁lock in share model和独占锁for update,如果并发读写都是当前读级别,那就不会出现幻读问题,此时加上了next-key-lock,是记录锁和间隙锁的结合。利用索引,把列上键值范围分成了一些区间(左开右闭),加锁是根据查询条件封住满足条件的区间(即使记录不存在),使得这些区间无法被修改,实现了幻读的避免。

如果select不加锁,自动适应MVCC,写却是当前读,就可能出现脏读

Mysql的Undo log日志的理解

undo log,就是大家经常所说的回滚日志。

它里面记录的是对数据的回滚操作。当我们对数据库中的数据有变动操作的时候,为了可以回滚到数据被改动之前的版本,就把数据的变动过程的逆向操作给记录在undo log中。我们对数据库的查询查找是不会记录undo log的,只有数据库中的数据有变化的操作才会记录undo log。

我们执行一个insert语句,在undo log中就记录一个delete语句,用于删除掉刚插入的数据,以此来达到回滚到插入之前的状态;

我们执行了一个update语句,在undo log中也记录一个upate语句,只不过这个update语句的内容是把我们刚才执行update操作的数据内容给修改回去,以此达到回滚到数据修改之前的状态;

我们执行一个delete语句,在undo log中就记录一个insert语句,用于把刚才删除的数据再插入到数据库中,以此来达到回滚到删除之前的状态。

简而言之:undo log中记录的内容是如何把数据还原到变动之前的状态,根据这个日志中的记录,就可以把数据还原到上一个事务提交后的状态。

还用Undo Log来实现多版本并发控制(简称:MVCC)。Undo Log是为了实现事务的原子性。什么是事务的原子性,这里简单提一句:一个事务的所有操作要么全部成功,要么全部失败,不能只提交部分操作。在失败的时候回,需要回滚之前的部分操作,而这个回滚操作就是依赖于我们今天提到的undo log。从undo log里面去回滚数据到事务开启之前的状态。

事务中的所有操作,要么全部完成,要么不做任何操作,不能只做部分操作。如果在执行的过程中发生了错误,要回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过。

而redolog是为了实现事务的持久性,要把所有对数据的修改,持久化到磁盘,只要事务提交成功了,不能因为重启、宕机等原因导致提交的数据丢失了,不见了。这里,把redo和undo两种log对照记一下。

事务一旦完成,该事务对数据库所做的所有修改都会持久的保存到数据库中。为了保证持久性,数据库系统会将修改后的数据完全的记录到持久的存储上。