mysql 知识点汇总

1. 使用 mysql 索引都有哪些原则?

  1. 只对WHERE和ORDER BY需要查询的字段设置索引,避免无意义的硬盘开销;
  2. 组合索引支持前缀索引;
  3. 更新表的时候,如增删记录,MySQL会自动更新索引,保持树的平衡;因此更多的索引意味着更多的维护成本

索引分类

  • index ----普通的索引,数据可以重复
  • fulltext----全文索引,用来对大表的文本域(char,varchar,text)进行索引。语法和普通索引一样。
  • unique ----唯一索引,唯一索引,要求所有记录都唯一
  • primary key ----主键索引,也就是在唯一索引的基础上相应的列必须为主键

原则

  1. 单表数据太少,索引反而会影响速度;更新非常频繁的数据不适宜建索引
  2. where后的条件,order by ,group by 等这样过滤时,后面的字段最好加上索引。根据实际情况,选择PRIMARY KEY、UNIQUE、INDEX等索引,但是不是越多越好,要适度
  3. 联合查询,子查询等多表操作时关连字段要加索引

2. 索引什么数据结构?

MyISAM存储引擎文件有三个

  • .frm 定义文件
  • .MYD 数据文件
  • .MYI 索引文件

InnoDB存储引擎文件有二个

  • .frm表的定义文件
  • .idb是数据文件

3. B+tree 和 B tree 什么区别?

MyISAM和InnoDB存储引擎简述

4. mysql 有哪些存储引擎啊?都有啥区别?

MyISAM和InnoDB存储引擎简述

5. 设计高并发系统数据库层面该怎么设计?

  1. 分表
    比如秒杀场景,并发的任务都需要从数据库获取库存;单表数据库就会成为瓶颈:做法就是分表
    把一个表的数据放到多个表中,然后查询的时候你就查一个表。

比如秒杀商品分到16个表中每次请求根据请求id进行hash之后取余,然后分到一个具体的表中更新当前的表;

比如按照用户 id 来分表,将一个用户的数据就放在一个表中。然后操作的时候你对一个用户就操作那个表就好了。这样可以控制每个表的数据量在可控的范围内,比如每个表就固定在 200 万以内。

  1. 分库
    一个健康的单库并发值你最好保持在每秒 1000 左右,不要太大。那么你可以将一个库的数据拆分到多个库中,访问的时候就访问一个库好了。如果存在join可能需要中间件处理。

6. 数据库锁有哪些类型?如何实现?

从数据库系统角度分为三种:排他锁、共享锁、更新锁。

SELECT ... LOCK IN SHARE MODE;
SELECT ... FOR UPDATE;

从程序员角度分为两种:一种是悲观锁,一种乐观锁。

悲观锁

关系数据库里用到了很多这种锁机制,比如行锁、表锁、读锁、写锁等,都是在操作之前先上锁。

  • 行锁
    update … where id=?

这样的语句时,数据库明确知道会影响哪一行,它就会使用行锁;

  • 表锁
    update … where birthday=?

因为事先不知道会影响哪些行就可能会使用表锁。
SET LOCK_TIMEOUT 4000 用来设置锁等待时间,单位是毫秒,4000意味着等待
4秒可以用select @@LOCK_TIMEOUT查看当前session的锁超时设置。-1 意味着
永远等待。

乐观锁

  • 版本号

就是给数据增加一个版本标识,在数据库上就是表中增加一个version字段,每次更新把这个字段加1,读取数据的时候把version读出来,更新的时候比较version,如果还是开始读取的version就可以更新了,如果现在的version比老的version大,说明有其他事务更新了该数据,并增加了版本号,这时候得到一个无法更新的通知,用户自行根据这个通知来决定怎么处理,比如重新开始一遍。这里的关键是判断version和更新两个动作需要作为一个原子单元执行,否则在你判断可以更新以后正式更新之前有别的事务修改了version,这个时候你再去更新就可能会覆盖前一个事务做的更新,造成第二类丢失更新,所以你可以使用update … where … and version=”old version”这样的语句,根据返回结果是0还是非0来得到通知,如果是0说明更新没有成功,因为version被改了,如果返回非0说明更新成功。

7.数据库常用的事务隔离级别都有哪些?都是什么原理?

具备四个特性,分别是:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability),也就是我们常说的事务ACID,这样才能保证事务((Transaction)中数据的正确性。

  • 隔离级别
    • 读未提交(Read Uncommitted)
      可能会产生“脏读”、“不可重复读”、“幻读”

    • 读提交(Read Committed)

          select * from T where ID=2 lock in share mode;  
          select * from T where ID=2 for update;  
      

      不然,普通的查询是不会加锁的。

      为什么“读提交”同“读未提交”一样,都没有查询加锁,但是却能够避免脏读呢?

      “快照(snapshot)”,而这种既能保证一致性又不加锁的读也被称为“快照读(Snapshot Read)”
      假设没有“快照读”,那么当一个更新的事务没有提交时,另一个对更新数据进行查询的事务会因为无法查询而被阻塞,这种情况下,并发能力就相当的差。而“快照读”就可以完成高并发的查询,不过,“读提交”只能避免“脏读”,并不能避免“不可重复读”和“幻读”。

    • 可重复读(Repeated Read)
      可重复读,顾名思义,就是专门针对“不可重复读”这种情况而制定的隔离级别,自然,它就可以有效的避免“不可重复读”。而它也是MySql的默认隔离级别。在这个级别下,普通的查询同样是使用的“快照读”,但是,和“读提交”不同的是,当事务启动时,就不允许进行“修改操作(Update)”了,而“不可重复读”恰恰是因为两次读取之间进行了数据的修改,因此,“可重复读”能够有效的避免“不可重复读”,但却避免不了“幻读”,因为幻读是由于“插入或者删除操作(Insert or Delete)”而产生的。

    • 串行化(Serializable)
      这是数据库的隔离级别,这种级别下,事务“串行化顺序执行”,也就是一个一个排队执行。这种级别下,“脏读”、“不可重复读”、“幻读”都可以被避免,但是执行效率奇差,性能开销也,所以基本没人会用。

8.如何设计可以动态扩容缩容的分库分表方案?

  • 停机扩容
    停机迁移,然后重启
  • 基于日志追平
    写入的时候同时写入两份;保存日志;校验新库并且在校验之后,根据旧库这段时间的日志追平新库,短时间停机即可

9.分库分表中间件,有啥优点和缺点,分库分表中间件的底层实现原理?

  • 分布式事务问题
    如果我们做了垂直分库或者水平分库以后,就必然会涉及到跨库执行SQL的问题,这样就引发了互联网界的老大难问题-"分布式事务"。那要如何解决这个问题呢?
    1.使用分布式事务中间件 2.使用MySQL自带的针对跨库的事务一致性方案(XA),不过性能要比单库的慢10倍左右。3.能否避免掉跨库操作(比如将用户和商品放在同一个库中)

  • 跨库join的问题
    分库分表后表之间的关联操作将受到限制,我们无法join位于不同分库的表,也无法join分表粒度不同的表, 结果原本一次查询能够完成的业务,可能需要多次查询才能完成。粗略的解决方法: 全局表:基础数据,所有库都拷贝一份。 字段冗余:这样有些字段就不用join去查询了。 系统层组装:分别查询出所有,然后组装起来,较复杂。

  • 横向扩容的问题
    当我们使用HASH取模做分表的时候,针对数据量的递增,可能需要动态的增加表,此时就需要考虑因为reHash导致数据迁移的问题。

  • 结果集合并、排序的问题
    因为我们是将数据分散存储到不同的库、表里的,当我们查询指定数据列表时,数据来源于不同的子库或者子表,就必然会引发结果集合并、排序的问题。如果每次查询都需要排序、合并等操作,性能肯定会受非常大的影响。走缓存可能一条路!

拦截了用户发送过来的SQL语句,首先对SQL语句做了一些特定的分析:如分片分析、路由分析、读写分离分析、缓存分析等,然后将此SQL发往后端的真实数据库,并将返回的结果做适当的处理,最终再返回给用户。

10. 分布式事务怎么解决的? TCC?那若出现网络原因,网络连不通怎么办?

大规模SOA系统中的分布事务处事_程立

TCC 将事务提交分为 Try - Confirm - Cancel 3个操作。
Try:预留业务资源/数据效验
Confirm:确认执行业务操作
Cancel:取消执行业务操作

TCC 优缺点

  • TCC优点:让应用自己定义数据库操作的粒度,使得降低锁冲突、提高吞吐量成为可能。

  • TCC不足之处:
    对应用的侵入性强。业务逻辑的每个分支都需要实现try、confirm、cancel三个操作,应用侵入性较强,改造成本高。
    实现难度较大。需要按照网络状态、系统故障等不同的失败原因实现不同的回滚策略。为了满足一致性的要求,confirm和cancel接口必须实现幂等。
    我们通过用户下单使用余额+红包支付来看一下TCC事务的具体应用。

假设用户下单操作来自3个系统下单系统、资金账户系统、红包账户系统,下单成功需要同时调用资金账户服务和红包服务完成支付
假设购买商品1000元,使用账户红包200元,余额800元,确认支付。

Try操作
tryX 下单系统创建待支付订单
tryY 冻结账户红包200元
tryZ 冻结资金账户800元
Confirm操作
confirmX 订单更新为支付成功
confirmY 扣减账户红包200元
confirmZ 扣减资金账户800元
Cancel操作
cancelX 订单处理异常,资金红包退回,订单支付失败
cancelY 冻结红包失败,账户余额退回,订单支付失败
cancelZ 冻结余额失败,账户红包退回,订单支付失败

11.分布式寻址方式都有哪些算法?一致性hash

分布式均匀算法

  • hash 算法(大量缓存重建)
    首先计算key的 hash 值,然后对节点数取模。然后打在不同的 master 节点上。一旦某一个 master 节点宕机,所有请求过来,都会基于最新的剩余 master 节点数去取模,尝试去取数据。这会导致大部分的请求过来,全部无法拿到有效的缓存,导致大量的流量涌入数据库。

  • 一致性 hash 算法(自动缓存迁移)+ 虚拟节点(自动负载均衡)
    一致性 hash 算法将整个 hash 值空间组织成一个虚拟的圆环,整个空间按顺时针方向组织,下一步将各个 master 节点(使用服务器的 ip 或主机名)进行 hash。这样就能确定每个节点在其哈希环上的位置。来了一个 key,首先计算 hash 值,并确定此数据在环上的位置,从此位置沿环顺时针“行走”,遇到的第一个 master 节点就是 key 所在位置。
    在一致性哈希算法中,如果一个节点挂了,受影响的数据仅仅是此节点到环空间前一个节点(沿着逆时针方向行走遇到的第一个节点)之间的数据,其它不受影响。增加一个节点也同理。


    mysql 知识点汇总_第1张图片
    image
  • redis cluster 的 hash slot 算法
    redis cluster 有固定的 16384 个 hash slot,对每个 key 计算 CRC16 值,然后对 16384 取模,可以获取 key 对应的 hash slot。每个节点负责维护一部分槽以及槽所映射的键值数据。
    redis cluster 中每个 master 都会持有部分 slot,比如有 3 个 master,那么可能每个 master 持有 5000 多个 hash slot。hash slot 让 node 的增加和移除很简单,增加一个 master,就将其他 master 的 hash slot 移动部分过去,减少一个 master,就将它的 hash slot 移动到其他 master 上去。移动 hash slot 的成本是非常低的。客户端的 api,可以对指定的数据,让他们走同一个 hash slot,通过 hash tag 来实现。


    mysql 知识点汇总_第2张图片
    [图片上传中...(image-f10d63-1576742673973)]

    mysql 知识点汇总_第3张图片
    image

一致性hash的golang实现 这种实现不是简单的分段,增加了replica的设置,将每个节点的槽位打散;比如可能是0-100 node1 100-200node2 200-300node3,300-400node2,400-500node1,500-600node3;原算法在remove一个node之后,影响的是后面的节点,那么后面的节点压力就会变大;改进之后理论上所有的节点平分了这个压力。

如何解决分库分表主键问题?有什么实现方案?

  • Twitter的Snowflake(又名“雪花算法”)
  • UUID/GUID(一般应用程序和数据库均支持)
  • MongoDB ObjectID(类似UUID的方式)
  • Ticket Server(数据库生存方式,Flickr采用的就是这种方式)

11. 跨分片技术问题

跨分片的排序分页

[图片上传失败...(image-863348-1576743217496)]
当排序字段就是分片字段的时候,我们通过分片规则可以比较容易定位到指定的分片,而当排序字段非分片字段的时候,情况就会变得比较复杂了。为了最终结果的准确性,我们需要在不同的分片节点中将数据进行排序并返回,并将不同分片返回的结果集进行汇总和再次排序,最后再返回给用户。


mysql 知识点汇总_第4张图片
p.png

上面图中所描述的只是最简单的一种情况(取第一页数据),看起来对性能的影响并不大。但是,如果想取出第10页数据,情况又将变得复杂很多,如下图所示:

mysql 知识点汇总_第5张图片
image

有些读者可能并不太理解,为什么不能像获取第一页数据那样简单处理(排序取出前10条再合并、排序)。其实并不难理解,因为各分片节点中的数据可能是随机的,为了排序的准确性,必须把所有分片节点的前N页数据都排序好后做合并,最后再进行整体的排序。很显然,这样的操作是比较消耗资源的,用户越往后翻页,系统性能将会越差。

跨分片的函数处理

在使用Max、Min、Sum、Count之类的函数进行统计和计算的时候,需要先在每个分片数据源上执行相应的函数处理,然后再将各个结果集进行二次处理,最终再将处理结果返回。如下图所示:


mysql 知识点汇总_第6张图片
image

跨分片join

Join是关系型数据库中最常用的特性,但是在分片集群中,join也变得非常复杂。应该尽量避免跨分片的join查询(这种场景,比上面的跨分片分页更加复杂,而且对性能的影响很大)。通常有以下几种方式来避免:

全局表

全局表的概念之前在“垂直分库”时提过。基本思想一致,就是把一些类似数据字典又可能会产生join查询的表信息放到各分片中,从而避免跨分片的join。

ER分片

在关系型数据库中,表之间往往存在一些关联的关系。如果我们可以先确定好关联关系,并将那些存在关联关系的表记录存放在同一个分片上,那么就能很好的避免跨分片join问题。在一对多关系的情况下,我们通常会选择按照数据较多的那一方进行拆分。如下图所示:


mysql 知识点汇总_第7张图片
image

这样一来,Data Node1上面的订单表与订单详细表就可以直接关联,进行局部的join查询了,Data Node2上也一样。基于ER分片的这种方式,能够有效避免大多数业务场景中的跨分片join问题。

内存计算

随着spark内存计算的兴起,理论上来讲,很多跨数据源的操作问题看起来似乎都能够得到解决。可以将数据丢给spark集群进行内存计算,最后将计算结果返回。

跨分片事务问题

跨分片事务也分布式事务,想要了解分布式事务,就需要了解“XA接口”和“两阶段提交”。值得提到的是,MySQL5.5x和5.6x中的xa支持是存在问题的,会导致主从数据不一致。直到5.7x版本中才得到修复。Java应用程序可以采用Atomikos框架来实现XA事务(J2EE中JTA)。感兴趣的读者可以自行参考《分布式事务一致性解决方案》,链接地址:
http://www.infoq.com/cn/articles/solution-of-distributed-system-transaction-consistency

你可能感兴趣的:(mysql 知识点汇总)