Toc
  1. 先抛出几个问题
  2. 索引
    1. 根据以上描述我们可以得到以下信息:
  3. 存储格式
  • 未完待续。。
  • 0 results found
    liang
    mysql笔记
    2019/07/17 数据库 mysql

    先抛出几个问题

    • 1.为什么不建议使用订单号作为主键?
    • 2.为什么要在需要排序的字段上加上索引?
    • 3.for update的记录不存在会导致锁住全表?
    • 4.redolog和binlog有什么区别?
    • 5.mysql如何回滚一条sql?
    • 6.char(50)和varchar(50)效果是一样的吗?

    索引

    对于 MySQL 数据库而言,数据是存储在文件里的,而为了能够快速定位到某张表里的某条记录进行查询和修改,我们需要将这些数据以一定的数据结构进行存储,这个数据结构就是我们说的索引。回忆一下我们大学里学过的算法与数据结构,能够支持快速查找的数据结构有:顺序数组、哈希、搜索树。

    数组要求 insert 的时候保证有序,这样查找的时候可以利用二分查找法达到 O(log(N)) 的时间复杂度,对范围查询支持也很好,但是 insert 的时候如果不是在数组尾部,就需要摞动后面所有的数据,时间复杂度为 O(N) 。所以有序数组只适合存储静态数据,例如几乎很少变动的配置数据,或者是历史数据。这里应该会有人有疑问:我用另外一种线性数据结构链表来替代数组不就可以解决数组因为要移动数据导致太慢的问题了么,要回答这个问题我们需要了解操作系统读取文件的流程,磁盘 IO 是一个相对很慢的操作,为了提高读取速度,我们应该尽量减少磁盘 IO 操作,而操作系统一般以 4kb 为一个数据页读取数据,而 MySQL 一般为 16kb 作为一个数据块,已经读取的数据块会在内存进行缓存,如果多次数据读取在同一个数据块,则只需要一次磁盘 IO ,而如果顺序一致的记录在文件中也是顺序存储的,就可以一次读取多个数据块,这样范围查询的速度也可以大大提升,显然链表没有这方面的优势。

    类似于 jdk 中的 hashmap ,哈希表通过一个特定的哈希函数将 key 值转换为一个固定的地址,然后将对应的 value 放到这个位置,如果发生哈希碰撞就在这个位置拉出一个链表,由于哈希函数的离散特性,所以经过哈希函数处理后的 key 将失去原有的顺序,所以哈希结构的索引无法满足范围查询,只适合等值查询的情况例如一些缓存的场景。

    二叉树在极端情况下会变成线性结构,也就是每个节点都只有左子节点或者只有右子节点,这样就无法利用二分查找只能从第一个节点开始向后遍历了,所以为了维持 O(log(N)) 的时间复杂度,我们需要在插入节点的时候对节点进行调整以保证树的平衡,所以平衡二叉树插入的时间复杂度也是 O(log(N)) ,二叉树只有两个子节点,如果数据量很大则树就很高,树的每一层一般不在同一个数据块中存储,为了尽量的减少磁盘读写次数,我们用N叉树来代替二叉树,在 MySQL 中这个N一般为 1200 ,这样树高是 4 的话也可以存储亿级别的数据,而且树的前面两层一般都在内存中, MySQL 中用到的 B+ 树,一般用非叶子节点构建索引,而叶子节点用来存储具体的值。

    InnoDB 中,有聚簇索引和普通索引之分,聚簇索引根据主键来构建,叶子节点存放的是该主键对应的这一行记录,而普通索引根据申明这个索引时候的列来构建,叶子节点存放的是这一行记录对应的主键的值,而普通索引中还有唯一索引和联合索引两个特例,唯一索引在插入和修改的时候会校验该索引对应的列的值是否已经存在,而联合索引将两个列的值按照申明时候的顺序进行拼接后在构建索引。

    根据以上描述我们可以得到以下信息:

    数据是以行为单位存储在聚簇索引里的,根据主键查询可以直接利用聚簇索引定位到所在记录,根据普通索引查询需要先在普通索引上找到对应的主键的值,然后根据主键值去聚簇索引上查找记录,俗称回表。

    普通索引上存储的值是主键的值,如果主键是一个很长的字符串并且建了很多普通索引,将造成普通索引占有很大的物理空间,这也是为什么建议使用 自增ID 来替代订单号作为主键,另一个原因是 自增ID 在 insert 的时候可以保证相邻的两条记录可能在同一个数据块,而订单号的连续性在设计上可能没有自增ID好,导致连续插入可能在多个数据块,增加了磁盘读写次数。

    存储格式

    未完待续。。

    打赏
    支付宝
    微信
    本文作者:liang
    版权声明:本文首发于liang的博客,转载请注明出处!