数据库笔记

数据库

概述

基本概念

存储过程

在数据库中存储复杂程序,以便外部程序调用的一种数据库对象。存储过程是为了完成特定功能的SQL语句集,经编译创建并保存在数据库中,用户可通过指定存储过程的名称并给定参数来调用执行。

create procedure sp_name(IN p_in INT) ...

# 将语句的结束符号从分号临时改为两个$$(可以是自定义)
delimiter $$
create procedure delete_matches ...
delimiter;
# 将语句的结束符号恢复为分号

完整性约束

完整性约束是在字段上强制执行的数据校验规则,为了防止不规范的数据进入数据库,在用户对数据做增删改时,DBMS自动按照一定的约束条件对数据进行监测,主要是对空值重复值的约束。

  • 实体完整性
    • 规定表的每一行在表中是唯一的实体
  • 完整性
    • 表中的必须满足某种特定的数据类型约束,包括取值范围精度
  • 参照完整性
    • 两个表之间的主键外键的数据应一致,保证表之间的数据一致性
  • 用户自定义完整性
    • 约束条件。如列约束(NOT NULL)表约束 (PRIMARY KEY)

范式

  • 第一范式
    • 属性原子性约束,要求属性具有原子性,不可再分割
    • 通俗:所有字段值都是不可分解的原子值
  • 第二范式
    • 数据库中的非主属性只依赖于主属性
      • 记录唯一性约束,要求记录有唯一标识,即实体的唯一性
    • 通俗:在一个数据库表中,一个表中只能保存一种数据,不可以把多种数据保存在同一张数据库表,确保表中的每列都和主键相关
  • 第三范式
    • 不存在非主属性关键字传递函数依赖关系
      • 字段冗余性约束,要求任何字段不能由其他字段派生出来,要求字段没有冗余
    • 通俗:数据库表中的每一列数据都和主键直接相关,而不能间接相关

满足第三范式,那就一定满足第二范式;满足第二范式,就一定满足第一范式

FAQ

  • 超键,候选键,主键,外键是什么?

    • 超键,能唯一标识某条数据属性的集合
    • 候选键,最小的超键
    • 主键,人为规定的一个候选键
    • 外键,用于描述两个表的关系
  • delete和truncate,drop使用场景

    • drop > truncate > delete
    • delete语句是dml (data maintain language),这个操作会放到rollback segment中,事务提交之后才生效,如果有相应的trigger,执行的时候将会被触发,执行时可带where条件truncatedrop是ddl (data define language),操作立即生效,原数据不放到rollback segment中,不能回滚,操作不触发trigger,执行时不可带where条件

分布式锁

分布式模型下,数据只有一份,需要利用锁的技术控制某一时刻修改数据的进程数。

Redis

  • setnx + expire

MySQL

  • 乐观锁
    • 特点是在提交数据更新之前,每个事务会先检查在该事务读取数据后,有没有其他事务又修改了该数据。如果其他事务有更新的话,那么当前正在提交的事务会进行回滚。乐观锁在数据库上的实现完全是逻辑的,不需要数据库提供特殊的支持。一般的做法是版本号机制,然后按照如下方式实现
    1, select data as old_data, version as old_version from XXX
    2, 根据获取的数据进行业务操作,得到 new_data 和 new_version
    3, update data = new_data, version = new_version where version = old_version
    4,如果更新成功,则操作完成,否则需要回滚并重试 
    
  • 唯一主键
  • 悲观锁
    • 特点是先获取锁,再进行业务操作,通常需要数据库本身提供支持,通过常用的select … for update来实现悲观锁。当数据库执行select … for
      update
      时会获取被select中的数据行的行锁,因此其它并发执行的select for update如果试图选中同一行则会发生排斥(需要等待行锁被释放),因此达到锁的效果。select … for
      update
      获取的行锁会在当前事务结束时自动释放,因此必须在事务中使用。
      • 不同的数据库对select for update的支持是有区别的,例如Oracle支持select for update no wait,表示拿不到锁立刻报错,而不是等待,MySQL就没有no
        wait
        这个选项。MySQL还有个问题是,select for update语句执行中所有扫描过的行都会被锁上,这一点很容易造成问题,因此如果在MySQL中用悲观锁务必要确定走了索引,而不是全表扫描
  • 悲观锁乐观锁是数据库用来保证数据并发安全防止更新丢失的两种方法
    • 响应速度快,选择乐观锁
    • 冲突频率高,选择悲观锁
    • 重试代价高,选择悲观锁

ZooKeeper

  • 创建临时节点

非关系型数据库

Redis

数据结构

  • String 字符串
    • set / get
    • mset / mget 一次存储或获取多个
    • incr / incrby (将字符串值解析成整型,将其加一)
    • decr / decrby
  • List 列表
    • rpush / rpop / lpush / lpop
  • Hash 哈希表
    • hset / hget
  • Set 无序集合
    • sadd / spop
    • smembers / sismember
  • ZSet 有序集合

限流

  • 计数器
    • 使用INCRBYEXPIRE指令,不能做到平滑限制,第1秒的请求全部进去,后面的全部进不去

内存淘汰机制

  • allkeys-lru
    当内存超出限制,在所有key中,移除最少使用的key推荐
  • allkeys-random
    当内存超出限制,在所有key中,随机移除某个key
  • volatile-lru
    当内存超出限制,在设置了过期时间key的字典中,移除最少使用的key
LRU 最近最少使用

除了需要key/value字典外,还需要附加一个链表,链表中的元素按照一定的顺序进行排列。当空间满的时候,会踢掉链表尾部的元素。当字典的某个元素被访问时,它在链表中的位置会被移动到表头。所以链表的元素排列顺序就是元素最近被访问的时间顺序

LFU 最近经常使用

redis4.0后出现,LRU实际上并不精确。LFU使用了莫里斯计数器追踪了key的访问频率,根据频率来淘汰key。

过期策略

redis删除过期键的策略如下

  • 定时删除

    • 优点:保证内存被尽快释放
    • 缺点
      • 创建定时器耗时,影响性能

    给key设置过期时间

    缺点是为每一个键实现一个定时器,会耗费较多的资源

  • 定期删除

    • 优点
      • 解决惰性删除的缺点
      • 解决定时删除的缺点
    • 缺点
      • 内存友好方面,不如定时
      • CPU时间友好方面,不如惰性

    定期扫描一遍expires字典,将已过期的键删除。(在redis.conf配置文件设置hs,1s刷新的频率)

    因为不是扫描所有key,每次是随机抽取一部分,所以缺点是不能保证所有的键过期时被及时删除,需在下一个定期删除区间内被扫描并删除

  • 惰性删除

    • 优点:对CPU占用少,只在拿的时候判断,过期就删除,不返回值
    • 缺点:可能内存泄露,无用的值占用了大量内存

    获取每一个key的时候,判断一下该key是否已经过期,如果已经过期则删除。

    如果一个key一直不使用,即使过了过期时间也会一直占用内存,大量的不使用的key会使得内存暴增。

redis采用了定期删除惰性删除两种策略

关系型数据库

Generic SQL

数据类型

  • TEXT VS BOLB
    • TEXT是一个不区分大小写的BLOB,唯一区别是对BLOB值进行排序和比较时区分大小写,对TEXT值不区分大小写
  • CHAR VS VARCHAR
    • CHAR的长度是不可变的,VARCHAR的长度是可变的
      • 如果char[10],存进去CSDN,那么除了CSDN外,后面跟六个空格,(varchar就立马把长度变成4了),取数据的时候,char类型需要trim()去掉多余的空格,而varchar不需要

索引

负面影响
  • 创建索引维护索引需要耗费时间
  • 索引需要占用物理空间
  • 当对表进行增、删、改的时候,索引也要动态维护
原则
  • 最左匹配
    对a,b,c三列建立联合索引。实际上建立了a、a,b、a,b,c三个索引。因为联合索引,实际查询的时候,多个索引在一个节点上,所以需要逐个字段匹配,先匹配最左边的字段值,即最左匹配

  • 索引个数
    MySQL最多可支持单表创建16个索引,不要过度创建索引,每个额外的索引都要占用额外的磁盘空间,并降低写操作的性能,因为在修改表的内容时,索引必须进行更新,有时可能需要重构,因此,索引越多,所花的时间越长

  • 使用短索引,如果对字符串列进行索引,应该指定一个长度,可节省大量索引空间,提升查询速度

  • 选择正确的列进行索引,比如where子句的列,join子句的列

类型

索引仅能分为聚集索引非聚集索引两种

  • 主键索引
    • 数据列不允许重复,不允许为NULL,一个表只能有一个主键
  • 唯一索引
    • 数据列不允许重复,允许为NULL,一个表允许多个列创建唯一索引
  • 普通索引
    • 数据列允许重复,允许为NULL
  • 全文索引
结构
  • 二叉树

    • 容易退化成链表
  • 红黑树

    • 缺点
      • 要做很多次的IO
    • 优点
      • 解决了二叉树的平衡问题
    • 逻辑约束
      • 节点是红色或黑色
      • 根结点是黑色
      • 每个红色节点的两个字节点都是黑色
      • 新插入的节点默认是红色
      • (同一层不要求同色各层不要求红色黑色相间)
    • 平衡措施
      • 变色
      • 自旋
  • B树与B+树索引

    • B树
      • 度(Degree) - 节点的数据存储个数
      • 叶节点具有相同的深度
      • 叶节点的指针为空
      • 节点中的数据key从左到右递增排列
    • 多路平衡搜索树(每个节点有多个指向),节点有序
    • 不选择B树做索引结构,因为B树的非叶子节点会存储数据,因为空间问题,导致度可能减少,高度就增高
    • B+树
      • 非叶子结点不存储data,只储存key,可以增大度(Degree) 度增大,高度减少
      • 叶子节点不存储指针
      • 顺序访问指针,提高区间访问的性能 (双向链表)
    • B树的优点:一个节点可以储存多个元素,相对于完全平衡二叉树(也是有序,二叉树是有顺序的),整个树的高度就降低了,磁盘IO效率提高了
    • B+树是B树升级版,在所有叶子结点中,增加了指向下一个叶子节点的指针,因此InnoDB建议为大部分表使用默认自增的主键作为主索引,提高范围查找的效率
  • Hash索引

    • 可以快速的精确查询,但是不支持范围查询 (不是有序的),不支持模糊查询
    • 可能存在hash冲突
FAQ
  • 索引下推是什么?

    • 在复合索引的查询中,针对特定的过滤条件而进行减少回表次数而做出的优化 (MySQL 5.6以后)
  • MYSQL查询非常慢怎么找原因?

    • 没有索引,或无法命中索引
    • 内存不足
    • 网络速度慢
    • 一次查询的数据量过大
    • 出现死锁
  • 什么情况下不宜建立索引?

    • 查询很少涉及到的列,或重复值比较多的列
    • 特殊的数据类型,如文本字段 text
  • 回表查询是什么?

    InnoDB有聚集索引普通索引聚集索引必须有,且只有一个,索引的叶子结点存储行记录,所以主键查询非常快。普通索引叶子结点存放主键值,所以普通索引需要先查主键值,再通过聚集索引行记录

  • 索引失效的场景?

    • 违反最左匹配原则 (缺少左边的索引字段)
    • 索引列上做任何操作 (计算、函数)
    • 索引范围条件右边的列 (> <, 本列如有索引则仍生效,总的来说,仍然是联合索引生效)
    • 使用不等于
    • like通配符开头
    • 数据类型出现隐式转换 (如varchar不加单引号可能会自动转换为int)
  • 锁的优化策略

    • 读写分离
    • 分段加锁
    • 减少锁持有的时间
    • 多个线程尽量以相同的顺序去获取资源
  • 如何实现索引覆盖?

    • 将被查询的字段,建立到联合索引
  • 索引覆盖是什么?

    只需要在一棵索引树上,就能获取SQL所需的所有列数据,无需回表。(或explain的输出结果,Extra字段为Using Index时,能够触发索引覆盖)

事务

隔离级别
  • Read uncommitted 读未提交
    • 另一个事务修改了数据,但尚未提交,而本事务中的SELECT会读到这些未被提交的数据
  • Read committed 读已提交/不可重复读
    • 事务A多次读取同一数据,事务B在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果因此本事务先后两次读到的数据结果会不一致
  • Repeatable read 可重复读 MySQL默认隔离级别
    • 在同一个事务里,SELECT的结果是事务开始时时间点的状态,因此,同样的SELECT操作读到的结果会是一致的,但是,会有幻读现象
    • 如何防止幻读
      • 多版本并发控制MVCC
      • 间隙锁(Next-Key Locking)
  • Serializable 串行化
    • 最高的隔离级别,在这个隔离级别下,不会产生任何异常。并发的事务,就像事务是在一个个按照顺序执行一样

j0gc9gmx2e

并发导致的问题
  • 脏读 (读未提交数据)
    • 事务A读取事务B未提交的数据,此时如果事务B回滚,事务A读到的就是脏数据
  • 不可重复读
    • 前后多次读取,数据内容不一致
    • 事务A多次读取同一数据,事务B在事务A多次读取的过程中,对数据做了更新并提交,导致事务A多次读取同一数据时,结果因此本事务先后两次读到的数据结果会不一致
  • 幻读
    • 前后多次读取,数据总量不一致
    • 幻读解决了不重复读,保证了同一事务里,查询的结果都是事务开始时的状态(一致性)
      • 事务T1对一个表中所有的行的某个数据项做了从1修改为2的操作,这时事务T2又对这个表中插入了一行数据项,而这个数据项的数值还是1并且提交给了数据库。而操作事务T1的用户如果再查看刚刚修改的数据,会发现还有跟没有修改的一样,其实这行是事务T2中添加的,就好像产生幻觉一样
  • 幻读 VS 不可重复读
    • 不可重复读针对update操作
    • 幻读针对insert操作、delete操作
    • 解析不可重复读只需锁住满足条件的行,解决幻读需要锁表
ACID特性
  • 原子性 Atomicity

    • 指事务是一个不可分割的工作单位,其中的操作要么做,要么都不做

    • 实现原理:undo log。InnoDB引擎除了二进制日志 bin log错误日志查询日志慢查询日志等,还提供了两种事务性日志:

      • redo log (重做日志,是追加操作,有大小限制,在配置文件)

        • 为了系统崩溃恢复数据用的,让数据库照着日志,把没做好的事情重做一遍,有了redo log,就可以保证即使数据库发生崩溃重启后,之前提交的记录都不会丢失
        • 存放的是数据修改的信息
        • 保证事务持久性
      • undo log (回滚日志,有大小限制,在配置文件)

        • 为了回滚用的,在事务提交之前就开始写数据,万一事务到最后又打算不提交了,要回滚,或者系统崩溃了,这些提前写入的数据就变成了脏数据,这时候就必须用undo log恢复了
        • 事务原子性和隔离性实现的基础。InnoDB实现原子性,靠的是undo log,当事务修改数据库时,InnoDB会生成对应的undo log,当需要回滚数据时,通过查看undo log进行数据回滚。
  • 一致性 Consistency

    • 事务开始前和结束后,数据库的完整性约束没有被破坏
      • 比如A向B转账,不可能A扣了钱,B没有收到

      • 数据库的完整性是指数据的正确性相容性

        • 正确性:数据是符合现实世界语义,反映当前实际状况的

        • 相容性:数据库同一对象在不同关系表中的数据是符合逻辑的

  • 隔离性 Isolation

    • 事务内部的操作与其他事务是隔离的,并发执行的各个事务之间不能相互干扰

    • 分两个方面讨论

      • (一个事务)写操作对(另一个事务)写操作的影响:锁机制保证隔离性

      • (一个事务)写操作对(另一个事务)读操作的影响:MVCC保证隔离性

        查询操作为了避免查询到旧数据、或已经被其他事务更改过的数据,需要满足一下条件

        • 查询时,当前事务的版本号需要大于或等于创建版本号
        • 查询时,当前事务的版本号需要小于删除的版本号

MVCC,多版本并发控制,乐观锁为理论基础。利用在每条数据后面加了隐藏的两列(创建版本号、删除版本号),每个事务在开始的时候都会有一个递增的版本号

  • 持久性 Durability
    • 事务一旦提交,它对数据库的改变就应该是永久性的 (考虑到有缓存的存在,持久性是必须保证数据是写入到磁盘)
    • 实现原理:redo log。InnoDB操作数据库时,会有一个Buffer Pool (缓存池),缓存池的数据再定期刷新到磁盘中。为了预防MySQL宕机导致刷脏失败,InnoDB刷缓存的时候,同时写入redo log,保证数据不会丢失。

MySQL

执行计划

# MySQL中的explain支持 SELECT、DELETE、INSERT、REPLACE、UPDATE
explain select ...
  • select_type SELECT的类型
    • SIMPLE,不涉及UNION或者子查询的简单查询
    • PRIMARY,最外层SELECT
    • UNION,
  • table 数据行的来源表
    • <unionM,N>

常用函数

  • 聚合函数

    • COUNT 统计查询结果的行树
    • MIN 查询指定列的最小值
    • MAX 查询指定列的最大值
    • SUM 求和,返回指定列的总和
    • AVG 求平均值,返回指定列数据的平均值
  • 数值型函数

    • ABS 返回绝对值
    • BIN 返回二进制
    • CEILING 返回大于X的最小整数值
    • EXP 返回E(自然对数的底)的X次方
    • FLOOR 返回小于X的最大整数值
    • GREATEST 返回集合中最大的值
    • LEAST 返回集合中最小的值
    • LN 返回X的自然对数
    • LOG 返回x的以y为底的对数
    • MOD 返回x/y的模
    • PI 返回pi的值
    • RAND 返回0到1内的随机值,可以通过提供一个参数种子,使RAND随机数生成器生成一个指定的值
    • ROUND 返回参数x的四舍五入的有y位小数的值
    • TRUNCATE 返回数字x截短为y位小数的结果
  • 字符串函数

    • LENGTH 计算字符串长度函数,返回字符串的字节长度
    • CONCAT 合并字符串函数
    • INSERT
    • LOWER
    • UPPER
    • LEFT 返回字符串str中最左边的x个字符
    • RIGHT 返回字符串str中最右边的x个字符

引擎

  • 引擎是针对
  • MyISAM 与 InnoDB 引擎 (MySQL 5.5版本开始默认引擎为InnoDB)
    • InnoDB支持事务,MyISAM不支持
    • InnoDB支持外键,MyISAM不支持
    • InnoDB最小的锁粒度是行锁,MyISAM最小的锁粒度是表锁MyISAM插入数据时锁全表
      • 表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低
      • 行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度最高
      • 页面锁:开销和加锁时间介于表锁行锁之间;会出现死锁;锁定粒度介于表锁行锁之间,并发度一般
    • InnoDB主键是聚集索引,MyISAM主键是非聚集索引
    • InnoDB不存放表记录行树,MyISAM会存放表记录行数
    • InnoDB文件,MyISAM会生成frm 表定义文件myd 数据文件myi 索引文件

聚集索引,每个InnoDB表都有一个特殊的索引,称为聚集索引,索引中保存了数据,避免直接读取磁盘。通常,聚集索引与主键义同。

  • 聚集索引
    • InnoDB将primary key列用作聚集索引。
    • 如果没有主键,InnoDB将uniquenot null的列作为聚集索引
    • 如果都没有,InnoDB内部生成一个隐藏的聚集索引
    • MyISAM支持表锁InnoDB支持表锁行锁,默认为行锁
      • 表锁
        • 开销小,加锁快,不会出现死锁。锁定粒度大,发生锁冲突的概率最高,并发量最低
      • 行锁
        • 开销大,加锁慢,会出现死锁。锁粒度小,发生锁冲突的概率小,并发度最高
    • 死锁的关键在于:两个或以上的Session加锁的顺序不一致,那么解决的关键就是:让不同的session加锁有次序,解决办法如下
      • 查出死锁线程并kill
      • 设置锁的超时时间

FAQ

  • InnoDB怎么保证必有主键?
  • InnoDB用自增主键有什么好处?
    • 在储存和检索时,InnoDB会对主键进行物理排序,这对auto_increment_int是个好消息,因为后一次插入的主键位置总是在最后。但是对uuid来说,这却是个坏消息,因为uuid时杂乱无章的,每次插入的主键位置是不确定的,可能在开头,也可能在中间,在进行主键物理排序的时候,势必会造成大量的IO操作影响效率,在数据量不停增长的时候,特别是数据量上了千万条记录的时候,读写性能下降的非常厉害。
  • MySQL有几种锁?
    • 表锁 InnoDB MyISAM
      • 不会出现死锁,粒度较大
    • 行锁 InnoDB
      • 会出现死锁,粒度最小
    • 页面锁 BDB
      • 一次锁定相邻的一组记录
  • 优化专题
    • SQL语句索引优化
      • exists替代in,用not exists替代not in
      • 避免索引失效
    • 数据库表结构优化
    • 系统配置优化
    • 硬件优化

Oracle

FAQ

  • 执行计划怎么看?

    • 先执行 explain plan for ...
    • 执行 select * from table(dbms_xplan.display) 获取结果
    • 结果说明
      • Id,序号,不是执行的先后顺序。执行的先后根据缩进来判断
      • Operation,当前操作的内容
        • table access full。全表扫描。使用多块读操作,一次I/O能读取多块数据块
        • table access by index rowid。通过ROWID的表存取,一次I/O只能读取一个数据块。通过rowid读取表字段,rowid可能是索引键值上的rowid。
        • index unique scan。索引唯一扫描,如果表字段有UNIQUEPRIMARY KEY约束,Oracle实现索引唯一扫描。
        • index range scan。索引范围扫描,最常见的索引扫描方式。在非唯一索引上都使用索引范围扫描。
        • index full scan。索引全扫描,即查询的数据都属于索引字段,一般含有排序操作。
        • index fast full scan。索引快速扫描,如果查询的数据都属于索引字段,并且没有进行排序操作,那么是属于这种情况。条件比较极端,出现比较少。
      • Name,操作的对象名称
      • Rows,当前操作的基数,Oracle估计当前操作的返回结果集
      • Cost,Oracle计算出来的一个数值,用于说明SQL执行的代价
      • Time,Oracle估计当前操作的时间
  • NVL与NVL2两个函数的用法和区别?

    • NVL(expr1, expr2), expr1为null,返回expr2;不为NULL,返回expr1。注意两者类型一致
    • NVL2(expr1, expr2, expr3), expr1不为NULL,返回expr2;为NULL,返回expr3。expr2和expr3类型不同的话,expr3会转换为expr2的类型。

参考链接

快速理解脏读、不可重复读、幻读和MVCC
般含有排序操作。
+ index fast full scan。索引快速扫描,如果查询的数据都属于索引字段,并且没有进行排序操作,那么是属于这种情况。条件比较极端,出现比较少。
+ Name,操作的对象名称
+ Rows,当前操作的基数,Oracle估计当前操作的返回结果集
+ Cost,Oracle计算出来的一个数值,用于说明SQL执行的代价
+ Time,Oracle估计当前操作的时间

  • NVL与NVL2两个函数的用法和区别?
    • NVL(expr1, expr2), expr1为null,返回expr2;不为NULL,返回expr1。注意两者类型一致
    • NVL2(expr1, expr2, expr3), expr1不为NULL,返回expr2;为NULL,返回expr3。expr2和expr3类型不同的话,expr3会转换为expr2的类型。

参考链接

快速理解脏读、不可重复读、幻读和MVCC

  • 23
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

newcih

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值