# 1. 数据库理论

# 1.1. 关系型数据库和非关系型数据及其区别?

# 1.1.1. 关系型数据库,典型,二维表结构

优点:

  1. 易于维护:丰富的完整性(实体、参照、用户定义完整性)降低了数据冗余和数据不一致的概率,都是使用表结构,格式一致
  2. 使用方便:sql语言通用,可用于复杂查询
  3. 复杂操作:支持sql

缺点:

  1. 读写性能比较差,尤其是海量数据的高效率读写
  2. 固定的表结构,灵活度稍欠
  3. 高并发读写需求,硬盘IO是很大的瓶颈

# 1.1.2. 非关系型数据库,可以是文档或者键值对

优点:

  1. 格式灵活:存储的数据格式可以是key,value形式、文档形式、图片形式等,使用灵活,应用场景广泛,而关系型数据库只支持基础类型
  2. 速度快,nosql可以磁盘或者随机存储器作为载体,而关系型数据库只能使用硬盘
  3. 高扩展性
  4. 成本低,部署简单

缺点:

  1. 不提供sql支持
  2. 无事务处理
  3. 数据结构相对复杂,复杂查询稍欠

# 1.2. 事务的原理,特性,事务并发控制

常用的字段、含义和区别 常用引擎区别

# 1.3. 什么是事务(Transaction)?

  • 事务是数据库并发控制的基本单位
  • 事务可以看做是一系列SQL语句的集合
  • 事务必须要么全部执行成功,要么全部执行失败(回滚)
  • 转账操作是事务使用的一个常见场景
    • 转账分两步:A减去50元,B加上50元,只执行了一个就必须回滚

# 1.4. 事务的ACID特性

  1. 原子性(Atomicity):一个事务中所有操作全部完成或失败 [ˌætəˈmɪsɪti]
  2. 一致性(Consistency):事务开始和结束之后数据完整性没有被破坏
  3. 隔离性(Isolatiion):允许多个事务同时对数据库修改和读写(保证性能)
  4. 持久性(Durability):事务结束之后,修改是永久的不会丢失(持久化到磁盘,内存是一时的,断电就消失)

目前主要有两种方式实现ACID:第一种是Write ahead logging,也就是日志式的方式(现代数据库均基于这种方式)。第二种是Shadow paging

# 1.5. 如果不对事务进行并发控制可能产生哪些问题?

  • 幻读(phantom read):一个事务第二次查出第一次没有的结果(别的事务已经插入了一些数据)
  • 非重复读(nonrepeatable read):一个事务重复读两次得到两次不同的结果(读取操作的结果是不可重复的)
  • 脏读(dirty read):一个事务读取到另一个事务没有提交的修改
  • 丢失修改:并发写入造成其中一些修改丢失

# 1.6. 四种事务隔离级别

  1. 读未提交(read uncommitted):别的事务可以读取到未提交改变
  2. 读已提交(read committed):只能读取已经提交的数据
  3. 可重复读(repeatable read):同一个事务先后查询结果一样(InnoDB默认可重复读)
  4. 串行化(Serializable):事务完全串行化的执行,隔离级别最高,执行效率最低

# 1.7. 如何解决高并发场景下的插入重复?

高并发场景下,写入数据库会有数据重复问题

  1. 使用数据库唯一索引(分库分表时无法使用)
  2. 使用队列异步写入
  3. 使用redis实现分布式锁(事务持有锁,插入完成后释放锁)

# 1.8. 乐观锁和悲观锁

  • 悲观锁是先获取锁在进行操作。一锁二查三更新,select for update
  • 乐观锁先修改,更新的时候发现数据已经变了就回滚(check and set)(假设我改的时候别人不会改)
  • 乐观锁通过版本号或者时间戳实现

根据响应速度、冲突频率、重试代价来判断使用哪一种锁

# 1.9. MySQL数据类型

三种类型:字符串、数值、日期

# 1.9.1. 字符串(文本):

字符串

  • varchar会额外拿出1个字节或2个字节存储字符串的长度。列长度小于255字节,使用1个字节表示,否则用2个
  • varchar长度,如果存储内容超出指定长度,会被截断
  • char适合存储密码,md5都变成32位了
  • varchar只会把大的变小,不会把小的变大。这是和char的区别。

MySQL 5.0 以上的版本:

  1. 一个汉字占多少长度与编码有关:
    1. UTF-8:一个汉字=3个字节
    2. GBK:一个汉字=2个字节
  2. varchar(n) 表示 n 个字符,无论汉字和英文,Mysql 都能存入 n 个字符,仅是实际字节长度有所区别
  3. MySQL 检查长度,可用 SQL 语言来查看:
select LENGTH(fieldname) from tablename
# 1.9.1.1. varchar(N)varchar2(N) 中的 N 是字符还是字节?
  • MySQL 5.7 版本 varchar(N)字段类型中的 N 是字符数,其中一个汉字或是一个字母表示一个字符。
  • Oracle 11g 版本 varchar2(N)和varchar2(N byte)字段类型中的 N 是字节数,其中一个汉字占 2 个字节,一个字母占 1 一个字节。varchar2(N char)字段类型中的 N 是字符数,其中一个汉字占 1 个字符,一个字母占 1 一个字符。
  • 所以针对不同的关系型数据库管理系统,字段类型varchar(N) 中的N表示的含义是不同的,以实际情况而定。所以不要轻易下结论,实践是检验真理的唯一标准。

# 1.9.2. char、varchar 与 text

关于 char、varchar 与 text 平时没有太在意,一般来说,可能现在大家都是用 varchar。但是当要存储的内容比较大时,究竟是选择 varchar 还是 text 呢?

这三种类型比较:

  1. char: char 不用多说了,它是定长格式的,但是长度范围是 0~255. 当你想要储存一个长度不足 255 的字符时,Mysql 会用空格来填充剩下的字符。因此在读取数据时,char 类型的数据要进行处理,把后面的空格去除。
  2. varchar: 在 5.0.3 以下的版本中的最大长度限制为 255,而在 5.0.3 及以上的版本中,varchar 数据类型的长度支持到了 65535,也就是说可以存放 65532 个字节(注意是字节而不是字符!!!)的数据(起始位和结束位占去了3个字节),也就是说,在 5.0.3 以下版本中需要使用固定的 TEXT 或 BLOB 格式存放的数据可以在高版本中使用可变长的 varchar 来存放,这样就能有效的减少数据库文件的大小。
  3. text: 与 char 和 varchar 不同的是,text 不可以有默认值,其最大长度是 2 的 16 次方-1

按照查询速度: char最快, varchar次之,text最慢。

总结起来,有几点:

  1. 经常变化的字段用 varchar
  2. 知道固定长度的用 char
  3. 尽量用 varchar
  4. 超过 255 字符的只能用 varchar 或者 text
  5. 能用 varchar 的地方不用 text

# 1.9.3. 数值

字符串

  1. 括号里的Length指的是数据库里面的可见长度
  2. Int(10)不是10个字节,它始终是4个字节,而是显示的时候,在数据库中最小显示10个字符宽

MySQL会自动填充0,zerofill

  • Int(3) 1234 =>1234
  • int(5) 12 => 00012

难道我们建了int(3),就不能存放数据1234了?

  • 并不是,还可以插入更多位的数字。3只是代表显示宽度。

decimal可以理解成当做字符串进行处理,floatdouble效率更高

在mysql的数据类型中浮点型分为两种,float()double()类型,定点型为decimal()

数据类型(M,D)

  • M:精度,数据的总长度;
  • D:标度,小数点后的长度; 其区别在于:
  • 当不指定精度时,FloatDouble默认会保存实际精度,而Decimal默认是整数;
  • 当标度不够时,都会四舍五入,但Decimal会警告信息。

# 1.9.4. 日期和时间:

字符串

# 1.10. InnoDB 和 MyISAM的区别

  1. MyISAM不支持事务,InnoDB支持事务
  2. MyISAM不支持外键,InnoDB支持外键
  3. MyISAM只支持表锁,InnoDB支持行锁和表锁
  4. InnoDB不支持全文索引,MyISAM支持全文索引

# 1.11. 索引原理及优化

什么是索引?

  • 索引是数据表中一个或者多个列进行排序的数据结构
  • 索引能够大幅提升检索速度
  • 创建、更新索引本身也会耗费时间和空间

# 1.12. 查找结构进化史:

  1. 线性查找:一个个找;实现简单;太慢
  2. 二分查找:有序;简单;要求是有序的,插入特别慢
  3. HASH:查询快;占用空间,不太适合存储大规模数据
  4. 二叉查找树:插入和查询很快(log(n));无法存储大规模数据,复杂度退化(单边增长,变成线性复杂度)
  5. 平衡树:解决bst退化的问题,树是平衡的;依然无法解决节点太多、树高度非常深的问题
  6. 多路查找树:一个父亲多个孩子节点(度);节点过多时,树不会特别深
  7. 多路平衡查找树:B-Tree

# 1.13. 什么是B-Tree?

  • 多路平衡查找树(每个节点最多 m 个孩子(m>=2),称为 m 阶或者度)
  • 叶节点具有相同的深度
  • 节点中的数据 key 从左到右是递增的

B-Tree无法实现范围查找

B-Tree

# 1.14. B+Tree

  • B+树是 B-Tree 的变形
  • Mysql实际使用的 B+Tree 作为索引的数据结构
  • 只在叶子节点带有指向记录的指针(为什么?可以增加树的度)
  • 叶子结点通过指针相连(为什么?实现范围查询)

B+Tree示例:

B-Tree

阶不是越大越好,和磁盘块大小有关,让操作系统更友好的加载和缓存数据

# 1.15. mysql索引类型

  1. 普通索引(create index
  2. 唯一索引,索引列的值必须唯一(create unique index
  3. 多列索引(B+树key的值由多个列组成)
  4. 主键索引(primary key),一个表只能有一个,主键索引一定是唯一索引
  5. 全文索引(fulltest index),InnoDB不支持

# 1.16. 什么时候创建索引?

建表的时候需要根据查询需求来创建索引

  1. 经常用作查询条件的字段(where条件)
  2. 经常用作表连接的字段
  3. 经常出现在order by,group by之后的字段

# 1.17. 创建索引有哪些需要注意的?

最佳实践

  1. 非空字段not null,mysql很难对空值作查询优化(B+树中,空值无法作比较)(很多公司建表规范要求索引字段有默认值)
  2. 区分度高,离散度大,作为索引的字段值尽量不要有大量的相同值(B+树key相同)
  3. 索引的长度不要太长(比较耗费时间)(索引在B+树中作为key值存在,int值很容易比较,字符串很难比较)

# 1.18. 索引什么时候失效?

记忆口诀:模糊匹配、类型隐转、最左匹配

  1. %开头的 LIKE 语句,模糊搜索(三者都是B+树key没办法直接比较)
  2. 出现类型隐式转换(动态语言中需要注意,如python
  3. 没有满足最左前缀原则

# 1.19. 什么是聚集索引和非聚集索引?

  • 聚集还是非聚集指的是B+Tree叶节点存的是指针还是数据记录
  • MyISAM索引和数据分离,使用的是非聚集索引
  • InnoDB数据文件就是索引文件,主键索引就是聚集索引

非聚集索引:

非聚集索引

聚集索引:

聚集索引

InnoDB中的辅助索引,先找到主键以后再根据主键找到数据

聚集索引与辅助索引:

聚集索引与辅助索引

# 1.20. 如何排查慢查询?

  1. 慢查询通常是缺少索引,索引不合理或者业务代码实现导致
  2. slow_query_log_file开启并且查询慢查询日志
  3. 通过explain排查索引问题
  4. 调整数据修改索引;业务代码层限制不合理访问(一次获取太多可以分页)

Mysql索引,如果是分布式系统下如何生成数据库的自增id?

# 1.21. Mysql语句编写

  1. 内连接(Inner Join):两个表都存在匹配时,才会返回匹配行
  2. 外连接(left/right join):返回一个表的行,即使另一个没有匹配
  3. 全连接(Full join):只要某一个表存在匹配就返回

内连接:“交集”

select * from A inner join B on a.id=b.id

左连接:返回左表中所有记录,即使右表中没有匹配的记录

  • 没有匹配到的字段会设置成null

# 1.22. 缓存及Redis

缓存的使用场景? Redis常用数据类型,使用方式? 缓存使用问题:数据一致性问题;缓存穿透、击穿、雪崩问题

什么是缓存,为什么使用缓存?

  • 内存缓存主要有Redis和Memcached
  • 缓解关系数据库(Mysql)并发访问的压力:热点数据
  • 减少响应的时间:内存IO速度比磁盘快
  • 提升吞吐量:Redis等内存数据库单机就可以支撑很大并发

# 1.23. Redis 和 Memcached 主要区别?

# 1.24. Redis常用数据类型和使用场景?

  1. String(字符串):用来实现简单的kv键值对存储,比如计数器
  2. List(链表):实现双向链表,比如用户的关注,粉丝列表
  3. Hash(哈希表):用来存储彼此相关信息的键值对(HSET name,key [id:usename])
  4. Set(集合):存储不重复元素,比如用户的关注者
  5. Sorted Set(有序集合):实时信息排行榜

Redis内置实现《Redis设计与实现》 Redis各种类型的C底层实现方式

  1. String:整数或者sds(Simple Dynamic String)
  2. List:ziplist或者double linked list
  3. Hash: ziplist或者hashtable
  4. Set:intset(小整数)或者hashtable
  5. SortedSet: skiplist 跳跃表

Ziplist(压缩列表):通过一个连续的内存块实现list结构,其中的每个entry节点头部保存前后节点长度信息,实现双向链表功能

# 1.25. Redis实现的跳跃表是什么结构?

Sorted set为了简化实现,使用skiplist而不是平衡树实现

跳跃表

链表不能随机访问,设置了多层,模拟二分查找

# 1.26. Redis有哪些持久化方式?两种

  • 快照方式:把数据快照放在磁盘二进制文件中,dump.rdb
    • 快照的实现方式是指定时间间隔把Redis数据库状态保存到一个压缩的二进制文件中,能够恢复到制定时间段的版本,宕机的话会丢失很多数据。
  • AOF(Append Only File):每一个写命令追加到appendonly.aof中(宕机不会丢失很多数据,但文件可能较大)

# 1.27. Redis 事务和 Mysql 事务的区别?

  • 将多个请求打包,一次性、按序执行多个命令的机制
  • Redis通过MULTI,EXEC,WATCH等命令实现事务功能
  • Python redis-py pipeline=conn.pipeline(transaction=True)

# 1.28. Redis如何实现分布式锁?

不同机器、不同进程的线程上进行同步,使用分布式锁 给一个线程指向键值对,其他线程重试或等待

  • 使用setnx实现加锁,可以同时通过expire添加超时时间
  • 锁的value值可以使用一个随机的uuid或者特定命名
  • 释放锁的时候,通过uuid判断是否是该锁,是则执行delete释放锁

当一个线程想要持有锁的时候,就给他设置一个key,当key已经存在时获取失败,进行重试或等待,释放锁时通过delete删除

# 1.29. 使用缓存的模式?

  1. Cache Aside:同时更新缓存和数据库(最常用)
  2. Read/Write Through:先更新缓存,缓存负责同步更新数据库
  3. Write Behind Caching:先更新缓存,缓存定期异步更新数据库

涉及到缓存和数据库一致的问题,先更新数据库后更新缓存,并发写操作可能导致缓存读取的是脏数据

# 1.30. 如何解决缓存穿透问题?

大量查询不到的数据的请求落到后端数据库,数据库压力增大

  • 由于大量缓存查不到就去数据库取,数据库也没有要查的数据(比如无脑爬虫通过自增id的方式爬取网站,网站差不到相关id的数据)
  • 解决:对于没查到返回为None的数据也缓存
    • 插入数据的时候删除相应缓存,或者设置较短的超时时间

# 1.31. 如何解决缓存击穿问题?(微博热点)

  1. 某些非常热点的数据key过期,大量请求达到后端数据库
  2. 分布式锁:获取锁的线程从数据库拉数据更新缓存,其他线程等待
  3. 异步后台更新:后台任务针对过期的key自动刷新

# 1.32. 如何解决缓存雪崩问题?

缓存不可用或者大量缓存key同时失效,大量请求直接打到数据库

  1. 多级缓存:不同级别的key设置不同的超时时间
  2. 随机超时:key的超时时间随机设置,防止同时超时
  3. 架构层:提升系统可用性,监控、报警完善

# 1.33. 数据库设计

目标:

  1. 减少数据冗余
  2. 避免数据维护异常
  3. 节约存储空间
  4. 高效的访问

步骤:

  1. 需求分析
  2. 逻辑设计(ER图)
  3. 物理设计
  4. 维护优化

# 1.34. Mysql查看支持的引擎

show engines;

以下两点必须使用 InnoDB:

  1. 可靠性高或者要求事务处理,则使用 InnoDB。这个是必须的。
  2. 表更新和查询都相当的频繁,并且表锁定的机会比较大的情况指定 InnoDB 数据引擎的创建。

对比之下,MyISAM 的使用场景:

  1. 做很多 count 的计算的。如一些日志,调查的业务表。
  2. 插入修改不频繁,查询非常频繁的。

# 1.35. 什么是存储过程?有哪些优缺点?

存储过程是一些预编译的SQL语句。 更加直白的理解:存储过程可以说是一个记录集,它是由一些T-SQL语句组成的代码块,这些T-SQL语句代码像一个方法一样实现一些功能(对单表或多表的增删改查),然后再给这个代码块取一个名字,在用到这个功能的时候调用他就行了。

  1. 存储过程是一个预编译的代码块,执行效率比较高
  2. 一个存储过程替代大量T_SQL语句 ,可以降低网络通信量,提高通信速率
  3. 可以一定程度上确保数据安全

# 1.36. 什么是视图?以及视图的使用场景有哪些?

视图是一种虚拟的表,具有和物理表相同的功能。可以对视图进行增,改,查,操作,试图通常是有一个表或者多个表的行或列的子集。对视图的修改不影响基本表。它使得我们获取数据更容易,相比多表查询。

  1. 只暴露部分字段给访问者,所以就建一个虚表,就是视图。
  2. 查询的数据来源于不同的表,而查询者希望以统一的方式查询,这样也可以建立一个视图,把多个表查询结果联合起来,查询者只需要直接从视图中获取数据,不必考虑数据来源于不同表所带来的差异

# 1.37. 说一说三个范式

  1. 第一范式(1NF):数据库表中的字段都是单一属性的,不可再分。这个单一属性由基本类型构成,包括整型、实数、字符型、逻辑型、日期型等。

  2. 第二范式(2NF):数据库表中不存在非关键字段对任一候选关键字段的部分函数依赖(部分函数依赖指的是存在组合关键字中的某些字段决定非关键字段的情况),也即所有非关键字段都完全依赖于任意一组候选关键字。

  3. 第三范式(3NF):在第二范式的基础上,数据表中如果不存在非关键字段对任一候选关键字段的传递函数依赖则符合第三范式。所谓传递函数依赖,指的是如果存在"A → B → C"的决定关系,则C传递函数依赖于A。因此,满足第三范式的数据库表应该不存在如下依赖关系: 关键字段 → 非关键字段x → 非关键字段y

# 1.38. Oracle和MySQL的区别

  1. Oracle是大型数据库而Mysql是中小型数据库,Oracle市场占有率达40%,Mysql只有 20% 左右,同时Mysql是开源的而 Oracle 价格非常高。

  2. Oracle支持大并发,大访问量,是OLTP最好的工具。

  3. 安装所用的空间差别也是很大的,Mysql安装完后才152M,而Oracle有3G左右,且使用的时候Oracle占用特别大的内存空间和其他机器性能。

  4. Oracle也Mysql操作上的一些区别

  • ①主键 Mysql一般使用自动增长类型,在创建表时只要指定表的主键为auto increment,插入记录时,不需要再指定该记录的主键值,Mysql将自动增长; Oracle没有自动增长类型,主键一般使用的序列,插入记录时将序列号的下一个值付给该字段即可;只是ORM框架是只要是native主键生成策略即可。
  • ②单引号的处理:MYSQL里可以用双引号包起字符串,ORACLE里只可以用单引号包起字符串。在插入和修改字符串前必须做单引号的替换:把所有出现的一个单引号替换成两个单引号。
  • ③翻页的SQL语句的处理:MYSQL处理翻页的SQL语句比较简单,用LIMIT 开始位置, 记录个数;ORACLE处理翻页的SQL语句就比较繁琐了。每个结果集只有一个ROWNUM字段标明它的位置,并且只能用ROWNUM<100,不能用ROWNUM>80
  • ④ 长字符串的处理:长字符串的处理ORACLE也有它特殊的地方。INSERT和UPDATE时最大可操作的字符串长度小于等于4000个单字节,,如果要插入更长的字符串,请考虑字段用CLOB类型,方法借用ORACLE里自带的DBMS_LOB程序包。插入修改记录前一定要做进行非空和长度判断,不能为空的字段值和超出长度字段值都应该提出警告,返回上次操作。
  • ⑤空字符的处理:MYSQL的非空字段也有空的内容,ORACLE里定义了非空字段就不容许有空的内容。按MYSQL的NOT NULL来定义ORACLE表结构,导数据的时候会产生错误。因此导数据时要对空字符进行判断,如果为NULL或空字符,需要把它改成一个空格的字符串。
  • ⑥字符串的模糊比较 MYSQL里用 字段名 like '%字符串%',ORACLE里也可以用 字段名 like '%字符串%', 但这种方法不能使用索引, 速度不快。
  • ⑦Oracle实现了ANSII SQL中大部分功能,如,事务的隔离级别、传播特性等,而Mysql在这方面还是比较的弱

# 什么是ORM?

Object Relational Mapping对象关系映射

  1. 用于实现业务对象和数据表中的字段映射(sqlalchemy peewee
  2. 优势:代码更加面向对象,代码量更少,灵活性高,提升开发效果

# ORM好处

  1. 隔离数据库和数据库版本之间的差异
  2. 便于维护
  3. ORM会提供防sql注入等功能
  4. 变量传递式的调用更加简单

# 为什么选择peewee

  1. 三个常用的ORM,django ormsqlalchemypeewee
  2. 简单、灵活、申明方式和djangoorm接近
  3. 社区活跃