目录

Life in Flow

知不知,尚矣;不知知,病矣。
不知不知,殆矣。

存档: 2020 年 04 月 (11)

行锁:减少锁冲突提升业务并发度

行锁  行锁就是针对数据表中行记录的锁。这很好理解,比如事务 A 更新了一行,而这时候事务 B 也要更新同一行,则必须等事务 A 的操作完成后才能进行更新。  MySQL 的行锁是在引擎层由各个引擎自己实现的。但并不是所有的引擎都支持行锁,比如 MyISAM 引擎就不支持行锁。  支持行锁意味着并发控制只能使用表锁,对于这种引擎的表,同一张表上任何时刻只能有一个更新在执行,这就会影响到业务并发度。 两阶段锁  在下面的操作序列中,事务 B 的 update 语句执行时会是什么现象呢?假设字段 id 是表 t 的主键。  这个问题的结论取决于事务 A 在执行完两条 update 语句后,持有哪些锁,以及在什么时候释放。你可以验证一下:实际上事务 B 的 update 语句会被阻塞,直到事务 A 执行 commit 之后,事务 B 才能继续执行。  也就是说,在 InnoDB 事务中,行锁是在需要的时候才加上的,但并不是不需要了就立刻释放,而是要等到事务结束时才释放。这个就是两阶段锁协议。 在事务中合理优化两段式提交  如果你的事务中需要锁多个行,要把最可能造成锁冲突、最可能影响并发度的....

全局锁、表级锁

数据库中的锁  数据库锁设计的初衷是处理并发问题。作为多用户共享的资源,当出现并发访问的时候,数据库需要合理地控制资源的访问规则。而锁就是用来实现这些访问规则的重要数据结构。  MySQL 里面的锁大致可以分成全局锁、表级锁和行锁三类 为什么需要锁  假设你现在要维护一个课程购买系统,关注的是用户账户余额表和用户课程表。  现在发起一个逻辑备份。假设备份期间,有一个用户,他购买了一门课程,业务逻辑里就要扣掉他的余额,然后往已购课程里面加上一门课。  如果时间顺序上是先备份账户余额表 (u_account) => 用户购买 => 备份用户课程表 (u_course),会怎么样呢?如下图所示:  如果用以上备份来恢复数据的话,用户 A 就发现,自己赚了。用户 A 的数据状态是:账户余额没扣,但是用户课程表里面已经多了一门课。 (用户也开心)  如果反过来:备份用户课程表 (u_course) => 用户购买 => 先备份账户余额表 (u_account) ,基于此备份进行恢复数据,会发现用户在没有发生任何购买行为的情况下,原本的余额减少了。(用户无法接受)  也就....

索引

索引简介  索引的出现其实就是为了提高数据查询的效率,就像书的目录一样。  在 MySQL 中,索引是在存储引擎层实现的,所以并没有统一的索引标准,即不同存储引擎的索引的工作方式并不一样。而即使多个存储引擎支持同一种类型的索引,其底层的实现也可能不同。 索引的常见模型  索引的出现是为了提高查询效率,但是实现索引的方式却有很多种,所以这里也就引入了索引模型的概念。可以用于提高读写效率的数据结构很多,例如:哈希表、有序数组、搜索树。  数据库底层存储的核心就是基于这些数据模型的。每碰到一个新数据库,我们需要先关注它的数据模型,这样才能从理论上分析出这个数据库的适用场景。 哈希索引 # 适用场景 * 哈希表这种结构适用于只有等值查询的场景,比如 Memcached 及其他一些 NoSQL 引擎。 # 优点 * 图中四个 ID_card_n 的值并不是递增的(并不是有序的,123456),这样做的好处是增加新的 User 时速度会很快,只需要往后追加。 # 缺点 * 因为不是有序的,所以哈希索引做区间查询的速度是很慢.(如果你现在要找身份证号在[ID_card_X, ID_card_Y]这....

设计模式

简单工厂  由一个工厂对象决定创建出哪一种产品类的实例。  创建型,但不属于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

Transaction  简单来说,事务就是要保证一组数据库操作,要么全部成功,要么全部失败。在 MySQL 中,事务支持是在引擎层实现的。  MySQL是一个支持多引擎的系统,但并不是所有的引擎都支持事务。 隔离性与隔离级别 隔离性(Isolation)  当数据库上有多个事务同时执行的时候,就可能出现脏读(dirty read)、不可重复读(non-repeatable read)、幻读(phantom read)的问题,为了解决这些问题,就有了“隔离级别”的概念。 隔离性与执行效率的平衡点  隔离得越严实,效率就会越低。因此很多时候,我们都要在二者之间寻找一个平衡点。 SQL 标准的事务隔离级别 * 读未提交(read uncommitted) 读未提交是指,一个事务还没提交时,它做的变更就能被别的事务看到。 * 读提交(read committed) 读提交是指,一个事务提交之后,它做的变更才会被其他事务看到。 * 可重复读(repeatable read) 可重复读是指,一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。当然在可重复读隔离级别下,未提交....

数据库设计

数据库设计范式 第一范式  表中的所有字段都是不可再分的。(将复合属性进行拆分) 第二范式  表中必须存在业务主键,并且非主键依赖于全部业务主键。业务主键:那些可以唯一标识出每一行业务数据的列,或是列的组合。  如果表的业务主键只有一个列组成,那么该表原生就符合第二范式。  如果表的业务主键有多个列组成,那么需要把那些非主键依赖于全部业务主键的列从表中拆分出来。 第三范式  表中的非主键列之间不能相互依赖。 范式化设计存在的问题 如何获取出一门课程包括所有章节和小节的信息? # 表关联多,查询性能越差 1. 需要查询的信息分别位于三个不同的表。 2. 并且需要经过2个关联表。 3. 如果需要查询目标数据,就需要用到5个表。 反范式化设计  主要思路是拿空间换时间,或多或少会违反范式化的设计原则,存在一定程度上的数据冗余。 课程章节 数据结构设计流程 1. 业务分析(抽象原始数据) 2. 逻辑设计 范式化设计 反范式化设计 3. 物理设计 存储引擎 数据类型 对象命名 4. 建立库表 业务分析 课程的属性 主标题、副标题、方向、分类、难度、最新、最热、时长、简介、人数、需知、收....

日志系统:redo log、binlog

一条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 这一行....