数据库之Mysql的常见面试问题

1、为什么要使用索引?

  答:如果进行全表扫描,将整个数据表的数据全部或者分批次加载到内存当中,存储的最小单位是块或者页,它们是由多行数据来组成的,将这些块都加载进来,逐个块去轮询,找到我们要的目标并返回,这种方式非常的慢,但是如果数据量小的话,这种方式也非常快的。如果数据量过大,就要避免全表扫描的情况发生,此时就要引入索引了,索引可以快速查询数据,避免全表扫描查找数据,提升检索效率

2、什么样的信息能称为索引?

  答:可以将记录限制到一定范围内的字段,主键是一个很好的数据切入点,包含主键、唯一键以及普通键等等。主键、唯一键等,只要是能让数据具备一定区分性的字段都可以成为索引。

3、索引的数据结构?

  答:查询高效的数据结构,生成索引,建立二叉查找树进行二分查找,还有红黑树、平衡二叉树。生成索引,建立B-Tree结构进行查找。生成索引,建立B+Tree结构进行查找。生成索引,建立Hash结构进行查找。索引的数据结构主流是B+树,还有哈希结构,BitMap结构,其中Mysql数据库不支持BitMap索引,其中基于Innodb、MyISAM引擎的Mysql不显示支持Hash。

3.1、二分查找树的使用。二叉查找树,每个节点最多有两个孩子,左子树和右子树,二叉查找树的重要性质是对于每个节点x,左子树的任意节点的值小于x,右子树的任意节点的值大于x。如果使用二叉查找树存储索引,确实可以提升查询效率,需要注意的是索引的存储块和数据库最小存储单位,块或者页,实际上并非一一对应的,只是为了发布理解,先将其一一对应起来,每个存储块存储的是关键字和指向子树的指针。此例中的树不仅是二叉树,还是平衡二叉树,即任意一个节点左右子树高度差均不超过一。二叉查找树的查找使用的二分查找,因为是对半搜索,所以时间复杂度是O(logn),其查询效率是很高的,但是数据库的数据面临的是插入和删除的。

  如果此时将2和6删除掉了,新增了11、13两个元素,此时就变成了线性的二叉树,此时的时间复杂度就变成了O(n),大大降低了查询的效率,影响程序运行的瓶颈是IO。

3.2、如何降低查询时间的复杂度,又降低IO的次数呢?就是将树变得矮一些,每一个节点存储的数据多一些,这个时候就可以使用B-Tree树了。

  平衡多路查找树,如果每个节点最多有m个孩子,这样的树就是m阶B树,此图是三阶B树的样子,当然现实中,索引每个节点的孩子上限肯定远大于三的,每个存储块中主要包含了关键字和指向孩子的指针,最多有几个孩子,取决于每个存储块的容量和以及数据库的相关配置,所以通常情况下,m是很大的。

运用B树,显示节点元素和指针的图示,如下所示:

B树的特征与定义。

  1)、根节点至少包括两个孩子。
  2)、树中每个节点最多含有m个孩子(m >= 2),这就是m阶B树的含义了,m取决于节点的容量以及数据库的相关配置。例子中是3阶B树,所以孩子数目不能大于3,并且孩子数目不能少于ceil(3 / 2) = 1.5,取上限就是2。
  3)、除根节点和叶子节点外,其它每个节点至少有ceil(m/2)个孩子,这里面的ceil是取上限,不是四舍五入的。
  4)、所有叶子节点都位于同一层,即叶子节点的高度都是一样的。
  5)、假设每个非终端节点中包含有n个关键字信息,其中。

    a)、Ki(i=1......n)为关键字,且关键字顺序升序排序K(i-1) < Ki。假设Ki为非终端节点的关键字,i等于1到n的任意一个正整数,则关键字需要按照顺序升序排序,即K(i-1) < Ki。比如8小于12,是按照升序排列的。

    b)、关键字的个数n必须满足:[ceil(m / 2) -1] <= n <= m-1。即任意节点的关键字个数上限比它的孩子数上限少一个,且对于非叶子节点来说,任何一个节点的关键字个数比它的指向孩子的指针个数少一个,比如有两个节点元素就有三个指针的。

    c)、非叶子节点的指针:P[1]、P[2]...,P[M]。其中P[1]指向关键字小于K[1]的子树,P[M]指向关键字大于K[M-1]的子树,其中P[i]指向关键字属于(K[i-1],K[i])的子树。其中P[1]指向关键字小于K[1]的子树这句话的意思是某节点最左边的孩子节点里面的关键字的值均小于该节点最左边的关键字的值,比如3和5小于8。P[M]指向关键字大于K[M-1]的子树这句话的意思是该节点最右边的孩子节点里面关键字的值均大于该节点里面所有关键字的值,比如13和15大于8和12。其中P[i]指向关键字属于(K[i-1],K[i])的子树这句话的意思是该节点其余的孩子节点里面的关键字的值的大小均位于离该孩子节点指针最近的两个关键字之间,是一个开区间的,比如P2这个指针指向的9和10均位于8和12这个开区间的范围内。
  6)、遵守这些约束,目的只有一个,让每个索引块尽可能存储更多的信息,让树的高度尽可能减少IO的次数。B-Tree树和二叉查找树的效率是一样高效的O(logn)。当数据发生变动的时候,必须会存在现有结构被打乱的情况,二叉查找树有可能被打乱成线性的,但是B-Tree树有五条规则约束,B-Tree有相应的策略,通过合并,分裂,上移,下移节点来保持B-Tree树的特征,此树远比二叉树矮的多,并且不会根据数据不断变动变成线性的这种情况。

3.3、B+树(B+Tree),B+树是B树的变体,其定义基本与B树相同,除了下面这几条。

  1)、非叶子节点的子树指针与关键字个数相同。B+树的非叶子节点元素个数和指针的数目是相同的,B树里面如果是3阶数,非叶子节点元素个数和指针是相同的。表明了B+树可以存储更多的关键字。
  2)、非叶子节点的子树指针P[i],指向关键字值[K[i],K[i + 1])前闭后开的子树。比如K[i]的值等于10,那么K[i + 1]的值就是20,10对应的子树里面的值10、15、18均小于K[i + 1]这个20的值,均大于等于K[i]这个10的值,注意,大于等于K[i]的条件不是硬性的,也有取子树里面的最大值18作为K[i]的这种情况。但是必须小于K[i + 1]的值。
  3)、非叶子节点仅用来索引,数据都保存在叶子节点中。比如要搜索树10相关的数据的时候,当搜索到第一个10的时候,并不能停止,必须要检索到叶子节点当中,因为叶子节点才存储了我们需要用到的数据,存储的数据有的可能是指向数据文件的指针,有的也可能是主键的值,或者直接将关键数据存储到这个节点上面了,总之会存储到叶子节点当中的。这就表明了B+树所有的检索都是从根部开始的,检索到叶子节点才能结束,同时非叶子节点仅用来存储索引,非叶子节点不存储数据的话,又可以存储更多的关键字了,这也就使得B+树相对B树来说更矮,B树的搜索可能在任意一个非叶子节点就终结掉了,也就是可能把数据文件存储到非叶子节点上。
  4)、所有叶子节点均有一个链指针指向下一个叶子节点,并按大小顺序链接。把这些叶子节点链接起来有什么用呢,B+树的叶子节点都是按照大小顺序来做排列的,链接起来的话呢,方便我们直接在叶子节点做范围统计,比如说要搜索大于等于10的这些数据,定位到叶子节点的10之后呢,会根据链接直接向后进行统计,而不会是回到根节点进行搜索了,支持范围统计,即一旦定位到某个叶子节点,便可以从该叶子节点开始横向去跨子树去做统计。

  5)、总结,B+树更适合用来做存储索引,相比于B树或者其它树来说,在文件系统以及数据库系统当中更有优势,原因如下所示:

    5.1)、B+树的磁盘读写代价更低。B+树的内部结构并没有指向关键字具体信息的指针,也就是不存放我们的数据,只存放索引信息,因此其内部节点相比于B树更小,如果把所有统一内部节点的关键字存放在同一盘块中,这个盘块所能容纳的关键字数量也越多,一次性读入内存中需要查找的关键字也就越多,相对来说,IO读写次数也就降低了。
    5.2)、B+树的查询效率更加文稳定。由于内部节点并不是最终指向文件内容的节点,而只是叶子节点中关键字的索引,所以任何关键字的查找,必须走一条从根节点到叶子节点的路,所有关键字查询的长度相同,导致每一个数据的查询效率也几乎是相同的,稳定的O(logn)。
    5.3)、B+树更有利于对数据的扫描。B树在提高了磁盘IO性能的同时,并没有解决元素遍历效率低下的问题,而B+树只需要遍历叶子节点就可以解决对全部关键字信息的扫描。对于数据库中频繁使用的范围查询,B+树在范围查询的过程中有更高的性能,这也就是把B+树做主流索引数据结构的原因。

3.4、运用Hash结构存储索引。

  有些数据库存储引擎还支持哈希这个数据结构作为其索引,哈希结构就是根据哈希函数的运算,只需经过一次定位便能找到需要查询数据所在的头,B+树需要从根节点到非叶子节点,再到叶子节点,最后才可以访问到我们需要的数据,这样可能会经过多次的IO访问,哈希索引的效率理论上要高于B+树的效率。

  哈希索引的查询效率虽然比较高,但是哈希结构做索引是有缺点。
    1)、缺点一如仅仅能满足"=","IN",不能使用范围查询。由于哈希索引比较的是进行哈希运算之后的哈希值,所以只能等值的过滤,不能用于基于范围的查询,因为经过相应的哈希算法处理之后的哈希值的大小,并不能保证和哈希运算前的完全一样。
    2)、缺点二,无法被用来避免数据的排序操作。因为哈希结构存放的应该哈希运算之后的值,而且哈希值的大小关系并不一定和哈希运算前的键值完全一样,所以数据库无法使用索引的数据来避免任何排序运算。
    3)、缺点三,不能利用部分索引键查询。对于组合索引,哈希索引在计算哈希值的时候是组合键,就是将组合索引键合并之后,在一起进行运算的哈希的值,而不是单独计算哈希值的,通过组合索引的前面一个或者几个索引键来查询的时候,哈希索引也无法被利用,而B+树是支持利用组合索引里面的部分索引的。
    4)、缺点四,不能避免表扫描。哈希索引是将索引键通过哈希运算之后将运算结果的哈希值和所对应的行指针信息存储到一个桶中,由于不同索引键存在相同的哈希值,所以即使取出满足某个哈希键值的那些数据来,也无法从哈希索引中直接完成查询,还是要通过访问这个桶中的实际数据进行相应的比较,这是无法避免表扫描的原因的。
    5)、缺点五,遇到大量Hash值相等的情况后性能并不一定就会比B+树索引高。对于选择性比较低的索引键,如果创建哈希索引,那么将会存在大量记录指针及其存放同一个桶的情况,从而造成整体性能非常低下,就类比之前的二叉树,很可能变成线性存储结构,也有可能在一个极端的情况下,所有的键计算出来的哈希值都是相同的,也就是都放到同一个桶中,那我们查询最后一条数据,就会变成线性的了,所以呢,这也是哈希索引不能成为主流索引的原因,因为不稳定,也不支持范围的查询。

3.6、BitMap索引,称为位图索引, 当表中的某个字段只有几种值的时候,比如要表示性别,只有男女两种性别的时候,如果仅仅是为了在这个字段上实现高效的统计,此时使用位图索引是最佳选择了,需要注意的是,目前很少的数据库支持位图索引,比较主流的是Oracle数据库。位图索引的结构类似于B+树。

4、密集索引和稀疏索引的区别?

  答:密集索引文件中的每个搜索码值都对应一个索引值。密集索引,叶子节点保存的不仅仅是键值,还保存了位于同一行记录里面的其他列的信息,由于密集索引决定了表的物理排列顺序,一个表只能有一个物理排列顺序,所以一个表只能创建一个密集索引。

    稀疏索引文件只为索引码的某些值建立索引项。叶子节点仅保存了键位类信息,以及该行数据的地址,有的稀疏索引是仅保存了键位信息及其主键,那么定位到叶子节点之后,仍然需要通过地址和主键信息进一步定位到数据。

5 、对MySql做具体分析,主要有两种存储引擎,一种是MyISAM,另外一种是Innodb。对于MyISAM不管是主键索引,唯一键索引,或者普通索引,其索引均属于稀疏索引。Innodb是必须有且仅有一个密集索引,密集索引的选取规则。

  5.1)、Innodb密集索引的选取规则。

    1)、若一个主键被定义,该主键则作为密集索引而存在的。
    2)、若没有主键被定义,该表的第一个唯一非空索引则作为密集索引。
    3)、若不满足以上条件,Innodb内部会生成一个隐藏主键(密集索引)是一个6字节的列,该列的值会随着数据的插入而自增,也就是说Innodb必须有一个主键,而该主键必须作为唯一的密集索引而存在。
    4)、非主键索引存储相关键位和其对应的主键值,包含两次查找,这就是为什么一定要主键索引。非主键索引,也就是稀疏索引的叶子节点并不存储行数据的物理地址,而是存储的是该行的主键值,所以非主键索引包含了两次查找,一次是查找次级索引自身,再查找主键。

  5.2)、Innodb使用的是密集索引,将主键组织到一棵B+树中,而行数据就存储在叶子节点中,因为Innodb的主键索引和对应的数据是保存在同一个文件中的,所以检索的时候,在加载叶子节点的主键进入内存的同时,也加载了对应的数据,即若使用where id = 某个值的时候,这样的条件查询主键,则按照B+树的检索算法即刻查找到叶子节点,并获得对应的行数据,若对稀疏索引进行条件筛选,则需要经历两个步骤,第一步在稀疏索引的B+树中检索该键,然后定位到主键信息,获取到主键信息之后呢,还需要精力第二步,第二步就是使用where id =某个值的时候,在B+树中再执行一遍我们B+树的检索操作,最终再到达叶子节点获取整行的数据。

  5.3)MyISAM使用的是均为稀疏索引,稀疏索引的B+树看上去没有什么不同,节点的结构呢,完全一致,只是存储的内容不一样而已,主键索引B+树的节点存储的主键,辅助键索引B+树存储的辅助键,表数据存储在独立的地方,就是索引和数据是分开存储的,这两棵B+树的叶子节点呢,都使用一个地址指向真正的表数据,像这里的映射关系,对于表数据来说,这两个键没有任何差别,由于索引数是独立的,通过辅助键检索,无需访问主键的索引数。

6、 如何定位并优化慢查询Sql?

答:可以先回答,具体场景需要具体分析。

  1)、根据慢日志定位慢查询sql。慢日志就是用来记录我们执行的比较慢的sql。Mysql有很多自带的系统变量,通过查询系统变量可以得知配置信息。
使用show variables like '%quer%';
根据查询出来的变量,需要关注slow_query_log这个是慢日志,slow_query_log_file这个文件会记录我们的慢日志,long_query_time这个是表示的是每次执行这个SQL花费10秒钟的都会被记录到慢日志文件里面,可以修改是1秒钟,如果大于1秒的都会被记录到慢日志文件里面。使用set global slow_query_log = on来打开慢日志查询,set global long_query_time = 1;这个变量需要重新链接客户端,临时生效,重启数据库恢复原值。
使用show status like '%slow_queries%',查询系统的状态,Slow_queries这个是慢查询的数量,当我们有一次执行比较慢的时候,value值就是1,以此类推,注意,这里是本次链接的条数。

  2)、使用explain等工具分析sql。在分析查询性能的时候,explain非常管用,explain放在select查询关键字的前面,用来描述mysql如何执行查询操作。以及mysql成功返回结果集执行的行数。explain可以帮助我们分析select语句,让我们知道查询效率低下的原因,从而改进我们的查询,让查询优化器可以更好的工作。

  注意,extra,可以获取到更为详细的信息,辅助我们l了解语句的执行方式,类型有很多,这里关注Using fIlesort、Using temporary。extra中出现以下2项意味着MYSQL根本不能使用索引,效率会受到重大影响,应尽可能对此进行优化。

extra项 说明
Using filesort 表示Mysql会对结果使用一个外部索引排序,而不是从表里面按照索引次序读到相关内容。可能在内存或者磁盘上进行排序。Mysql中无法利用索引完成的排序操作称为"文件排序"。

Using temporary 表示Mysql在对查询结果排序的时候使用临时表,常见于排序Order by和分组查询Group by。


  3)、修改sql或者尽量让sql走索引,即修改自己的sql进行优化或者将需要查询的字段添加一个索引即可。 

可以使用语句来强制使用某个索引,如下所示。mysql的查询优化器,最重要的目标是尽可能的使用索引,并且使用最严格的索引来消除尽可能多的数据行,最终目标是提交select语句查找数据行,而不是排除数据行,优化器试图排除数据行的原因在于它排除数据行的速度越快,那么找到与条件匹配的数据行也就越快,因此,查询优化器会根据一些分析和判断的标准决定走那个索引。

7、联合索引的最左匹配原则的成因,联合索引就是有多列组成的索引。

  7.1)、最左匹配原则,假设有两列A、B,此时对A、B设置一个联合索引,就是将A、B都设置成索引,设置顺序是A、B,在where语句中调用A = ? and B =? ,此时会走A、B的联合索引,如果使用where A = ? 也会走联合索引,但是当我们调用where B = ?的时候,此时不会走A、B的组合索引了。

  7.2)、最左前缀匹配原则,非常重要的原则,mysql会一直向右匹配直到遇到查询(>、<、between、like)就停止匹配,比如a= 3 and b = 4 and c > 5 and d = 6,如果建立(a、b、c、d)顺序的索引,d是用不到索引的,如果建立(a、b、d、c)的索引则都可以用到,a、b、d的顺序可以任意调整

  7.3)、=和in可以乱序,比如a = 1 and b = 2 and c =3 建立(a、b、c)索引可以任意顺序,mysql的查询优化器会帮你优化成索引可以识别的形式。

  7.4)、联合索引的最左匹配原则的成因,mysql创建复合索引的规则是首先会对复合索引的最左边的,也就是第一个索引字段的数据进行排序,在第一个字段的排序的基础上呢,在对后面第二个索引字段进行排序,其实就相当于实现了类似Order By字段1,Order By 字段2,这样的一种排序规则.所以呢,第一个字段是绝对有序的,而第二个字段就是无序的了,因此通常情况下,直接使用第二个字段进行条件判断是用不到索引的,这就是所谓的Mysql为什么要强调联合索引的最左匹配原则的原因。

8、索引是建立的越多越好吗?

  答:首先数据量小的表不需要建立索引,建立索引会增加额外的索引开销。数据变更需要维护索引,因此更多的索引意味着更多的维护成本。更多的索引意味着也需要更多的空间。

9、锁模块之MyISAM与InooDB关于锁方面的区别是什么?

  1)、MyISAM默认用的是表级锁,不支持行级锁。MyISAM不支持事务,表级锁的情况不好进行模拟,此时将数据表数据量增大,这样进行查询或者更新比较多的数据的时候,会需要一点时间去执行语句,这个时候再开个窗口来去做别的操作,这样就可以制造出并发访问的场景,并且可以看到MyISAM会自动的为我们加上相关的表锁。

1.1)、第一种情况,下面演示了,先上读锁,再上写锁或者读锁的情况,如下所示:

主键索引范围的查询,myisam引擎,MyISAM默认用的是表级锁,不支持行级锁。

先上读锁,再上写锁:当读锁未被释放的时候,另外一个窗口想要对同一张表加上写锁的时候,就会被阻塞,直到所有的读锁都被释放为止。

先上读锁,再上读锁:MyISAM,在进行范围查询的时候呢,我们依然可以对表里面的数据进行读操作,这也是读锁,又叫为共享锁的原因。 

1.2)、此时,演示先上写锁,后上读锁或者写锁这种情况。MyISAM默认用的是表级锁,不支持行级锁。表级锁会锁住整张表,如果先执行1到两百万的查询操作,然后再执行两百万开外的数据进行修改操作,数据表同样是被锁住的,但是如果是行级锁的话,只锁住1到两百万行的数据,对两百万开外的数据进行更新是可以进行更新的。锁按照级别分为共享锁和排它锁,上了共享锁之后呢,依然支持上共享锁,不支持上排它锁,要是先上排它锁,那么另外的读或者写都是不允许的,共享锁和排它锁的这种情况,也同样适合于支持行级锁的InnoDB引擎。

第二个窗口,模拟并发操作。

先上写锁,再上读锁:查询操作需要等待更新操作执行完毕,将写锁释放,才可以执行查询操作的。

先上写锁,再上写锁:当第二次更新语句的时候,需要等待第一此更新的语句执行完毕,将写锁释放,才可以执行第二个更新操作。

2)、InnoDB默认用的是行级锁,也支持表级锁。

  2.1)、什么是二段锁呢,也就是加锁和解锁是分成两个步骤来进行的,即先对同一个事务里面的一批操作分别进行加锁,然后到commit的时候,在对事务里面加上了锁呢,进行统一的解锁,而当前commit是自动提交的,所以看起来和Myisam没有太大的区别。

第二个窗口,模拟并发操作。

先读,后写:显示的上共享锁,后面加上lock in share mode就上了读锁了。上了读锁之后,再执行更新语句就无法执行了。此时需要该session执行commit之后,更新语句才可以成功。

2.2)、先读,后读:此时id等于3这个sql语句并没有提交,此时,使用另外一个session,将id等于3的数据加上共享锁,看看是否可以进行查询。是可以将数据读出来的。此时,这两个共享锁是不会起冲突的。

第二个窗口,模拟并发操作。

3)、InnoDB行级锁和索引是否有关呢

  什么是表级锁和行级锁呢,首先需要明白,并发的访问是有多个语句可能同时操作一张表,或者同一张表里面的同一条数据。

  用到表级锁,只要操作到表里面的数据的时候,均会上表锁,因此表级锁和索引无关。行级锁和索引是否有关呢,除了使用id主键索引以外的其他键,只要sql用到了索引设计到的行都会被上共享锁或者排它锁。当不走索引的时候,整张表都会被锁住,也就是此时的查询用的是表级锁,所以InnoDB在sql没有索引的时候,用的是表级锁,而sql用到索引之后,用的是行级锁,以及Get锁,这个Get锁是设计到走普通非唯一索引的时候用到的。

  无论是表级锁还是行级锁,都分为共享锁和排斥锁,共享锁和排斥锁的兼容性,它们之间的关系如下所示:

  X S
X 冲突 冲突
S 冲突 兼容

解释如下所示:

  如果session1对某一行数据上了排它锁X,排它锁怎么上的,进行增加、修改、删除就行了,或者select for update的时候都会给某一行数据上排它锁,同时呢,如果session2想对这行数据排它锁,这是不允许的,会冲突,会等待session1的锁释放。此时,如果想要上共享锁S,同样也会冲突,会等待session1的锁释放。

  如果session1对某一行数据上了共享锁S,如果session2想对这行数据上排它锁的时候,也会是冲突的,也需要等待共享锁的释放,才可以上排它锁。但是呢,如果用session2对这行数据上共享锁的时候,它是不冲突的,两者是兼容的。   

  行级锁是不是一定比表级锁要好,也未必,锁的粒度越细代价越高,相比表级锁,在表的头部直接加锁来讲,行级锁还要扫描到m某行的时候对其上锁,这样代价是比较大的,InnoDB支持事务的同时,也相比MyISAM引擎带来了更大的开销,InnoDB有且仅有一个聚集索引的,数据文件是和索引绑到一起的,必须要有主键,通过主键索效率很高,但是辅助索引呢,需要查询两次,先查询到主键,然后再通过主键查询到数据,而MyISAM是非聚集索引,数据文件是分离的,索引保存的是数据文件的指针,主键索引和辅助索引是独立的,因此MyISAM引擎在存检索系统中,也就是增删改很少的系统中,其性能要好于InnoDB的。

4)、MyISAM和InnoDB适合的场景。

  MyISAM适合的场景,场景一,频繁执行全表count语句,对于InnoDB来讲,它是不保存表的具体行数的,执行count统计的适合需要重新扫描统计,但是MyISAM用一个变量保存了整个表的行数,执行上述语句的时候,只需要读出该变量即可,速度很快。场景二,对数据进行增删改的频率不高,查询非常频繁的时候,因为增删改会设计到锁表操作。场景三,适合没有事务的场景。
  InnoDB适合的场景,场景一,适合数据增删改查都相当频繁的场景,增删改只是某些行被锁,在大多数情况下避免了阻塞,而MyISAM对某行数据的操作都会锁住整张表。场景二,可靠性要求比较高,要求支持事务的系统。

5)、数据库锁的分类。

  5.1)、按照锁的粒度划分,可以分为表级锁、行级锁、页级锁。其中InnoDB默认支持表级锁、行级锁,InnoDB对行级上锁的时候,会先上一种级别的意向锁。MyISAM仅支持表级锁。BDB引擎支持页级锁,是一种介于表级与页级之间的锁。
  5.2)、按锁级别划分,可分为共享锁、排它锁。
  5.3)、按照加锁方式的划分,可以分为自动锁、显式锁。像意向锁、MyISAM的表级锁、以及update、insert、delete加上的锁就是自动锁,这是mysql自动加上的锁。而select for update、lock in share mode这些显式去加的锁就是显式锁。
  5.4)、按照操作划分,可以分为DML锁,DDL锁。对数据进行操作的锁就是DML锁,包括对数据的增删改查,而对表机构进行变更的如alter table加上的锁就是DDL锁。
  5.5)、按照使用方式划分,可以分为乐观锁、悲观锁

    乐观锁,认为数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据冲突与否进行检测,如果发现了冲突则返回用户错误的信息,让用户决定如何去做,相对于悲观锁,在对数据库进行处理的时候呢,乐观锁并不会使用数据库提供的锁机制,一般的是实现乐观锁的方式,记录数据的版本,实现数据版本,一种是使用版本号,一种是使用时间戳。
    悲观锁,指的是数据被外界,外界指的是本系统当前的其它事务,以及来自外部系统的事务的处理,对这些外界的处理持保守态度,因此在整个数据的处理过程中,将数据处于锁定状态,悲观锁的实现往往依靠数据库提供的锁机制,只有数据库底层提供的锁机制才能真正保证数据访问的排他性,否则即使在本系统中,实现了加锁机制,也无法保证外部系统不会修改数据。全程排它锁锁定正是一种悲观锁的实现,悲观并发控制是先取锁再访问的保守策略,为数据的处理安全提供了保证,但是在效率上方面,处理加锁的机制会让数据库产生额外的开销,还有增加死锁的机会,另外在只读性数据库处理中,由于不会产生冲突,也没有必要使用锁,如果上锁会增加系统负担同时还会降低并行性,如果一个事务锁定了某行数据,其它事务就必须等待该事务处理完,才可以去处理。

10、锁模块之数据库事务的四大特性?

答:ACID。

  1)、原子性(Atomic),指事务包含的所有操作要么全部执行,要么全部失败回滚,要么全做,要么全都不做。
  2)、一致性(Consistency),事务应确保数据库的状态从一个一致状态转变为另外一个一致的状态,一致的状态时指数据库中的数据应该满足完整性约束,例如转账,A用户和B用户两者的钱加起来是2k,A用户和B用户进行转账,不论怎么进行转账,最后A用户和B用户的钱加到一起还是2k。
  3)、隔离性(Isolation),隔离性是多个事务并发执行的时候,一个事务的执行不应该其它事务的执行,重点掌握。
  4)、持久性(Durability),一个事务一旦提交,数据库的修改应该永久保存到数据库中,持久性意味着当系统发生故障的时候,确保已提交事务的更新不能丢失,即对已提交事务的更新能恢复,一旦一个事务b被提交,DBMS必须保证提供适当的冗余,使其耐得住系统的故障,所以持久性主要在于DBMS的恢复性能。

11、锁模块之事务并发访问产生的问题以及事务隔离机制?事务隔离级别以及各级别下的并发访问问题。事务并发访问引起的问题以及如何避免,就是事务并发访问可能引起的问题,以及避免这些问题的一类或者几类。mysql会利用锁机制创建出来不同的事务隔离级别,从低到高进行学习。

  1)、更新丢失lost update,mysql所有事务隔离级别在数据库层面上均可避免。也就是一个事务的更新会覆盖另一个事务的更新,现在主流的数据库都会自动加锁避免这种更新丢失问题的发生。

  2)、脏读dirty read,read committed事务隔离级别以上可以避免。指的是一个事务读到另一个事务的未提交的更新数据,而该种问题可以在已提交读事务隔离级别以上去避免。何避免脏读带来的恶果呢,只需要把事务隔离级别成read committed即可。read committed是提交读,这个是只能读到其他事务已提交的内容,该事务隔离级别也是Oracle默认的事务隔离级别。

3)、不可重复读non repeatable read,REPEATABLE-READ事务隔离级别以上可避免。指的是事务A多次读取同一数据,而事务B在事务A多次读取的过程中对数据做了更新并提交,导致事务A多次读取同一数据的时候结果不一致。解决不可重复读的问题,是将事务隔离级别再调大一级,设置成innodb默认的事务隔离级别即REPEATABLE-READ(repeatable read)可重复读,就是支持多次重复读,能够读到相同的结果。

 

4)、幻读Phantom read,SERIALIZABLE(serializable)事务隔离级别可避免。指的是事务A读取与搜索条件相匹配的若干行,事务B以插入或者删除行等方式来修改事务A的结果集,导致事务A看起来像出现幻觉一样。如何避免幻读了,将事务隔离级别设置成最高的隔离seriatizable级别即可避免。

5)、事务并发访问引起的问题以及如何避免,总结:

  5.1)、指的注意的是,我们说的是数据库层面是如何规避这些并发访问产生的问题的。在数据库层面上,数据丢失在各个隔离级别下均是很难复现的,真正的更新丢失问题发生在应用程序中,需要在应用程序中去避免。
  5.2)、读未提交read uncommitted,会发生脏读,不可重复读,幻读这三个问题。
  5.3)、读已提交read committed,避免了脏读,但是不可避免不可重复读、幻读这两个问题。
  5.4)、可重复读repeatable read,避免了脏读、不可重复读,理论上不能避免幻读,但是通过某种巧妙的方式规避了幻读。
  5.5)、串行化serializable,避免了脏读、不可重复读、幻读。不可重复读侧重于对同一数据的修改,幻读侧重于新增或者删除。事务隔离级别越高,安全性越高,串行化执行越严重,这样就降低了数据库的并发度。根据业务需要设置事务隔离级别,mysql默认的可重复读repeatable read。

事务隔离级别 更新丢失 脏读 不可重复读 幻读

未提交读

read uncommitted

避免 发生 发生 发生

已提交读

read committed

避免 避免 发生 发生

可重复读

repeatable read

避免 避免 避免 发生

串行化

serializable

避免 避免 避免 避免

12、Group By与Having的学习。

12.1)、Group By是根据给定的数据列的每个成员,对查询结果进行分组统计,最终得到一个分组汇总表。对于这个含义有两个解释,如下所示:

  1)、满足select子句中的列名必须为分组列或者列函数。如果使用Group By,那么select语句中选出的列y要是是Group By里面用到的字段,要么就是带有sum、min、max、count、avg等等。注意,该条件只针对同一张表成立,因为如果两个表连接的时候,select后面的列可以不是group by后面的列的。
  2)、列函数对于group by自居定义的每个组各返回一个结果,可以通过explain查看Extra字段可以看到Using temporary。


12.2)、Having通常与Group By子句一起使用。

  1)、Having在和Group By子句使用的时候,我们可以应用他在Group By子句之后l来指定过滤的条件,如果省略了Group By子句,Having子句行为就像Where子句一样,Having支持所有Where操作符。
  2)、Where过滤行,Having过滤组。
  3)、出现在同一sql的顺序,where > group by > having。

13、锁模块之当前读和快照读,InnoDB可重复读隔离级别下如何避免幻读。

  1)、表象:快照读(非阻塞读),伪MVCC。表象是可以看到的现象,但并不是真正的原因,真正的原因是体现在内在的,表象是通过基于伪MVCC机制实现的快照读,即非阻塞读,来避免让我们看到幻行,前提是在repeatable read(RR)级别下。
  2)、内在:next-key锁(行锁+gap锁)。

14、当前读和快照读。

  1)、当前读,select ...lock in share mode、select ... for update、 update 、 delete  、 insert。当前读就是加了锁的增删改查语句,不管是上的共享锁还是排它锁,均为当前读,为什么叫做当前读呢,因为读取的是记录最新版本,并且读取之后还需要保证其它并发事务不能修改当前记录,对读取的记录加锁,其中除了select ...lock in share mode加的是共享锁,其它加的都是select ... for update、 update 、 delete  、 insert排它锁。为什么update 、 delete  、 insert也是当前读呢,我们了解到RDBMS主要有两大部分组成程序实例(mysql server的实例)和存储(InnoDB引擎)。
  2)、快照读,不加锁的非阻塞读,select。快照读就是简单的select操作,不加锁。不加锁的条件是在事务隔离级别不为serializable的前提下才成立的。如果是serializable事务隔离级别,由于是串行读,快照读也退化成了当前读即select ...lock in share mode模式。之所以出现快照读是为了提升并发性能的考虑,快照读的实现是基于多版本并发控制即MVCC,可以认为MVCC是行级锁的变种,但是它在很多情况下,避免了加锁操作,因此开销更低,既然是基于多版本的,也就意味着快照读有可能读到的并不是数据的最新版本,可能是之前的历史版本。
  3)、在read committed(RC)事务隔离级别下,当前读和快照读结果是一样的,都是更新过后的数据。
  4)、在repeatable read(RR)事务隔离级别下,事务首次调用的地方很关键,也就是创建快照的时机决定了数据的版本。如果是先执行快照读,当前读和快照读结果是不一样的,当前读的结果是更新后的结果,快照读是更新前的结果。如果是先执行更新操作,再执行快照读和当前读,那么快照读和当前读的结果是一致的。

15、RC、RR级别下的InnoDB的非阻塞读如何实现?

答:InnoDB要实现在read committed(RC)、repeatable read(RR)级别下的快照读离不开三个因子。

  1)、数据行里面的DB_TRX_ID、DB_ROLL_PTR、DB_ROW_ID字段。第一个因子是每行数据记录除了存储数据以外,还有额外的一些字段,最关键是这三个字段。DB_TRX_ID该字段用来标识最近一次对本行记录做修改,不管是插入还是更新,事务的标识符,即最后一次修改本行数据的事务id,至于删除操作,在Innodb引擎看来也不过是一次修改操作、DB_ROLL_PTR其中ROLL就是回滚,PTR就是指针的意思,这个是回滚指针,指的是写入回滚段roll back segment的undo日志记录,如果一行记录被更新,则undo log report包含重建该行记录b被更新之前的内容所必须的信息、DB_ROW_ID指的是行号,包含一个随着新行插入而单调递增的行id,当由innodb自动产生聚集索引的时候,聚集索引会包括这一个行id的值,否则这个行id不会出现在任何索引中。如果innodb引擎表既没有主键也没有唯一键的话,innodb会自动为我们一次创建一个自增的隐藏主键就是这里的DB_ROW_ID字段。
  2)、undo日志,当我们对记录做了变更操作的时候,就会产生undo记录,undo记录中存储的是老版本数据,当一个旧的事务需要读取数据的时候,为了可以读取到老版本的数据,需要顺着undo链找到满足的记录,undo主要分为两种,一种是Insert undo log,一种是update undo log。其中Insert undo log表示的是事务对Insert新纪录产生的undo log,只在事务回滚的时候需要,并且在事务提交的时候可以立即丢弃。update undo log指的是事务对日志进行delete或者update操作的时候产生的undo log,不仅在事务回滚的时候需要,快照读也需要,所以不可以随便删除,只要当数据库所使用的快照中不涉及该日志记录,对应的回滚日志才会被线程删除掉。
  3)、read view。主要是用来做可见性判断的,即当我们去执行快照读select的时候会针对我们查询的数据创建出一个read view,来决定当前事务能看到的是那个版本的数据,有可能是当前最新版本的数据,也有可能是只允许你看undo Log里面某个版本的数据。read view遵循一个可见性算法,主要是将要修改的数据的DB_TRX_ID取出来,与系统其它活跃的事务id做比较,如果大于或者等于这些id的话,就通过DB_ROLL_PTR指针去取出undo log,直到小于这些活跃事务id为止。这样就保证了当前获取到的版本是活跃的最稳定的版本。

猜你喜欢

转载自www.cnblogs.com/biehongli/p/12565020.html