跳到主要内容

基础2

数据库三范式,根据某个场景设计数据表?

  • 所有字段值都是不可分解的原子值。
  • 在一个数据库表中,一个表中只能保存一种数据,不可以把多种数据保存在同一张数据库表中。
  • 数据表中的每一列数据都和主键直接相关,而不能间接相关。

第一范式(确保每列保持原子性)

第一范式是最基本的范式。如果数据库表中的所有字段值都是不可分解的原子值,就说明该数据库表满足了第一范式

第一范式的合理遵循需要根据系统的实际需求来定。比如某些数据库系统中需要用到“地址”这个属性,本来直接将“地址”属性设计成一个数据库表的字段就行。但是如果系统经常会访问“地址”属性中的“城市”部分,那么就非要将“地址”这个属性重新拆分为省份、城市、详细地址等多个部分进行存储,这样在对地址中某一部分操作的时候将非常方便。这样设计才算满足了数据库的第一范式,如下表所示。

上表所示的用户信息遵循了第一范式的要求,这样在对用户使用城市进行分类的时候就非常方便,也提高了数据库的性能。

第二范式(确保表中的每列都和主键相关)

第二范式在第一范式的基础之上更进一层。第二范式需要确保数据库表中的每一列都和主键相关,而不能只与主键的某一部分相关(主要针对联合主键而言)。也就是说在一个数据库表中,一个表中只能保存一种数据,不可以把多种数据保存在同一张数据库表中

比如要设计一个订单信息表,因为订单中可能会有多种商品,所以要将订单编号和商品编号作为数据库表的联合主键

第三范式(确保每列都和主键列直接相关,而不是间接相关)

第三范式需要确保数据表中的每一列数据都和主键直接相关,而不能间接相关

比如在设计一个订单数据表的时候,可以将客户编号作为一个外键和订单表建立相应的关系。而不可以在订单表中添加关于客户其它信息(比如姓名、所属公司等)的字段。

数据库的读写分离、主从复制,主从复制分析的 7 个问题?

主从复制的几种方式

同步复制

  • 所谓的同步复制,意思是 master 的变化,必须等待 slave-1,slave-2,…,slave-n 完成后才能返回。 这样,显然不可取,也不是 MySQL 复制的默认设置。比如,在 WEB 前端页面上,用户增加了条记录,需要等待很长时间。

异步复制

  • 如同 AJAX 请求一样。master 只需要完成自己的数据库操作即可。至于 slaves 是否收到二进制日志,是否完成操作,不用关心,MySQL 的默认设置。

半同步复制

  • master 只保证 slaves 中的一个操作成功,就返回,其他 slave 不管。 这个功能,是由 google 为 MySQL 引入的。

主从复制分析的 7 个问题

问题 1:master 的写操作,slaves 被动的进行一样的操作,保持数据一致性,那么 slave 是否可以主动的进行写操作?

假设 slave 可以主动的进行写操作,slave 又无法通知 master,这样就导致了 master 和 slave 数据不一致了。因此slave 不应该进行写操作,至少是 slave 上涉及到复制的数据库不可以写。实际上,这里已经揭示了读写分离的概念

问题 2:主从复制中,可以有 N 个 slave,可是这些 slave 又不能进行写操作,要他们干嘛?

以实现数据备份

类似于高可用的功能,一旦 master 挂了,可以让 slave 顶上去,同时 slave 提升为 master

异地容灾,比如 master 在北京,地震挂了,那么在上海的 slave 还可以继续。

主要用于实现 scale out,分担负载,可以将读的任务分散到 slaves 上

很可能的情况是,一个系统的读操作远远多于写操作,因此写操作发向 master,读操作发向 slaves 进行操作

问题 3:主从复制中有 master,slave1,slave2,…等等这么多 MySQL 数据库,那比如一个 JAVA WEB 应用到底应该连接哪个数据库?

当 然,我们在应用程序中可以这样,insert/delete/update这些更新数据库的操作,用connection(for master)进行操作,select 用 connection(for slaves)进行操作。那我们的应用程序还要完成怎么从 slaves 选择一个来执行 select,例如使用简单的轮循算法

这样的话,相当于应用程序完成了 SQL 语句的路由,而且与 MySQL 的主从复制架构非常关联,一旦 master 挂了,某些 slave 挂了,那么应用程序就要修改了。能不能让应用程序与 MySQL 的主从复制架构没有什么太多关系呢?

找一个组件application program 只需要与它打交道,用它来完成 MySQL 的代理,实现 SQL 语句的路由

MySQL proxy 并不负责,怎么从众多的 slaves 挑一个?可以交给另一个组件(比如 haproxy)来完成。

这就是所谓的MySQL READ WRITE SPLITE,MySQL的读写分离。

问题 4:如果 MySQL proxy , direct , master 他们中的某些挂了怎么办?

总统一般都会弄个副总统,以防不测。同样的,可以给这些关键的节点来个备份。

问题 5:当 master 的二进制日志每产生一个事件,都需要发往 slave,如果我们有 N 个 slave,那是发 N 次,还是只发一次?

如果只发一次,发给了 slave-1,那 slave-2,slave-3,…它们怎么办?

显 然,应该发 N 次。实际上,在 MySQL master 内部,维护 N 个线程,每一个线程负责将二进制日志文件发往对应的 slave。master 既要负责写操作,还的维护 N 个线程,负担会很重。可以这样,slave-1 是 master 的从,slave-1 又是 slave-2,slave-3,…的主,同时 slave-1 不再负责 select。 slave-1 将 master 的复制线程的负担,转移到自己的身上这就是所谓的多级复制的概念

问题 6:当一个 select 发往 MySQL proxy,可能这次由 slave-2 响应,下次由 slave-3 响应,这样的话,就无法利用查询缓存了。

应该找一个共享式的缓存,比如 memcache 来解决。将 slave-2,slave-3,…这些查询的结果都缓存至 mamcache 中。

问题 7:随着应用的日益增长,读操作很多,我们可以扩展 slave,但是如果 master 满足不了写操作了,怎么办呢?

scale on ?更好的服务器? 没有最好的,只有更好的,太贵了。。。

scale out ? 主从复制架构已经满足不了。

可以分库【垂直拆分】,分表【水平拆分】。

使用 explain 优化 sql 和索引?

对于复杂、效率低的 sql 语句,我们通常是使用 explain sql 来分析 sql 语句,这个语句可以打印出,语句的执行。这样方便我们分析,进行优化

  • table:显示这一行的数据是关于哪张表的
  • type:这是重要的列,显示连接使用了何种类型。从最好到最差的连接类型为const、eq_reg、ref、range、indexALL
  • all: full table scan ;MySQL 将遍历全表以找到匹配的行;
  • index : index scan; index 和 all 的区别在于 index 类型只遍历索引;
  • range:索引范围扫描,对索引的扫描开始于某一点,返回匹配值的行,常见与between ,< ,>等查询;
  • ref:非唯一性索引扫描,返回匹配某个单独值的所有行,常见于使用非唯一索引即唯一索引的非唯一前缀进行查找;
  • eq_ref:唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配,常用于主键或者唯一索引扫描;
  • const,system:当 MySQL 对某查询某部分进行优化,并转为一个常量时,使用这些访问类型。如果将主键置于 where 列表中,MySQL 就能将该查询转化为一个常量。
  • possible_keys:显示可能应用在这张表中的索引。如果为空,没有可能的索引。可以为相关的域从 WHERE 语句中选择一个合适的语句
  • key实际使用的索引。如果为 NULL,则没有使用索引。很少的情况下,MySQL 会选择优化不足的索引。这种情况下,可以在 SELECT 语句中使用 USE INDEX(indexname)来强制使用一个索引或者用 IGNORE INDEX(indexname)来强制 MySQL 忽略索引
  • key_len使用的索引的长度。在不损失精确性的情况下,长度越短越好
  • ref:显示索引的哪一列被使用了,如果可能的话,是一个常数
  • rows:MySQL 认为必须检查的用来返回请求数据的行数
  • Extra:关于 MySQL 如何解析查询的额外信息。将在表 4.3 中讨论,但这里可以看到的坏的例子是 Using temporary 和 Using filesort,意思 MySQL 根本不能使用索引,结果是检索会很慢。

MySQL 慢查询怎么解决?

  • slow*query*log 慢查询开启状态。
  • slow*query*log_file 慢查询日志存放的位置(这个目录需要 MySQL 的运行帐号的可写权限,一般设置为 MySQL 的数据存放目录)。
  • long*query*time 查询超过多少秒才记录。

什么是 内连接、外连接、交叉连接、笛卡尔积等?

内连接

内连接查询操作列出与连接条件匹配的数据行,它使用比较运算符比较被连接列的 列值。

内连接分三种

  1. 等值连接:在连接条件中使用等于号(=)运算符比较被连接列的列值,其查询结 果中列出被连接表中的所有列,包括其中的重复列。

例,下面使用等值连接列出 authors 和 publishers 表中位于同一城市的作者和出版社:

SELECT * FROM authors AS a INNER JOIN publishers AS p ON a.city=p.city 
  1. 不等连接: 在连接条件使用除等于运算符以外的其它比较运算符比较被连接的 列的列值。这些运算符包括>、>=、<=、<、!>、!<<>
  2. 自然连接:在连接条件中使用等于(=)运算符比较被连接列的列值,但它使用选 择列表指出查询结果集合中所包括的列,并删除连接表中的重复列。

例,在选择列表中删除 authors 和 publishers 表中重复列(city 和 state):

SELECT a.*,p.pub_id,p.pub_name,p.country FROM authors AS a INNER JOIN publishers AS p ON a.city=p.city

外连接

外连接,返回到查询结果集合中的不仅包含符合连接条件的行,而且还包括左表(左外连接或左连接)、右表(右外连接或右连接)或两个边接表(全外连接)中的所有数据行。   

  • left join(左联接) 返回包括左表中的所有记录和右表中联结字段相等的记录。
  • right join(右联接) 返回包括右表中的所有记录和左表中联结字段相等的记录。

例如 1

SELECT a.*,b.* FROM luntan LEFT JOIN usertable as b ON a.username=b.username

例如 2

SELECT a.*,b.* FROM city as a FULL OUTER JOIN user as b ON a.username=b.username

交叉连接

交叉连接不带 WHERE 子句它返回被连接的两个表所有数据行的“笛卡尔积”,返回到结果集合中的数据行数等于第一个表中符合查询条件的数据行数乘以第二个表中符合查询条件的数据行数。

例,titles 表中有 6 类图书,而 publishers 表中有 8 家出版社,则下 列交叉连接检索到的记录数将等于 6*8=48 行。   

例如:

SELECT type,pub_name FROM titles CROSS JOIN publishers  ORDER BY type

笛卡尔积

笛卡尔积是两个表每一个字段相互匹配,去掉where 或者inner join的等值 得出的结果就是笛卡尔积。笛卡尔积也等同于交叉连接

总结

  • 内连接: 只连接匹配的行。
  • 左外连接: 包含左边表的全部行(不管右边的表中是否存在与它们匹配的行),以及右边表中全部匹配的行。
  • 右外连接: 包含右边表的全部行(不管左边的表中是否存在与它们匹配的行),以及左边表中全部匹配的行。
  • 全外连接: 包含左、右两个表的全部行,不管另外一边的表中是否存在与它们匹配的行。
  • 交叉连接 生成笛卡尔积-它不使用任何匹配或者选取条件,而是直接将一个数据源中的每个行与另一个数据源的每个行都一一匹配。

MySQL 都有什么锁,死锁判定原理和具体场景,死锁怎么解决?

MySQL 都有什么锁

MySQL 有三种锁的级别:页级、表级、行级

  • 表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。
  • 行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。
  • 页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般

什么情况下会造成死锁

  • 所谓死锁: 是指两个或两个以上的进程在执行过程中。
  • 因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。
  • 此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等竺的进程称为死锁进程。
  • 表级锁不会产生死锁.所以解决死锁主要还是针对于最常用的 InnoDB。

死锁的关键在于:两个(或以上)的 Session 加锁的顺序不一致。

那么对应的解决死锁问题的关键就是:让不同的 session加锁有次序

死锁的解决办法

  • 查出的线程杀死 kill
SELECT trx_MySQL_thread_id FROM information_schema.INNODB_TRX;
  • 设置锁的超时时间

Innodb 行锁的等待时间,单位秒。可在会话级别设置,RDS 实例该参数的默认值为 50(秒)。

生产环境不推荐使用过大的 innodb_lock_wait_timeout参数值

该参数支持在会话级别修改,方便应用在会话级别单独设置某些特殊操作的行锁等待超时时间,如下:

set innodb_lock_wait_timeout=1000; —设置当前会话 Innodb 行锁等待超时时间,单位秒。

varchar 和 char 的使用场景?

char 的长度是不可变的,而 varchar 的长度是可变的

定义一个 char[10]和 varchar[10]。

如果存进去的是‘csdn’,那么 char 所占的长度依然为 10,除了字符‘csdn’外,后面跟六个空格,varchar 就立马把长度变为 4 了,取数据的时候,char 类型的要用 trim()去掉多余的空格,而 varchar 是不需要的

char 的存取数度还是要比 varchar 要快得多,因为其长度固定,方便程序的存储与查找。

char 也为此付出的是空间的代价,因为其长度固定,所以难免会有多余的空格占位符占据空间,可谓是以空间换取时间效率。

varchar 是以空间效率为首位

char 的存储方式是:对英文字符(ASCII)占用 1 个字节,对一个汉字占用两个字节。

varchar 的存储方式是:对每个英文字符占用 2 个字节,汉字也占用 2 个字节。

两者的存储数据都非 unicode 的字符数据。

19.MySQL 高并发环境解决方案?

MySQL 高并发环境解决方案 分库 分表 分布式 增加二级缓存。。。。。

需求分析:互联网单位 每天大量数据读取,写入,并发性高。

  • 现有解决方式:水平分库分表,由单点分布到多点数据库中,从而降低单点数据库压力。
  • 集群方案:解决 DB 宕机带来的单点 DB 不能访问问题。
  • 读写分离策略:极大限度提高了应用中 Read 数据的速度和并发量。无法解决高写入压力。

数据库崩溃时事务的恢复机制(REDO 日志和 UNDO 日志)?

Undo Log

Undo Log 是为了实现事务的原子性,在 MySQL 数据库 InnoDB 存储引擎中,还用了 Undo Log 来实现多版本并发控制(简称:MVCC)。

  • 事务的原子性(Atomicity)事务中的所有操作,要么全部完成,要么不做任何操作,不能只做部分操作。如果在执行的过程中发生了错误,要回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过。
  • 原理Undo Log 的原理很简单,为了满足事务的原子性,在操作任何数据之前,首先将数据备份到一个地方(这个存储数据备份的地方称为 UndoLog)。然后进行数据的修改。如果出现了错误或者用户执行了 ROLLBACK 语句,系统可以利用 Undo Log 中的备份将数据恢复到事务开始之前的状态

之所以能同时保证原子性和持久化,是因为以下特点

  • 更新数据前记录 Undo log。
  • 为了保证持久性,必须将数据在事务提交前写到磁盘。只要事务成功提交,数据必然已经持久化。
  • Undo log 必须先于数据持久化到磁盘。如果在 G,H 之间系统崩溃,undo log 是完整的, 可以用来回滚事务
  • 如果在 A-F 之间系统崩溃,因为数据没有持久化到磁盘。所以磁盘上的数据还是保持在事务开始前的状态。

缺陷每个事务提交前将数据和 Undo Log 写入磁盘,这样会导致大量的磁盘 IO,因此性能很低

如果能够将数据缓存一段时间,就能减少 IO 提高性能。但是这样就会丧失事务的持久性。因此引入了另外一种机制来实现持久化,即 Redo Log。

Redo Log

  • 原理和 Undo Log 相反Redo Log 记录的是新数据的备份在事务提交前,只要将 Redo Log 持久化即可,不需要将数据持久化。当系统崩溃时,虽然数据没有持久化,但是 Redo Log 已经持久化。系统可以根据 Redo Log 的内容,将所有数据恢复到最新的状态