行锁:减少锁冲突提升业务并发度 2020-04-29 行锁 行锁就是针对数据表中行记录的锁。这很好理解,比如事务 A 更新了一行,而这时候事务 B 也要更新同一行,则必须等事务 A 的操作完成后才能进行更新。 MySQL 的行锁是在引擎层由各个引擎自己实现的。但并不是所有的引擎都支持行锁,比如 MyISAM 引擎就不支持行锁。 支持行锁意味着并发控制只能使用表锁,对于这种引擎的表,同一张表上任何时刻只能有一个更新在执行,这就会影响到业务并发度。 两阶段锁 在下面的操作序列中,事务 B 的 update 语句执行时会是什么现象呢?假设字段 id 是表 t 的主键。 这个问题的结论取决于事务 A 在执行完两条 update 语句后,持有哪些锁,以及在什么时候释放。你可以验证一下:实际上事务 B 的 update 语句会被阻塞,直到事务 A 执行 commit 之后,事务 B 才能继续执行。 也就是说,在 InnoDB 事务中,行锁是在需要的时候才加上的,但并不是不需要了就立刻释放,而是要等到事务结束时才释放。这个就是两阶段锁协议。 在事务中合理优化两段式提交 如果你的事务中需要锁多个行,要把最可能造成锁冲突、最可能影响并发度的....
全局锁、表级锁 2020-04-24 数据库中的锁 数据库锁设计的初衷是处理并发问题。作为多用户共享的资源,当出现并发访问的时候,数据库需要合理地控制资源的访问规则。而锁就是用来实现这些访问规则的重要数据结构。 MySQL 里面的锁大致可以分成全局锁、表级锁和行锁三类 为什么需要锁 假设你现在要维护一个课程购买系统,关注的是用户账户余额表和用户课程表。 现在发起一个逻辑备份。假设备份期间,有一个用户,他购买了一门课程,业务逻辑里就要扣掉他的余额,然后往已购课程里面加上一门课。 如果时间顺序上是先备份账户余额表 (u_account) => 用户购买 => 备份用户课程表 (u_course),会怎么样呢?如下图所示: 如果用以上备份来恢复数据的话,用户 A 就发现,自己赚了。用户 A 的数据状态是:账户余额没扣,但是用户课程表里面已经多了一门课。 (用户也开心) 如果反过来:备份用户课程表 (u_course) => 用户购买 => 先备份账户余额表 (u_account) ,基于此备份进行恢复数据,会发现用户在没有发生任何购买行为的情况下,原本的余额减少了。(用户无法接受) 也就....
索引 2020-04-23 索引简介 索引的出现其实就是为了提高数据查询的效率,就像书的目录一样。 在 MySQL 中,索引是在存储引擎层实现的,所以并没有统一的索引标准,即不同存储引擎的索引的工作方式并不一样。而即使多个存储引擎支持同一种类型的索引,其底层的实现也可能不同。 索引的常见模型 索引的出现是为了提高查询效率,但是实现索引的方式却有很多种,所以这里也就引入了索引模型的概念。可以用于提高读写效率的数据结构很多,例如:哈希表、有序数组、搜索树。 数据库底层存储的核心就是基于这些数据模型的。每碰到一个新数据库,我们需要先关注它的数据模型,这样才能从理论上分析出这个数据库的适用场景。 哈希索引 # 适用场景 * 哈希表这种结构适用于只有等值查询的场景,比如 Memcached 及其他一些 NoSQL 引擎。 # 优点 * 图中四个 ID_card_n 的值并不是递增的(并不是有序的,123456),这样做的好处是增加新的 User 时速度会很快,只需要往后追加。 # 缺点 * 因为不是有序的,所以哈希索引做区间查询的速度是很慢.(如果你现在要找身份证号在[ID_card_X, ID_card_Y]这....
设计模式 2020-05-19 简单工厂 由一个工厂对象决定创建出哪一种产品类的实例。 创建型,但不属于GOF23种设计模式。 编码风格,不属于设计模式。 # 适用场景 * 工厂类负责创建的对象比较少 * 客户端(应用层)只知道传入工厂类的参数,对于如何创建对象(逻辑)不关心 # 优点 * 只需要传入一个正确的参数,就可以获取你所需要的对象,无需知道其创建细节。 # 缺点 * 工厂类的职责相对过重,增加新的产品需要修改工厂类的判断逻辑,违背开闭原则。 * 无法形成基于继承的结构。 Video public abstract class Video { public abstract void produce(); } JavaVideo public class JavaVideo extends Video { @Override public void produce() { System.out.println("Java 开讲了!!!"); } } PythonVideo public class PythonVideo extends Video { @Override public void....
Isolation 2020-04-30 Transaction 简单来说,事务就是要保证一组数据库操作,要么全部成功,要么全部失败。在 MySQL 中,事务支持是在引擎层实现的。 MySQL是一个支持多引擎的系统,但并不是所有的引擎都支持事务。 隔离性与隔离级别 隔离性(Isolation) 当数据库上有多个事务同时执行的时候,就可能出现脏读(dirty read)、不可重复读(non-repeatable read)、幻读(phantom read)的问题,为了解决这些问题,就有了“隔离级别”的概念。 隔离性与执行效率的平衡点 隔离得越严实,效率就会越低。因此很多时候,我们都要在二者之间寻找一个平衡点。 SQL 标准的事务隔离级别 * 读未提交(read uncommitted) 读未提交是指,一个事务还没提交时,它做的变更就能被别的事务看到。 * 读提交(read committed) 读提交是指,一个事务提交之后,它做的变更才会被其他事务看到。 * 可重复读(repeatable read) 可重复读是指,一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。当然在可重复读隔离级别下,未提交....
数据库设计 2020-04-19 数据库设计范式 第一范式 表中的所有字段都是不可再分的。(将复合属性进行拆分) 第二范式 表中必须存在业务主键,并且非主键依赖于全部业务主键。业务主键:那些可以唯一标识出每一行业务数据的列,或是列的组合。 如果表的业务主键只有一个列组成,那么该表原生就符合第二范式。 如果表的业务主键有多个列组成,那么需要把那些非主键依赖于全部业务主键的列从表中拆分出来。 第三范式 表中的非主键列之间不能相互依赖。 范式化设计存在的问题 如何获取出一门课程包括所有章节和小节的信息? # 表关联多,查询性能越差 1. 需要查询的信息分别位于三个不同的表。 2. 并且需要经过2个关联表。 3. 如果需要查询目标数据,就需要用到5个表。 反范式化设计 主要思路是拿空间换时间,或多或少会违反范式化的设计原则,存在一定程度上的数据冗余。 课程章节 数据结构设计流程 1. 业务分析(抽象原始数据) 2. 逻辑设计 范式化设计 反范式化设计 3. 物理设计 存储引擎 数据类型 对象命名 4. 建立库表 业务分析 课程的属性 主标题、副标题、方向、分类、难度、最新、最热、时长、简介、人数、需知、收....
日志系统:redo log、binlog 2020-04-16 一条SQL更新语句如何执行 # 这个表有一个主键 ID 和一个整型字段 c mysql> create table T(ID int primary key, c int); # 如果要将 ID=2 这一行的值加 1,SQL 语句就会这么写 mysql> update T set c=c+1 where ID=2; 查询语句的那一套流程,更新语句也是同样会走一遍。 前面我们说过,在一个表上有更新的时候,跟这个表有关的查询缓存会失效,所以这条语句就会把表 T 上所有缓存结果都清空。 这也就是我们一般不建议使用查询缓存的原因。接下来,分析器会通过词法和语法解析知道这是一条更新语句。优化器决定要使用 ID 这个索引。然后,执行器负责具体执行,找到这一行,然后更新。 与查询流程不一样的是,更新流程还涉及两个重要的日志模块:redo log(重做日志)和 binlog(归档日志) 执行器和 InnoDB 引擎在执行这个简单的 update 语句时的内部流程。 执行器先找引擎取 ID=2 这一行。ID 是主键,引擎直接用树搜索找到这一行。如果 ID=2 这一行....