pull/6/head
yuanguangxin 4 years ago
parent cde96ead44
commit 30b2af7afe

@ -261,13 +261,7 @@
<workItem from="1588302756697" duration="3701000" />
<workItem from="1588749661816" duration="451000" />
<workItem from="1589857792404" duration="221000" />
</task>
<task id="LOCAL-00016" summary="add q42">
<created>1581519773712</created>
<option name="number" value="00016" />
<option name="presentableId" value="LOCAL-00016" />
<option name="project" value="LOCAL" />
<updated>1581519773712</updated>
<workItem from="1591254276718" duration="1288000" />
</task>
<task id="LOCAL-00017" summary="update md">
<created>1581704713975</created>
@ -605,7 +599,14 @@
<option name="project" value="LOCAL" />
<updated>1588749779332</updated>
</task>
<option name="localTasksCounter" value="65" />
<task id="LOCAL-00065" summary="update">
<created>1591254334744</created>
<option name="number" value="00065" />
<option name="presentableId" value="LOCAL-00065" />
<option name="project" value="LOCAL" />
<updated>1591254334745</updated>
</task>
<option name="localTasksCounter" value="66" />
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
@ -690,14 +691,16 @@
<screen x="0" y="0" width="1440" height="900" />
</state>
<state x="458" y="204" key="#com.intellij.refactoring.safeDelete.UnsafeUsagesDialog/0.0.1440.900@0.0.1440.900" timestamp="1587221348872" />
<state x="303" y="50" key="CommitChangelistDialog2" timestamp="1588749779133">
<screen x="0" y="0" width="1440" height="900" />
<state x="404" y="60" key="CommitChangelistDialog2" timestamp="1591254334575">
<screen x="0" y="0" width="1920" height="1080" />
</state>
<state x="303" y="50" key="CommitChangelistDialog2/0.0.1440.900@0.0.1440.900" timestamp="1588749779133" />
<state x="143" y="78" width="1152" height="720" key="DiffContextDialog" timestamp="1588749770793">
<screen x="0" y="0" width="1440" height="900" />
<state x="404" y="60" key="CommitChangelistDialog2/0.0.1920.1080@0.0.1920.1080" timestamp="1591254334575" />
<state x="191" y="94" width="1152" height="720" key="DiffContextDialog" timestamp="1591254332165">
<screen x="0" y="0" width="1920" height="1080" />
</state>
<state x="143" y="78" width="1152" height="720" key="DiffContextDialog/0.0.1440.900@0.0.1440.900" timestamp="1588749770793" />
<state x="191" y="94" key="DiffContextDialog/0.0.1920.1080@0.0.1920.1080" timestamp="1591254332165" />
<state x="144" y="78" width="1152" height="720" key="DiffContextDialog/0.23.1440.793@0.23.1440.793" timestamp="1582714686712" />
<state x="143" y="78" width="1152" height="720" key="DiffContextDialog/0.23.1440.797@0.23.1440.797" timestamp="1588303139733" />
<state width="1398" height="204" key="GridCell.Tab.0.bottom" timestamp="1587225087588">
@ -744,14 +747,11 @@
<screen x="0" y="0" width="1440" height="900" />
</state>
<state x="221" y="63" key="SettingsEditor/0.0.1440.900@0.0.1440.900" timestamp="1585223890241" />
<state x="320" y="190" key="Vcs.Push.Dialog.v2" timestamp="1588749781144">
<screen x="0" y="0" width="1440" height="900" />
<state x="427" y="228" key="Vcs.Push.Dialog.v2" timestamp="1591254336079">
<screen x="0" y="0" width="1920" height="1080" />
</state>
<state x="320" y="190" key="Vcs.Push.Dialog.v2/0.0.1440.900@0.0.1440.900" timestamp="1588749781144" />
<state x="100" y="100" width="1240" height="700" key="com.intellij.history.integration.ui.views.DirectoryHistoryDialog" timestamp="1581744794182">
<screen x="0" y="23" width="1440" height="797" />
</state>
<state x="100" y="100" width="1240" height="700" key="com.intellij.history.integration.ui.views.DirectoryHistoryDialog/0.23.1440.797@0.23.1440.797" timestamp="1581744794182" />
<state x="427" y="228" key="Vcs.Push.Dialog.v2/0.0.1920.1080@0.0.1920.1080" timestamp="1591254336079" />
<state x="100" y="100" width="1240" height="700" key="com.intellij.history.integration.ui.views.FileHistoryDialog" timestamp="1587219014962">
<screen x="0" y="23" width="1440" height="797" />
</state>
@ -772,10 +772,6 @@
<screen x="0" y="0" width="1440" height="900" />
</state>
<state x="398" y="248" key="git4idea.merge.GitPullDialog/0.0.1440.900@0.0.1440.900" timestamp="1582971880352" />
<state x="385" y="210" key="run.anything.popup" timestamp="1581652493432">
<screen x="0" y="0" width="1440" height="900" />
</state>
<state x="385" y="210" key="run.anything.popup/0.0.1440.900@0.0.1440.900" timestamp="1581652493432" />
<state x="385" y="196" width="670" height="676" key="search.everywhere.popup" timestamp="1587219643331">
<screen x="0" y="0" width="1440" height="900" />
</state>

@ -127,6 +127,15 @@ Redis默认是快照RDB的持久化方式。对于主从同步来说主从刚
3. 隔离性:同一时间,只允许一个事务请求同一数据,不同的事务之间彼此没有任何干扰。
4. 持久性:事务完成后,事务对数据库的所有更新将被保存到数据库,不能回滚。
### Mysql的存储引擎
1. InnoDB存储引擎InnoDB存储引擎支持事务其设计目标主要面向在线事务处理OLTP的应用。其特点是行锁设计支持外键并支持非锁定锁即默认读取操作不会产生锁。从Mysql5.5.8版本开始InnoDB存储引擎是默认的存储引擎。
2. MyISAM存储引擎MyISAM存储引擎不支持事务、表锁设计支持全文索引主要面向一些OLAP数据库应用。InnoDB的数据文件本身就是主索引文件而MyISAM的主索引和数据是分开的。
3. NDB存储引擎NDB存储引擎是一个集群存储引擎其结构是share nothing的集群架构能提供更高的可用性。NDB的特点是数据全部放在内存中从MySQL 5.1版本开始可以将非索引数据放在磁盘上因此主键查找的速度极快并且通过添加NDB数据存储节点可以线性地提高数据库性能是高可用、高性能的集群系统。NDB存储引擎的连接操作是在MySQL数据库层完成的而不是在存储引擎层完成的。这意味着复杂的连接操作需要巨大的网络开销因此查询速度很慢。如果解决了这个问题NDB存储引擎的市场应该是非常巨大的。
4. Memory存储引擎Memory存储引擎之前称HEAP存储引擎将表中的数据存放在内存中如果数据库重启或发生崩溃表中的数据都将消失。它非常适合用于存储临时数据的临时表以及数据仓库中的纬度表。Memory存储引擎默认使用哈希索引而不是我们熟悉的B+树索引。虽然Memory存储引擎速度非常快但在使用上还是有一定的限制。比如只支持表锁并发性能较差并且不支持TEXT和BLOB列类型。最重要的是存储变长字段时是按照定常字段的方式进行的因此会浪费内存。
5. Archive存储引擎Archive存储引擎只支持INSERT和SELECT操作从MySQL 5.1开始支持索引。Archive存储引擎使用zlib算法将数据行row进行压缩后存储压缩比一般可达110。正如其名字所示Archive存储引擎非常适合存储归档数据如日志信息。Archive存储引擎使用行锁来实现高并发的插入操作但是其本身并不是事务安全的存储引擎其设计目标主要是提供高速的插入和压缩功能。
6. Maria存储引擎Maria存储引擎是新开发的引擎设计目标主要是用来取代原有的MyISAM存储引擎从而成为MySQL的默认存储引擎。它可以看做是MyISAM的后续版本。Maria存储引擎的特点是支持缓存数据和索引文件应用了行锁设计提供了MVCC功能支持事务和非事务安全的选项以及更好的BLOB字符类型的处理性能。
### 事务的并发问题
1. 脏读事务A读取了事务B更新的数据然后B回滚操作那么A读取到的数据是脏数据
@ -142,8 +151,21 @@ Redis默认是快照RDB的持久化方式。对于主从同步来说主从刚
| 可重复读 | 否 | 否 | 是 |
| 串行化 | 否 | 否 | 否 |
在MySQL可重复读的隔离级别中并不是完全解决了幻读的问题而是解决了读数据情况下的幻读问题。而对于修改的操作依旧存在幻读问题就是说MVCC对于幻读的解决时不彻底的。
通过索引加锁间隙锁next key lock可以解决幻读的问题。
### MVCC多版本并发控制
MVCC在RR可重复读级别解决了可能出现不可重复读的问题同时解决了读数据情况下的幻读问题对于修改新增的操作依旧存在幻读问题。它的实现原理主要是依赖记录中的 3个隐式字段undolog指针 Read View 来实现的。
#### 隐式字段
每行记录除了我们自定义的字段外,还有数据库隐式定义的最近修改(修改/插入)事务ID回滚指针指向这条记录的上一个版本隐含的自增ID隐藏主键如果数据表没有主键InnoDB会自动以DB_ROW_ID产生一个聚簇索引。
#### Read View
Read View就是事务进行快照读操作的时候生产的读视图在该事务执行的快照读的那一刻会生成数据库系统当前的一个快照记录并维护系统当前活跃事务的ID。Read View遵循一个可见性算法主要是将要被修改的数据的最新记录中的DB_TRX_ID即当前事务ID取出来与系统当前其他活跃事务的ID去对比由Read View维护如果DB_TRX_ID跟Read View的属性做了某些比较不符合可见性那就通过DB_ROLL_PTR回滚指针去取出Undo Log中的DB_TRX_ID再比较即遍历链表的DB_TRX_ID从链首到链尾即从最近的一次修改查起直到找到满足特定条件的DB_TRX_ID, 那么这个DB_TRX_ID所在的旧记录就是当前事务能看见的最新老版本。
### Next-Key Lock
InnoDB 采用 Next-Key Lock 解决幻读问题。在`insert into test(xid) values (1), (3), (5), (8), (11);`后由于xid上是有索引的该算法总是会去锁住索引记录。现在该索引可能被锁住的范围如下(-∞, 1], (1, 3], (3, 5], (5, 8], (8, 11], (11, +∞)。Session A执行后会锁住的范围(5, 8], (8, 11]。除了锁住8所在的范围还会锁住下一个范围所谓Next-Key。
### Mysql的逻辑结构
@ -158,18 +180,26 @@ SQL的执行顺序from---where--group by---having---select---order by
### MVCC,redolog,undolog,binlog
* undoLog 也就是我们常说的回滚日志文件 主要用于事务中执行失败进行回滚以及MVCC中对于数据历史版本的查看。由引擎层的InnoDB引擎实现,是逻辑日志,记录数据修改被修改前的值,比如"把id='B' 修改为id = 'B2' 那么undo日志就会用来存放id ='B'的记录”。当一条数据需要更新前,会先把修改前的记录存储在undolog中,如果这个修改出现异常,,则会使用undo日志来实现回滚操作,保证事务的一致性。当事务提交之后undo log并不能立马被删除,而是会被放到待清理链表中,待判断没有事物用到该版本的信息时才可以清理相应undolog。它保存了事务发生之前的数据的一个版本用于回滚同时可以提供多版本并发控制下的读MVCC也即非锁定读。
* redoLog 是重做日志文件是记录数据修改之后的值用于持久化到磁盘中。redo log包括两部分一是内存中的日志缓冲(redo log buffer),该部分日志是易失性的;二是磁盘上的重做日志文件(redo log file)该部分日志是持久的。由引擎层的InnoDB引擎实现,是物理日志,记录的是物理数据页修改的信息,比如“某个数据页上内容发生了哪些改动”。当一条数据需要更新时,InnoDB会先将数据更新然后记录redoLog 在内存中然后找个时间将redoLog的操作执行到磁盘上的文件上。不管是否提交成功我都记录你要是回滚了那我连回滚的修改也记录。它确保了事务的持久性。
* redoLog 是重做日志文件是记录数据修改之后的值用于持久化到磁盘中。redo log包括两部分一是内存中的日志缓冲(redo log buffer),该部分日志是易失性的;二是磁盘上的重做日志文件(redo log file)该部分日志是持久的。由引擎层的InnoDB引擎实现,是物理日志,记录的是物理数据页修改的信息,比如“某个数据页上内容发生了哪些改动”。当一条数据需要更新时,InnoDB会先将数据更新然后记录redoLog 在内存中然后找个时间将redoLog的操作执行到磁盘上的文件上。不管是否提交成功我都记录你要是回滚了那我连回滚的修改也记录。它确保了事务的持久性。每个InnoDB存储引擎至少有1个重做日志文件组group每个文件组下至少有2个重做日志文件如默认的ib_logfile0和ib_logfile1。为了得到更高的可靠性用户可以设置多个的镜像日志组mirrored log groups将不同的文件组放在不同的磁盘上以此提高重做日志的高可用性。在日志组中每个重做日志文件的大小一致并以循环写入的方式运行。InnoDB存储引擎先写重做日志文件1当达到文件的最后时会切换至重做日志文件2再当重做日志文件2也被写满时会再切换到重做日志文件1中。
* MVCC多版本并发控制是MySQL中基于乐观锁理论实现隔离级别的方式用于读已提交和可重复读取隔离级别的实现。在MySQL中会在表中每一条数据后面添加两个字段最近修改该行数据的事务ID指向该行undolog表中回滚段的指针。Read View判断行的可见性创建一个新事务时copy一份当前系统中的活跃事务列表。意思是当前不应该被本事务看到的其他事务id列表。
* binlog由Mysql的Server层实现,是逻辑日志,记录的是sql语句的原始逻辑比如"把id='B' 修改为id = B2。binlog会写入指定大小的物理文件中,是追加写入的,当前文件写满则会创建新的文件写入。 产生:事务提交的时候,一次性将事务中的sql语句,按照一定的格式记录到binlog中。用于复制和恢复在主从复制中从库利用主库上的binlog进行重播(执行日志中记录的修改逻辑),实现主从同步。业务数据不一致或者错了用binlog恢复。
### binlog和redolog的区别
1. redolog是在InnoDB存储引擎层产生而binlog是MySQL数据库的上层服务层产生的。
2. 两种日志记录的内容形式不同。MySQL的binlog是逻辑日志其记录是对应的SQL语句。而innodb存储引擎层面的重做日志是物理日志。
2. 两种日志记录的内容形式不同。MySQL的binlog是逻辑日志其记录是对应的SQL语句,对应的事务。而innodb存储引擎层面的重做日志是物理日志是关于每个页Page的更改的物理情况
3. 两种日志与记录写入磁盘的时间点不同binlog日志只在事务提交完成后进行一次写入。而innodb存储引擎的重做日志在事务进行中不断地被写入并日志不是随事务提交的顺序进行写入的。
4. binlog不是循环使用在写满或者重启之后会生成新的binlog文件redolog是循环使用。
5. binlog可以作为恢复数据使用主从复制搭建redolog作为异常宕机或者介质故障后的数据恢复使用。
### InnoDB的关键特性
1. 插入缓冲对于非聚集索引的插入或更新操作不是每一次直接插入到索引页中而是先判断插入的非聚集索引页是否在缓冲池中若在则直接插入若不在则先放入到一个Insert Buffer对象中。然后再以一定的频率和情况进行Insert Buffer和辅助索引页子节点的merge合并操作这时通常能将多个插入合并到一个操作中因为在一个索引页中这就大大提高了对于非聚集索引插入的性能。
2. 两次写两次写带给InnoDB存储引擎的是数据页的可靠性有经验的DBA也许会想如果发生写失效可以通过重做日志进行恢复。这是一个办法。但是必须清楚地认识到如果这个页本身已经发生了损坏物理到page页的物理日志成功页内逻辑日志失败再对其进行重做是没有意义的。这就是说在应用apply重做日志前用户需要一个页的副本当写入失效发生时先通过页的副本来还原该页再进行重做。在对缓冲池的脏页进行刷新时并不直接写磁盘而是会通过memcpy函数将脏页先复制到内存中的doublewrite buffer之后通过doublewrite buffer再分两次每次1MB顺序地写入共享表空间的物理磁盘上这就是doublewrite。
3. 自适应哈希索引InnoDB存储引擎会监控对表上各索引页的查询。如果观察到建立哈希索引可以带来速度提升则建立哈希索引称之为自适应哈希索引。
4. 异步IO为了提高磁盘操作性能当前的数据库系统都采用异步IOAIO的方式来处理磁盘操作。AIO的另一个优势是可以进行IO Merge操作也就是将多个IO合并为1个IO这样可以提高IOPS的性能。
5. 刷新邻接页当刷新一个脏页时InnoDB存储引擎会检测该页所在区extent的所有页如果是脏页那么一起进行刷新。这样做的好处显而易见通过AIO可以将多个IO写入操作合并为一个IO操作故该工作机制在传统机械磁盘下有着显著的优势。
### Mysql如何保证一致性和持久性
MySQL为了保证ACID中的一致性和持久性使用了WAL(Write-Ahead Logging,先写日志再写磁盘)。Redo log就是一种WAL的应用。当数据库忽然掉电再重新启动时MySQL可以通过Redo log还原数据。也就是说每次事务提交时不用同步刷新磁盘数据文件只需要同步刷新Redo log就足够了。

Loading…
Cancel
Save