Mysql的介绍
【1】MySQL是一个轻量级关系型数据库管理系统,将数据保存在不同的表中,而不是将所有数据放在一个大仓库内,就增加了速度并提高了灵活性。
【2】sql语言分类:
【3】索引分类
1)聚簇索引(又称为主键索引,本质上数据是存储在叶子节点上)
2)非二级索引(又称为二级索引,本质上叶子节点只存储数据的id,需要进行回表获得数据)
【1】类型
(1)NORMAL(普通索引,也是最常用的)
(2)FULLTEXT(全文索引)
(3)SPATIAL(空间索引)
(4)UNIQUE(唯一索引)
【2】方法
(1)BTREE(也就是B+Tree)
(2)HASH(也就是hash table结构)
【3】字段个数
(1)单个
(2)多个(组合索引又称为复合索引)
Mysql的列的数据类型详解
【1】数值
【2】字符串
【3】时间和日期
【4】null,没有值【注意: 使用NULL进行运算,结果为NULL】
Mysql的存储引擎【 存储引擎生效的单位是表 】
【1】展示
【2】MyISAM存储引擎
【2.1】文件说明
【2.2】图示
【3】InnoDB存储引擎
【3.1】文件说明
【3.2】图示
【3.3】为什么建议InnoDB表必须建主键,并且推荐使用整型的自增主键?
1)首先如果不建立的话,它会从数据列中找出全部不同的一列作为主键,如果找不到会创建一个隐藏列作为主键。那么既然会有隐藏列列了,干脆直接创建就好。
2)如果 使用UUID作为主键,首先,UUID不易于存储,16字节128位,通常以36长度的字符串表示,很多场景不适用。其次新行的主键值不一定比之前的主键值大,所以innoDb无法做到总是把新行插入到索引的最后,而需要为新行寻找合适的位置来分配新的空间,这个过程会导致:
3)使用自增主键则可以避免上述问题:
【3.4】非主键索引结构叶子节点存储的是主键值,主要是为了一致性和节省存储空间。所以相对而言,聚集索引会比非聚集所以要快一点,毕竟不用跨文件查找。
常用函数记录
【1】数据函数
【2】字符串函数
【3】日期和时间函数
【4】聚合函数
Mysql索引底层数据结构
【1】 索引的本质:索引是帮助MySQL 高效获取数据的排好序的数据结构。
【2】索引数据结构:在我们创建索引的时候会给予我们两个选择,BTREE【这里指的是b+tree】与HASH。
【2.1】B+Tree结构
1)说明
2)分析能存储的数据量
【1】页是 InnoDB 管理的最小单位,常见的有 FSP_HDR,INODE, INDEX 等类型。页结构分为文件头(前38字节),页数据和文件尾(后8字节)。每个数据页大小为16kb,查看方法为
【2】系统从磁盘中读取数据到内存时是以磁盘块(block)为基本单位(4kb【这个与操作系统有关】),位于同一个磁盘块中的数据会被一次性读取出来。block大小空间往往没有16kb大,因此innodb每次io操作时都会将若干个地址连续的磁盘块的数据读入内存,从而实现整页读入内存。
【3】如果索引字段为bigint,它会占据8B,而地址指针【指向下一个磁盘块文件地址】占据的是6B。所以此时,一般能存1170个左右的数据【 这个数值其实是没有减去数据页的一些其他数据的,只是假设全部用于存储数据】。如果非叶子节点是两层的话,那么就会是136万的数据量。假设叶子结点存储的数据是1K,那么可以存储16个,数据量便会是2176万的数据。
【4】而且Mysql的索引的根结点是常驻内存的。
3)图示
【2.2】hash结构
1)说明
2)图示
【3】对于那么多的数据结构,如链表,数组,二叉树,红黑树(平衡二叉树),B-tree(多路平衡二叉树)为什么都不选择?
【3.1】首先对于链表与数组,它们自身的缺点很明显,数组查询快但是遇到向中间插入数据的情况会涉及到大量的数据迁移,而链表涉及的数据迁移几乎没有但是查询效率在大数据量下其实很慢。故是不合适的。
【3.2】对于二叉树,在极端情况下,它是会存在退化为链表结构的。所以才会有平衡二叉树的存在,但是平衡二叉树也会存在问题,就是数据量大了之后层级会很多,也不能很好的利用磁盘块的理念,所以才会出现多路平衡二叉树。
【3.3】那么为什么B-tree也不会被选择呢?
1)说明
2)图示
3)理由
【1】层级依旧会出现很大的情况,如果数据量是1k,那么地址指针的大小先不算,一个数据页最多能容纳16个数据,两千多万的数据便需要7层,远远大于B+Tree的层级。一次load节点是一次磁盘IO,是非常慢的,但是我们把它load到内存中之后在你内存里随机的找某一个元素是非常快的,跟一次磁盘IO这个时间消耗去比对的话几乎可以忽略不计。故层级越多涉及的磁盘IO也就越多。
【2】其次是不好优化,因为BTree中,数据都在节点上,必然会出现层级不一的情况,快慢便是取决于你所在的层级。而B+Tree要获取数据需要到对应的叶子结点上,保证了经历的层级数是相同的。
【3】最后较为明显的是BTree的叶子节点是没有串联起来的,所以进行不了从头开始的全表扫描,都是要从头节点开始找下去,这样效率会低很多。
【4】联合索引的原理
1)说明
2)图示
Mysql的锁机制
【1】锁分类
【2】对锁类型的分析【 本质上怎么说,这些基本不用手动加,数据库的引擎会自动加】
【2.1】 读锁(共享锁,S锁(Shared)):针对同一份数据,多个读操作可以同时进行而不会互相影响,比如:select * from T where id=1 lock in share mode;
【2.2】 写锁(排它锁,X锁(eXclusive)):当前写操作没有完成前,它会阻断其他写锁和读锁,数据修改操作都会加写锁,查询也可以通过for update加写锁,比如:select * from T where id=1 for update;
【2.3】 意向锁(Intention Lock):又称I锁,针对表锁,主要是为了提高加表锁的效率,是mysql数据库自己加的。当有事务给表的数据行加了共享锁或排他锁,同时会给表设置一个标识,代表已经有行锁了,其他事务要想对表加表锁时,就不必逐行判断有没有行锁可能跟表锁冲突了,直接读这个标识就可以确定自己该不该加表锁。特别是表中的记录很多时,逐行判断加表锁的方式效率很低。而这个标识就是意向锁。
【3】对锁粒度的分析
【3.1】 表锁:每次操作锁住整张表。 开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低;一般用在整表数据迁移的场景。
【3.1.1】基本操作
【3.1.2】总结
【3.2】 行锁:每次操作锁住一行数据。 开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度最高。
【3.2.1】一个session开启事务更新不提交,另一个session更新同一条记录会阻塞,更新不同记录不会阻塞
【3.2.2】InnoDB与MYISAM的最大不同有两点:
【3.3】总结:
Mysql的事务机制
【1】事务的ACID特性
【2】并发事务处理带来的问题
一个事务正在对一条记录做修改,在这个事务完成并提交前,这条记录的数据就处于不一致的状态;这时,另一个事务也来读取同一条记录,如果不加控制,第二个事务读取了这些”脏”数据,并据此作进一步的处理,就会产生未提交的数据依赖关系。这种现象被形象的叫做”脏读”。
总结来说: 事务A读取到了事务B已经修改但尚未提交的数据,还在这个数据基础上做了操作。此时,如果B事务回滚,A读取的数据无效,不符合一致性要求。
一个事务在读取某些数据后的某个时间,再次读取以前读过的数据,却发现其读出的数据已经发生了改变、或某些记录已经被删除了!这种现象就叫做”不可重复读”。
总结来说: 事务A内部的相同查询语句在不同时刻读出的结果不一致,不符合隔离性
一个事务按相同的查询条件重新读取以前检索过的数据,却发现其他事务插入了满足其查询条件的新数据,这种现象就称为”幻读”。
总结来说: 事务A读取到了事务B提交的新增数据,不符合隔离性。
【3】事务隔离级别
【4】事务隔离级别注意事项
MVCC多版本并发控制机制
【1】Mysql在 读已提交和 可重复读隔离级别下都实现了MVCC机制。(至于 为什么要实现)
【2】因为隔离性就是靠MVCC(Multi-Version Concurrency Control)机制来保证的,对一行数据的读和写两个操作默认是不会通过加锁互斥来保证隔离性,避免了频繁加锁互斥,而在串行化隔离级别为了保证较高的隔离性是通过将所有操作加锁互斥来实现的。
【3】实现这个机制主要是依赖 undo日志版本链与 read view机制:
【4】undo日志版本链是指一行数据被多个事务依次修改过后,在每个事务修改完后,Mysql会保留修改前的数据undo回滚日志,并且用两个隐藏字段trx_id和roll_pointer把这些undo日志串联起来形成一个历史记录版本链。
【5】当事务开启,执行任何查询sql时会生成当前事务的 一致性视图read-view,该视图在事务结束之前都不会变化【这里 指在可重复读隔离级别下】(如果是 读已提交隔离级别 在每次执行查询sql时都会重新生成),这个视图由执行查询时所有未提交事务id数组(数组里最小的id为min_id)和已创建的最大事务id(max_id)组成,事务里的任何sql查询结果需要从对应版本链里的最新数据开始逐条跟read-view做比对从而得到最终的快照结果。
【6】图示:
【7】版本链比对规则:
【8】对于删除的情况可以认为是update的特殊情况,会将版本链上最新的数据复制一份,然后将trx_id修改成删除操作的trx_id,同时在该条记录的头信息(record header)里的(deleted_flag)标记位写上true,来表示当前记录已经被删除,在查询时按照上面的规则查到对应的记录如果delete_flag标记位为true,意味着记录已被删除,则不返回数据。
【 注意】begin/start transaction 命令并不是一个事务的起点,在执行到它们之后的第一个修改操作InnoDB表的语句,事务才真正启动,才会向mysql申请事务id,mysql内部是严格按照事务的启动顺序来分配事务id的。
Innodb引擎SQL执行的BufferPool缓存机制
【1】图示
【2】说明( 为什么Mysql不能直接更新磁盘上的数据而设置这么一套复杂的机制来执行SQL?)
1)如果来一个请求就直接对磁盘文件进行随机读写,然后更新磁盘文件里的数据,这样的效率是很低的。因为磁盘随机读写的性能是非常差的,所以直接更新磁盘文件是不能让数据库抗住很高并发的。
2)Mysql这套机制看起来复杂,但它可以保证每个更新请求都是更新内存BufferPool,然后顺序写日志文件,同时还能保证各种异常情况下的数据一致性(undoLog针对数据进行恢复,RedoLog对数据进行重做操作)。
3)更新内存的性能是极高的,然后顺序写磁盘上的日志文件的性能也是非常高的,要远高于随机读写磁盘文件。
4)正是通过这套机制,才能让我们的MySQL数据库在较高配置的机器上每秒可以抗下几干甚至上万的读写请求。
Original: https://www.cnblogs.com/chafry/p/16854603.html
Author: 忧愁的chafry
Title: Mysql详解
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/712210/
转载文章受原作者版权保护。转载请注明原作者出处!