谈谈对msyql的理解有哪些(24.3.31版)
你对 mysql 理解有哪些?
对于mysql的介绍
mysql 是一款开源的关系型数据库,遵循 sql 规范,如今在很多大厂都在应用,和 mysql 同类型的数据库还有 oracle,sql server等,但是后面两个都需要收费,所以一直被 mysql 力压一头。
对于ACID 的理解
- (ACID)mysql 最大的特征就是支持事务操作,保证了 ACID 四大特性,对事务操作的四大特性,即原子性,隔离型,持久性和一致性,原子性指事务内的操作要么全部成功,要么全部失败;隔离性是指事务之间的隔离机制,即便在并发环境下,也能够保证运行;持久性,指的是事务一旦提交,就永久修改了,这就要求数据库做好一些应对,来保证数据一旦提交就不会因为外部原因而出现问题;最后一致性,是指事务完成时,所有的数据都保持一致的状态。
对于 ACD 的介绍
- (ACD)那么,这四大特性,在mysql中是如何保证的呢?我们一一分析,原子性和隔离性可以放在一块说,这里底层用到了undo log日志,所谓undo,就是事务操作的反过程,比如某个事务增加了一个数据,那么在undo log文件中,就记录了一个删除操作,那么事务一旦失败,就可以从undo log日志中找到那条反操作sql,将数据状态恢复到最开始的状态。持久性呢,可以由底层的redo log日志文件来保证,所谓redo,就是再做一遍,在事务发生时,先记录这条sql日志,将其记录到redo log文件中,再将其写入数据库,一旦发生宕机,那么来不及写入的数据,或者还在缓存中没来及得刷新到磁盘的数据,都可以借助redo log文件来进行恢复。最后隔离性,这个东西就比较复杂了,涉及到了mysql底层的mvcc模型,即多版本并发控制。
对于隔离性 isolution 的理解
- (I)并发事务下,会带来三个问题,即脏读、不可重复读、幻读。脏读,就是读取到了其他事务还未提交的数据;不可重复读,就是读到了该事务在事务过程中,先前读到的数据和之后读到的数据不一致,也就是在该事务操作过程中,有其他事务也修改了这条数据;最后是幻读,顾名思义,就是在一个事务中,查询时没有查到数据,但是后来插入时,又发现该数据已经存在了,和不可重复读最大的区别就是,这是对新数据而非已经存在的数据。
对于mysql隔离级别的介绍
- (隔离级别)针对于这样的问题,我们针对地提出了四个解决方案,即读未提交、读已提交、可重复读、序列化四种解决方案。mysql默认支持可重复读,而oracle默认支持读已提交。这四种方案,依次解决了0个、1个、2个、3个问题,其中读未提交啥也没有解决,读已提交解决了脏读,不会读到别人没有提交的数据,而可重复读的隔离级别,解决了不可重复读问题,也就是在读某条数据操作时,其他事务可以读,但是不能修改。最后一个是序列化,解决了幻读问题,但是这种方式最为低效,在某个事务发生时,其他事务不能进行操作,变成了串行操作,失去了并发性,实际开发中基本不用,因为很多时候,用不到这么高的隔离级别。
对于 mvcc 的基本介绍
- mvcc版本控制,也就是多版本并发控制,针对于当前读的操作,在这个过程中,利用到了版本链和undo logi日志,每条数据都有一个特殊的字段,记录了前一个修改操作的记录,这个记录被放在了undo log日志中,通过指针的方式进行链接。都记录上有一些字段,记录了这条记录是那个事务进行操作的。当一个快照读发生时,首先会生成一个读视图,里面记录了当前对于这行数据操作的事务最大事务id、最小事务id、操作的事务ids,然后,再沿着版本链进行判断,找到可以读的版本,并获取里面的数据。读未提交RC和可重复读RR最大的区别在于前者每次读操作都会生成一个读视图,而后者只会生成一次,也就是说,即便后来有事务提交了,也不会刷新读视图,被那个事务监测到,就保证了前后读到的数据一致,可重复读的场景就解决了。
对于索引的介绍
mysql 还支持索引,在mysql底层中,用到了B+树作为存储结构,所谓建立索引,就是一个根据某个字段所建立的B+树结构罢了,那么又为什么选择B+树呢,主要是在存储过程中,B+树的优势很大,它的分支结点不存储数据,而叶子阶段存储数据,那么在存储分支节点时,一个页就可以存储更多的数据了,那么我们在进行检索的时候,和磁盘进行IO更少的情况下,就可以实现找到要查找的页在那个位置了,随机再取出叶子节点进行各种操作了。除此之外,叶子节点之间使用了双向链表进行了连接,很适合那种范围查询,在mysql中用的比较多。
索引分为聚簇索引和非聚簇索引,前者是必须的,每次建表不管是否指定,都会生成。如果不指定,则会从表字段中选择一个合适的字段作为主键,生成聚簇索引,如果都不合适,就用行的隐藏字段——rowId 作为主键进行排序。由于默认有聚簇索引,因此我们使用主键作为where查询条件进行查询会有很好的性能,但是特殊情况下,使用主键不够用,就需要建立二级索引,即非聚簇索引了。二级索引结构独立于前一个B+树,是再新开辟一个空间进行存储,通过指定索引字段,可以是多个字段,也可以是单个字段,然后根据它们进行索引树的建立,需要注意的是,虽然这个索引树的分支节点也不存储数据,但是叶子也并没有存储全部的数据字段,只是存储了该字段对应的主键,所以想要获取全部数据,需要进行回表查询,多耗费一点性能。
索引的建立,对于存储和性能都是有消耗的。比如每次新添加一条数据,不仅要在聚簇索引上添加,还需要在所有的二级索引上也添加上数据,如果索引较多,那么插入的效率是很低的,还会影响查询效率。
我们可以使用联合索引的方式,来进行优化,对于一条数据,我们预先和后端同学沟通好,那么字段查询时要作为条件,然后根据其先后顺序,构建一条索引来,大大减少了索引数量。同时,联合索引还有一个好处,如果说我们要查询到数据在联合索引中都有,那么在查询时,只需要走一遍联合索引就可以了,不需要进行回表查询,提高效率。
但是很多时候,索引的建立是必须的,也面临着大量数据的查询,我们可以怎么做呢?我们可以进行主从复制操作,搭建主从msyql集群。
mysql 主从集群
mysql 主从集群的原理,就是主数据库负责所有的写操作,而从数据库负责读操作业务,所有的读操作来,主数据库先进行添加,然后以固定时间将插入的数据以binlog文件的形式发送给从数据库 ,从数据库读取这个binlog文件,记录到自己的缓冲文件中,等到何时的时机,再进行数据的同步。
分库分表操作
面对大量的数据并发,还有更多的操作版本,比如可以进行分库分表,而分库分表又有更多的讨论点:
怎么分库?怎么分表?分哪些库?分哪些表?
首先看数据库,有两种方式,一种是水平分库,一种是垂直分库。
水平分库,就是将数据库中的数据水平切,分到不同的数据库中。考虑一个场景,如果一个数据库规模越来越大,数据量千万级,但是磁盘的容量是有限的,不可能无限地加容量,因此必须要分到其他地方,也就是将数据分到多个地方进行存储,id为1万以内的在一个数据库,1万到2万的一个数据库,这样就能保证容量合适,保证数据库的性能
而垂直分库呢,也就是垂直地切,将数据库中的某些表分到其他地方去,原本数据库中有4张表,现在分成2个库,每个库2张,根据表的热点不同,进行不同的处理,某些表经常操作,而某些表又不经常操作,因此需要特殊情况特殊处理。
对于分表操作:
也是分为垂直分表和水平分表,类似于上面的做法,
垂直分表,将表中的某些不常用的字段或者大的字段分出来,放到另一个表中进行处理,比如blob,text等,分开进行处理;水平分表,也类似于上面的水平分库,将整个表的数据拆分成多分,存储到多个表中进行处理。
那么分库分表有什么好处呢?
从垂直分库分表上来讲,都是将原来的一张表或几张表分成更多的子表,显而易见,这可以实现将冷热数据进行分离,便于管理和监控,总是,是一种解耦的思路,同时,可以根据不同的业务需求,进行细粒度的拆分,提高效率。
从水平分库分表上来讲,都是一个数据表或库中的数据量过多,在这样的情况下,算法优化已经陷入瓶颈,高并发高性能都面临着压力,还有表内的各种机制限制,对于大容量的表是不友好的,比如,行锁表锁,某条数据的操作时,被加上了行锁,影响了其他并发事务的操作。