数据库

事务特性

ACID:原子性、隔离性、一致性、持久性
一致性:执行事务前后,数据保持一致。多个事务对同一数据读取一致。

范式

  1. 每个列不可再拆分
  2. 非主键列完全依赖于主键,不能只依赖于主键的一部分
  3. 非主键列只依赖于主键,不依赖于其它键

隔离级别

MySQL默认隔离级别:可重复读

隔离级别 一句话解释 问题
未提交读 read uncommited A 事务可读到 B 事务未提交的数据 读数据时未加锁 写数据时只加行级共享锁 脏读,读到另一个事务未提交的数据
提交读 read commited 一个事务要等另一个事务提交后才能读到数据 读数据时加行级共享锁 写数据时加行级排他锁,写完立即释放 不可重复读,事务结束前读取了另一个事务已提交的更新数据
可重复读 A 事务读取数据时,不允许 B 事务修改操作 读数据时加行级共享锁,直到事务结束 写数据时加行级排他锁,直到事务结束 幻读,事务结束前读取了另一个事务已提交的新增数据
序列化 串行化顺序读取 表级共享锁,直到事务结束 表级排他锁,直到事务结束

悲观锁和乐观锁

行级表级的写锁(排它锁)即为悲观锁

MVCC 多版本并发控制 Multiversion Concurrency Control

乐观锁实现方案之一:通过增加版本号(事务ID),来解决数据不可重复读的问题

  • insert逻辑:

    创建一条记录,DB_TRX_ID为当前事务ID,DB_ROLL_PTR为NULL。

  • delete逻辑:

    将当前行的DB_TRX_ID设置为当前事务ID,DELELE_BIT设置为1。

  • update逻辑:

    复制一行,新行的DB_TRX_ID为当前事务ID,DB_ROLL_PTR指向上个版本的记录,事务提交后DB_ROLL_PTR设置为NULL。

  • SELECT逻辑:

    1、只查找创建早于当前事务ID的记录,确保当前事务读取到的行都是事务之前就已经存在的,或者是由当前事务创建或修改的;

    2、行的DELETE BIT为1时,查找删除晚于当前事务ID的记录,确保当前事务开始之前,行没有被删除。

索引数据结构

  • 采用B+树,innodb引擎。数据与索引为同一个文件,叶子结点即数据块,叶子结点未尾有指针相互串联
  • 辅助索引(主键外其它列的索引)非叶子节点存储该列的值,叶子节点(数据块)存储主键值。所以辅助索引需要查询两遍
  • 优点:
    • 树的深度低,磁盘IO次数少,因而效率更高
    • 查询效率更稳定,因为数据都放在最后

聚簇索引

特点:

  • 索引与数据在一块,找到索引后即找到数据,无需再次查找
  • 索引顺序与数据块的磁盘顺序相同,所以一个表只能有一个聚簇索引
  • 主键都是聚簇索引,非主键索引都是非聚簇索引

非聚簇索引

  • 索引与数据不在一块,索引存储着数据的指针,需要再再次查找

undo、redo、binlog

undo、redo:事务用,记录是物理操作,重复执行没有问题
binlog:主从同步用,记录的是逻辑操作,即SQL语句,重复执行数据会出问题

引擎:MyIsam 和 innodb

  • innodb 支持事务、外键、行级锁,MyIsam不支持
  • innodb 主键索引与数据在同一个文件中,MyIsam索引与文件是分开的

优化

思路:

  1. 确认瓶颈,优化瓶颈处
    • 添加业务日志,查看是否业务问题
    • 配置慢查询日志,收集所有的慢SQL
  2. 优化业务逻辑,优化SQL
    • 比如将联表拆成业务代码来做
    • 分析慢SQL:
      • 没有索引,添加索引
      • 有索引,通过 explain 命令分析是否用到了索引,根据一堆索引失效常见问题去优化。比如联合索引最左匹配,like查询没用上索引,查询时字段上加了函数等等
      • 降低select量,降低非聚簇索引二次查询,或者传输量
  3. 仍然慢,加缓存
  4. 数据库读写分离
  5. 加机器
  6. 分库分表

分库分表

垂直分

即切分字段,将不常用的字段新建表来存

水平分

即切分数据

问题

JOIN、事务、自增ID会出现问题

方案

  • range
    • 即按范围,比如按id范围,按时间范围
    • 冷热分离,不均衡
  • hash
    • 比如按ID取模
    • 均衡,但不方便扩容
  • 雪花算法:分布式唯一ID的解决方案
    • 时间 + 机器ID + 流水号
    • 方便扩容
  • 一致性hash算法
    • 数据被映射到一个232 的环上
    • 机器节点也被映射到一个232 的环上
    • 按顺时针方向分发数据到下一个最近的机器节点
    • 不均衡时可使用虚拟节点,再加一层映射
    • 方便扩容,只影响环上逆时针方向的前一个节点而已

分页查询

说明:MySQL并不是跳过offset行,而是取offset+N行,然后返回放弃前offset行,返回N行,那当offset特别大的时候,效率就非常的低下,要么控制返回的总页数,要么对超过特定阈值的页数进行SQL改写。 

正例:先快速定位需要获取的id段,然后再关联: 

SELECT a.* FROM 表1 a, (select id from 表1 where 条件 LIMIT 100000,20 ) b where a.id=b.id

Null

null本身是用特殊方式表示的,会占用字节

char、varchar

  • char存储时会忽略后面空格
  • 超长均会截断
  • varchar会多一个或两个字节来存储实际长度
  • char不管实际多少字符,均占用最长字节
    ## 分布式事务
    参考文章:分布式事务

CAP:

  • C:Consistency 一致性:同一数据的多个副本是否实时相同。
  • A:Availability 可用性:一定时间内 & 系统返回一个明确的结果 则称为该系统可用。
  • P:Partition tolerance 分区容错性。将同一服务分布在多个系统中,从而保证某一个系统宕机,仍然有其他系统提供相同的服务。

BASE:

  • BA:base availabe 基本可用
  • S:soft State。数据不用实时一致
  • E:Eventual Consisstency:但数据经过一段时间后最终是一致的

基于可靠消息服务的分布式事务

全部成功时:

  1. 发起方及事务方A、消息中心 M、协助方B
  2. A发送给M,要求B执行一段事务
  3. M收到后,入库,返回给A已收到
  4. A执行自己事务,完毕后向M发送commit。发送完毕后A即结束,进行其它处理
  5. M同步发送要求B执行事务的代码给B,并进行重试确保事务完成

A事务执行失败时:

  1. 发起方及事务方A、消息中心 M、协助方B
  2. A发送给M,要求B执行一段事务
  3. M收到后,入库,返回给A已收到
  4. A执行自己事务,执行失败,向M发送rollback。发送完毕后A即结束,进行其它处理
  5. M撤销自己的消息

M未收到commit消息时:

  1. 发起方及事务方A、消息中心 M、协助方B
  2. A发送给M,要求B执行一段事务
  3. M收到后,入库,返回给A已收到
  4. A执行自己事务,执行失败,向M发送rollback/commit,但消息丢了。发送完毕后A即结束,进行其它处理
  5. M一段时间后回查A状态,commit即提交,rollback即回滚,处理中则继续等待

发布于 2020/08/20 浏览