MySQL 锁
MySQL有哪些锁
按锁粒度分类:
- 行锁:锁某行数据,锁粒度最小,并发度高
- 表锁:锁整张表,铁粒度最大,并发度低
- 间隙锁:锁的是一个区间
还可以分为:
- 共享锁:也就是读锁,一个事务给某行数据加了读锁,其他事务也可以读,但是不能写
- 排它锁:也就是写锁,一个事务给某行数据加了写锁,其他事务不能读,也不能写
还可以分为:
- 乐观锁:并不会真正的去锁某行记录,而是通过一个版本号来实现的
- 悲观锁:上面所的行锁、表锁等都是悲观锁
MySQL查询慢如何优化
- 检查是否走了索引,如果没有则优化SQL利用索引
- 检查所利用的索引,是否是最优索引
- 检查所查字段是否都是必须的,是否查询了过多字段,查出了多余数据
- 检查表中数据是否过多,是否应该进行分库分表了
- 检查数据库实例所在机器的性能配置,是否太低,是否可以适当增加资源
MySQL的聚簇索引和非聚簇索引
- 聚簇索引:将数据存储与索引放到了一块、并且是按照一定的顺序组织的,找到索引也就找到了数据,数据的物理存放顺序与索引顺序是一致的,只要索引是相邻的,那么对应的数据一定也是相邻地存放在磁盘上的
- 非聚簇索引:叶子节点不存储数据、存储的是数据行地址,也就是说根据索引查找到数据行的位置再取磁盘查找数据,这个就有点类似一本树的目录,比如我们要找第三章第一节,那我们先在这个目录里面找,找到对应的页码后再去对应的页码看文章。
聚簇索引的优点:
- 查询通过聚簇索引可以直接获取数据,相比非聚簇索引需要第二次査询(非覆盖索引的情况下)效率要高
- 聚簇索引对于范围查询的效率很高,因为其数据是按照大小排列的
- 聚簇索引适合用在排序的场合,非聚族索引不适合
聚簇索引的缺点:
- 维护索引很昂贵,特别是插入新行或者主键被更新导至要分页(page split)的时候。建议在大量插入新行后,选在负载较低的时间段,通过OPTIMIZE TABLE优化表,因为必须被移动的行数据可能造成碎片。使用独享表空间可以弱化碎片
- 表因为使用UUId(隨机ID)作为主键,使数据存储稀疏,这就会出现聚簇索引有可能有比全表扫面更慢,所以建议使用int的auto_increment作为主键
- 如果主键比较大的话,那辅助索引将会变的更大,因为辅助索引的叶子存储的是主键值;过长的主键值,会导致非叶子节点占用占用更多的物理空间
如何分库分表
- 水平
- 水平分表:操作数据库中某张表,把这张表的数据分到多张新表里面,每张新表里面的数据量大致相等
- 水平分库:操作数据库,把数据库中的表分到多个数据库里面,每个数据库里面的数据量大致相等
- 垂直
- 垂直分表:操作数据库中某张表,把这张表中一部分字段数据存到一张新表里面,再把这张表另一部分字段数据存到另外一张表里面
- 垂直分库:把单一数据库按照业务进行划分,专库专表
分库分表后如何保证主键唯一
- UUID
- 主键单调递增
- Redis、MongoDB、ZK 生成唯一的分布式ID
ACID是如何保证的
- Atomicity(原子性):一个事务中的所有操作要么全部成功,要么全部失败
- Consistency(一致性):事务必须在一致性状态内执行。事务的执行必须从一个一致性状态变到另一个一致性状态
- Isolation(隔离性):一个事务的执行不能被其他事务干扰。即一个事务在执行时,对其他事务的执行不会产生影响
- Durability(持久性):一个事务执行后对数据库中的数据改变必须被持久化到数据库,即使数据库系统发生宕机,数据库重新启动后,这个事务中变化的数据也必须可以被恢复出来
- A 原子性:由undo日志保证。它记录了需要回滚的数据,当事务回滚时撤销已经执行成功的sql
- C 一致性:(目的)由其他三大特性保证
- I 隔离性:由MVCC保证(多版本并发控制)
- D 持久性:由内存和redo日志保证,mysql修改数据同时在内存和redo日志中记录,当系统崩溃时,redo日志保证数据恢复
MVCC多版本并发控制
多版本并发控制:读取数据时通过一种类似快照的方式将数据保存下来,这样读锁就和写锁不冲突了,不同的事务session会看到自己特定版本的数据,版本链
MySQL主从同步原理
主节点当中binlog dump 线程 ,从节点:IO 、 SQL 线程 Mater一条线程 Slave两条线程
- 主节点binlog dump 线程,将 binlog 写入到从节点的 binlog 文件中,
- 主节点log dump 线程,当binlog有变动时,该线程读取内容发送给从节点
- 从节点的IO线程读取 binlog 文件,写入relay.log文件当中
- 从节点SQL线程兑取 relay.log 文件,然后执行SQL语句,从而保证主从节点数据的一致性
注:主从节点使用 binlog文件 + position 偏移量来定位主从同步的位置,从节点会保存其已接收到的偏移量如果从节点发生宕机重启,则会自动从position的位置发起同步。
由于mysql默认的复制方式是异步的,主库把日志发送给从库后不关心从库是否已经处理,这样会产生一个问题就是假设主库挂了,从库处理失败了,这时候从库升为主库后,日志就丢失了。由此产生两个概念。
- 全同步复制:主库写入binlog后强制同步日志到从库,所有的从库都执行完成后才返回给客户端,但是很显然这个方式的话性能会受到严重影响。
- 半同步复制:和全同步不同的是,半同步复制的逻辑是这样,从库写入日志成功后返回ACK确认给主库,主库收到至少一个从库的确认就认为写操作完成。
事务的基本特性(ACID)和隔离级别
隔离级别:
- READ UNCOMMITTED:读未提交,可能会读到其他事务未提交的数据,也叫做脏读。
- READ COMMITTED:读已提交,两次读取结果不一致,叫做不可重复读。不可重复读解决了脏读的问题,他只会读取已经提交的事务。
- REPEATABLE READ:可重复读,默认隔离级别,每次读取的数据都是一样的,但是可能会出现幻读
- SERIALIZABLE:串行,会给每一行读取的数据加锁,影响性能
- 脏读
- 读到了其他事务未提交的数据,未提交意味着这些数据可能会回滚,也就是可能最终不会存到数据库中,也就是不存在的数据。读到了并一定最终存在的数据,这就是脏读。
- 脏读最大的问题就是可能会读到不存在的数据。比如在上图中,事务B的更新数据被事务A读取,但是事务B回滚了,更新数据全部还原,也就是说事务A刚刚读到的数据并没有存在于数据库中。
- 不可重复读
- 是在一个事务内,最开始读到的数据和事务结束前的任意时刻读到的同一批数据出现不一致的情况。
- 事务A多次读取同一数据,但事务B在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果不一致。
- 幻读
- 并不是说两次读取获取的结果集不同,幻读侧重的方面是某一次的select操作得到的结果所表征的数据状态无法支撑后续的业务操作。
- 更为具体一些:select某记录是否存在,不存在,准备插入此记录,但执行insert时发现此记录已存在,无法插入,此时就发生了幻读。
- 1、事务A,查询是否存在 id=5 的记录,没有则插入,这是我们期望的正常业务逻辑。
- 2、这个时候 事务B 新增的一条 id=5 的记录,并提交事务。
- 3、事务A,再去查询 id=5 的时候,发现还是没有记录(因为这里是在RR级别下研究(可重复读),所以读到依然没有数据)
- 4、事务A,插入一条 id=5 的数据。
- 最终 事务A 提交事务,发现报错了。这就很奇怪,查的时候明明没有这条记录,但插入的时候,却告诉我主键冲突,这就好像幻觉一样。这才是所有的幻读。