索引的设计原则
原则: 查询更快,占用空间少;
- 适合的索引列在 where 字句中的列 或者连接字句指定的列;
- 数据较少的表,索引效果差,没必要建立索引,因为建立索引,会产生索引文件,占用额外的磁盘空间,并且也需要去维护,反而增加了开销;
- 不要过度的索引,因为索引也是需要磁盘空间的,过多的索引会降低写操作的性能,在修改表内容的时候,索引也需要进行重构的,索引列越多,这个维护索引的时间越长,所以只要保持需要的索引有利查询即可;
- 定义外键列一定要建立索引;
- 更新频繁的列不适合创建索引;
- 尽量扩展索引,不要新建索引,比如表中已经有 a 索引,现在需要 (a,b)索引,那么将 a索引修改成 (a,b) 的联合索引即可;
- 查询中很少涉及到的列,重复值比较多的列不要建索引;
Mysql 的执行计划
explain select * from A where x = ?and y = ?;
主要观察 type 的值,看走的时那种类型的索引;
事务的基本特性和隔离级别
事务基本特性 ACID分别是 原子性,一致性,隔离性,持久性;
- 原子性: 是指一个事务里面的操作要么全部成功,要么全部失败(比如转账);
- 一致性: 是指数据库从一个一致性的状态转换到另一个一致性状态;比如 A转账到B 100,当A中只有90 时,支付之前数据库里的数据是符合约束的,但是如果事务执行成功了,那么就破坏了数据库数据的约束(指的是 A账户实际只有 90,只能转出90,那么给 B账户加了100,A转出与B转入的数据就不一致了),因此该事务就不允许成功;这里就是事务提供了一致性的保证,当然这种一致性的保证是 业务层级的,需要在代码中判断处理;
- 隔离性: 指的是一个事务修改在提交前,对其他事务是不可见的;如果其他事务读取到这个事务中修改的值,那么其他事务就是脏读,是不准确的;
- 持久性 : 指的是事务一旦提交,事务的修改就会永久保存在数据库中;
注意: 原子性,隔离性,持久性的都是为了最终的一致性服务的;
隔离性的四个级别
- read uncommit 读未提交,指的是读取到未提交事务中的数据,也叫脏读;
- read commit 读已提交 ,两次读取结果不一致,叫做不可重复读; 读已提交指一定是读取已经提交的数据,没有提交是读取不到的;不可重复读情况是指 假如 A ,B,C 三个事务,A事务更新 Money 字段为 100且已经提交,那么B事务读取该字段值为 100,但B事务没结束时,C事务又更新该字段为 200且已经提交,那么B事务读取该字段值为 200;那么想解决这种情况可以在 B事务读的时候加 读锁,那么字段就不会被在读的时候被更改;
read commit 读已提交 这种时 oracle 的默认级别; - repeatable read 可重复读,读的也一定是已经提交的数据;就是每次读取的结果都一样,可重复读情况是指 假如 A ,B,C 三个事务,A事务更新 Money 字段为 100且已经提交,那么B事务读取该字段值为 100,但B事务没结束时,C事务又更新该字段为 200且已经提交,那么B事务读取该字段值还是 100,可重复读到的数始终是第一次读取到的提交事务的值;
可能出现幻读,出现幻读的原因与 MVCC有关,相关解释看下面 MVCC介绍;
这是 Mysql的默认级别; - serializable 串行,一般不会使用,他会给没一行读取的数据加锁,会导致大量超时和锁竞争问题;
什么是MVCC
多版本并发控制: 读取数据时通过一种类似快照的方式将数据记录下来,这样读锁和写锁就不会相互冲突,不同事务的 session 会看到自己特定版本的数据,版本链;
MVCC 只在 read commit ,repeatable read 两种隔离级别下工作,其他两个隔离级别和 MVCC不兼容;
readview 指针对查询情况,只是在查询时产生的;
比如未提交的事务 id 为 排序的数组里面值 是 [100,103,104],注意数组里面事务是从小到大排列的,并且中间是没有空隙的 (例如事务数组中为什么没有 102,因为并没有未提交事务id 是 102,不存在100到103中间还有事务id是102这种说法);若我执行查询的事务id是60,60<100,那么说明要查询的数据是已经提交了的数据,可直接访问;若查询事务id 是100,则事务id在未提交事务id数组中,则根据 readview 中 roll_pointer 的找它的上一个已经提交版本的事务,根据100中上一个事务版本的地址找到上一个版本为 60,而60不在未提交事务数组中,且在左边,取这个已经提交的最新版本的数据; 在右边意味着事务在 readview生成之后产生的且还未提交,等这个 未提交的事务id进入到 readview后,如事务id在readview中一样,获取上一个事务的地址,直至找到最近一个提交事务并访问取数据;
repeatable read 可能出现幻读的情况
repeatable read 可重复读隔离级别时只在第一次读的时候生成一个 readview,之后的读都是复用之前的 readview;假如 select * from A where id>1 and id<10 这个范围有 5条数据,那么 readview 中就会有这5条数,第一次读是5条,那么这时又其他事务 insert 了一条 id在 1-10 之间的记录,那么这个 readview 就1-10范围有了6条数据,那么第二次读是6条就是幻读;
怎么处理慢查询
首先确定慢查询的原因是什么? 是查询条件没有命中索引,还是 加载了太多不需要的列或者数据量太大;
优化:
- 看是否加载了许多不需要的列,将查询多余了的列去掉;
2)看查询条件是否命中索引;
3)若SQL已经无法优化并且数据量太大,如果可以的话进行纵向或横向的分表;
Mysql 主从同步原理
同步过程: 主从复制主要有三个线程, master(binlog dump Thread),slave(I/O Thread, SQL Thread), master 一条线程和 salve 两条线程;
主节点 binlog,主从 复制的基础是 主库记录数据库的所有变更记录到 binlog中,binlog 是数据库服务器启动的那一刻起,保存所有数据库结构或内容的一个文件;
主节点的 log dump 线程,当binlog有变动时,log dump 线程会读取内容并发送给从节点;
从节点 I/O 线程接收到 binlog内容,将内容写到 reply.log 文件中;
从节点通过 SQL 线程读取到 reply.log 内容并对数据库数据进行更新,最终保持数据的一致性;
注意: 主从节点使用 binlog文件 + position 偏移量来定位同步位置,从节点会保存已经接收到的偏移量,如果从节点宕机重启,重节点会自动从 position的位置进行同步;
由于 Mysql的同步是异步的,若主库将 binlog文件发送给从库后不关心从库是否成功执行,那么假设主库挂了,从库处理失败了,日志就会丢失;
由此产生两种同步方式
全同步复制
当主库将文件发送给从库后必须收到所有从库成功执行完毕的消息,才认为是同步成功,这种同步方式性能会受到严重影响;
半同步复制
当主库将文件发送给从库后且收到一半以上从库成功执行完毕的消息,那么就认为是同步成功;