第一范式就是数据库表的每一列都是不可分割的基本数据项,同一列中不能有多个值。
第二范式要求实体的属性完全依赖于主关键字(即不存在部分依赖)。所谓完全依赖是指不能存在仅依赖主关键字一部分的属性,如果存在,那么这个属性和主关键字的这一部分应该分离出来形成一个新的实体
第三范式要求一个数据库表中不包含已在其它表中已包含的非主关键字信息(即消除传递依赖)。
例如,存在一个部门信息表,其中每个部门有部门编号(dept_id)、部门名称、部门简介等信息。那么在员工信息表中列出部门编号后就不能再将部门名称、部门简介等与部门有关的信息再加入员工信息表中。
更多知识点,参考:高性能Mysql——范式与反范式。
数据冗余(想修改一个属性,就要更新多行数据)
插入异常(想要插入数据,结构因为表设计的问题,导致不能成功插入)
删除异常(只想删除其中的某些数据 ,结果把不该删的也删了)
更新异常(想更新一条数据,结果工作量大,还容易出错)
例如,事务A对数据进行了修改,但是还没有提交,这时事务B读取这个数据,然后事务A回滚,那么事务B取的数据无效。不符合一致性。
解决办法:把数据库的事务隔离级别调整到READ_COMMITTED
例如事务A先读取数据,然后事务B对该同一数据修改并提交,那么事务A再次读取该数据时,由于事务B对该数据的修改,事务A两次读到的的数据可能是不一样的。不符合隔离性。
解决办法:把数据库的事务隔离级别调整到REPEATABLE_READ
在事务A查询结束后,事务B往User表中插入了一条id为1的数据。此时,由于事务A查询到id为1的用户不存在,因此插入1条id为1的数据,报错:主键冲突。不符合隔离性。
解决办法:把数据库的事务隔离级别调整到SERIALIZABLE_READ
解决办法:乐观锁
MySQL数据的四种隔离级别:
① Serializable (串行化):可避免脏读、不可重复读、幻读的发生。
② Repeatable read (可重复读):可避免脏读、不可重复读的发生。
③ Read committed (读已提交):可避免脏读的发生。
④ Read uncommitted (读未提交):最低级别,任何情况都无法保证。
通过SELECT @@transaction_isolation;
(mysql 5.7以下是select @@tx_isolation
)可以查看事务隔离级别:
隔离界别为可重复读,那么我写一个脏读和可重复读的事务:
# 事务A
begin;
select * from orders where id="190827F4AK12R30H";
commit;
# 事务B
begin;
UPDATE orders SET left_msg="new message" where id="190827F4AK12R30H";
select sleep(10);
commit;
查询结果是:尽管事务B修改了left_msg为new message,但是事务A的查询结果是old message。
# 事务A
begin;
select * from orders where id="190827F4AK12R30H";
select sleep(10);
select * from orders where id="190827F4AK12R30H";
commit;
# 事务B
begin;
UPDATE orders SET left_msg="new message" where id="190827F4AK12R30H";
commit;
查询结果是:尽管事务B修改了left_msg为new message,但是两次事务A的查询结果都是 old message。
最后试试幻读:
# 事务A
begin;
select * from stu where id=1;
select sleep(10);
insert into stu(id,name,age) values (1,"xiaoming",13);
commit;
# 事务B
begin;
insert into stu(id,name,age) values (1,"xiaohong",15);
commit;
运行结果:
可把事务级别改成串行set session transaction isolation level SERIALIZABLE;
。
使用set session transaction isolation level REPEATABLE READ;
改回来
CHAR和VARCHAR最大的不同就是一个是固定长度,一个是可变长度。
CHAR存储定长数据很方便,CHAR字段上的索引效率级高,比如定义 char(10),那么不论你存储的数据是否达到了10个字节,都要占去10个字节的空间,不足的自动用空格填充(并且如果存储的char类型的字符串后面有空格的话,innodb会忽略)。
VARCHAR值保存时只保存需要的字符数,另加一个字节来记录长度(如果列声明的长度超过255,则 使用两个字节)。
一般来说,CHAR比VARCHAR更快,因为CHAR是固定长度的,而VARCHAR需要增加一个长度标识,处理时需要多一次运算。但是有例外,参考:MySQL Innodb数据库性能实践——VARCHAR vs CHAR,但是如果使用的是Innodb引擎的话,推荐使用varchar代替char。特别是字符串的平均长度比最大长度要小很多的情况;当然,如果你的字符串本来就很短或者字符串长度固定,例如只有10个字符,那么就优先选CHAR了。
当我们在选择使用int的类型的时候,不论是int(3)还是int(11),它在数据库里面存储的都是4个字节的长度。
如果int的值为10
int(11)显示结果为00000000010
int(3)显示结果为010
就是显示的长度不一样而已,但都是占用四个字节的空间。
类型长度,参考:https://www.runoob.com/mysql/mysql-data-types.html。
更多知识点,参考高性能Mysql——Schema与数据类型优化。
一个完整的日期格式如下:YYYY-MM-DD HH:MM:SS[.fraction],它可分为两部分:date部分和time部分,其中,date部分对应格式中的“YYYY-MM-DD”,time部分对应格式中的“HH:MM:SS[.fraction]”。对于date字段来说,它只支持date部分,如果插入了time部分的内容,它会丢弃掉该部分的内容,并提示一个warning。
timestamp和datetime的不同点:
DATETIME 的日期范围是 1001——9999 年;TIMESTAMP 的时间范围是 1970——2038 年
DATETIME 存储时间与时区无关;TIMESTAMP 存储时间与时区有关,显示的值也依赖于时区
DATETIME 的存储空间为 8 字节;TIMESTAMP 的存储空间为 4 字节
DATETIME 的默认值为 null;TIMESTAMP 的字段默认不为空(not null),默认值为当前时间(CURRENT_TIMESTAMP)
union 在进行表链接后会筛选掉重复的记录,所以在表链接后会对所产生的结果集进行排 序运算,删除重复的记录再返回结果。 union all 则会显示重复结果,只是简单的两个结果合并并返回.所以效率比union高,在保证没有重复数据的情况下用union all.
参考:https://blog.csdn.net/weter_drop/article/details/84729822
delete语句为DML(data maintain Language),这个操作会被放到 rollback segment中,事务提交后才生效。如果有相应的 tigger,执行的时候将被触发。
truncate、drop是DLL(data define language),操作立即生效,原数据不放到 rollback segment中,不能回滚。
MySQL索引使用的数据结构主要有BTree索引 和 哈希索引 。
对于哈希索引来说,底层的数据结构就是哈希表,如果哈希冲突很多的话,一些索引维护操作的代价也会很高。
聚集索引是基于B+树的,非叶节点只做根据主键的索引,而由叶节点来保存主键记录的数据或者指针,这样使得B+树每个非叶子节点所能保存的关键字大大增加,使得B+树层级更少,IO操作也更少。B+树叶子节点的关键字从小到大有序排列,天然具备排序功能;左边的数据会有一个向右的指针,使得全节点遍历更快。
如果没有主键被定义,那么该表的第一个唯一非空索引被作为聚集索引。
如果没有主键也没有合适的唯一索引,那么innodb内部会生成一个隐藏的主键作为聚集索引。
非聚集索引和聚集索引差不多,通过聚集索引可以查到需要查找的数据, 而通过非聚集索引可以查到记录对应的主键值 , 再使用主键的值通过聚集索引查找到需要的数据。
参考:高性能Mysql——创建高性能的索引。
参考:其他索引方式
参考:高性能Mysql——InnoDB事务是如何通过日志来实现的?
两者的对比:
不要轻易相信“MyISAM比InnoDB快”之类的经验之谈,这个结论往往不是绝对的。在很多我们已知场景中,InnoDB的速度都可以让MyISAM望尘莫及,尤其是用到了聚簇索引,或者需要访问的数据都可以放入内存的应用。
选择哪种搜索引擎,应视具体应用而定
①**如果是读多写少的项目,**可以考虑使用MyISAM,MYISAM索引和数据是分开的,而且其索引是压缩的,可以更好地利用内存。所以它的查询性能明显优于INNODB。压缩后的索引也能节约一些磁盘空间。MYISAM拥有全文索引的功能,这可以极大地优化LIKE查询的效率。
②如果你的应用程序一定要使用事务,毫无疑问你要选择INNODB引擎
③如果是用MyISAM的话,merge引擎可以大大加快应用部门的开发速度,他们只要对这个merge表做一些select count(*)操作,非常适合大项目总量约几亿的rows某一类型(如日志,调查统计)的业务表。
一、导致SQL执行慢的原因
1、硬件问题。如网络速度慢,内存不足,I/O吞吐量小,磁盘空间满了等。
2、没有索引或者索引失效。(一般在互联网公司,DBA会在半夜把表锁了,重新建立一遍索引,因为当你删除某个数据的时候,索引的树结构就不完整了。所以互联网公司的数据做的是假删除,一是为了做数据分析,二是为了不破坏索引 )
3、数据过多(分库分表)
4、服务器调优及各个参数设置(调整my.cnf)
二、分析原因时,一定要找切入点
1、先观察,开启慢查询日志,设置相应的阈值(比如超过3秒就是慢SQL),在生产环境跑上个一天过后,看看哪些SQL比较慢。
2、Explain和慢SQL分析。比如SQL语句写的烂,索引没有或失效,关联查询太多(有时候是设计缺陷或者不得以的需求)等等。
3、Show Profile是比Explain更近一步的执行细节,可以查询到执行每一个SQL都干了什么事,这些事分别花了多少秒。
4、找DBA或者运维对MySQL进行服务器的参数调优。
解析:
(1)explain出来的各种item的意义
id:每个被独立执行的操作的标志,表示对象被操作的顺序。一般来说, id 值大,先被执行;如果 id 值相同,则顺序从上到下。
select_type:查询中每个 select 子句的类型。
table:名字,被操作的对象名称,通常的表名(或者别名),但是也有其他格式。
partitions:匹配的分区信息。
type:join 类型。
possible_keys:列出可能会用到的索引。
key:实际用到的索引。
key_len:用到的索引键的平均长度,单位为字节。
ref:表示本行被操作的对象的参照对象,可能是一个常量用 const 表示,也可能是其他表的
key 指向的对象,比如说驱动表的连接列。
rows:估计每次需要扫描的行数。
filtered:rows*filtered/100 表示该步骤最后得到的行数(估计值)。
extra:重要的补充信息。
(2)profile的意义以及使用场景
Profile 用来分析 sql 性能的消耗分布情况。当用 explain 无法解决慢 SQL 的时候,需要用profile 来对 sql 进行更细致的分析,找出 sql 所花的时间大部分消耗在哪个部分,确认 sql的性能瓶颈。
(3)explain 中的索引问题
Explain 结果中,一般来说,要看到尽量用 index(type 为 const、 ref 等, key 列有值),避免使用全表扫描(type 显式为 ALL)。比如说有 where 条件且选择性不错的列,需要建立索引。
被驱动表的连接列,也需要建立索引。被驱动表的连接列也可能会跟 where 条件列一起建立联合索引。当有排序或者 group by 的需求时,也可以考虑建立索引来达到直接排序和汇总的需求。
参考:高性能Mysql——创建高性能的索引
视图是虚拟的表,与包含数据的表不一样,视图只包含使用时动态检索数据的查询;不包含任何列或数据。使用视图可以简化复杂的sql操作,隐藏具体的细节,保护数据;视图创建后,可以使用与表相同的方式利用它们。
视图不能被索引,也不能有关联的触发器或默认值,如果视图本身内有order by 则对视图再次order by将被覆盖。
创建视图:create view XXX as XXXXXXXXXXXXXX;
对于某些视图比如未使用联结子查询分组聚集函数Distinct Union等,是可以对其更新的,对视图的更新将对基表进行更新;但是视图主要用于简化检索,保护数据,并不用于更新,而且大部分视图都不可以更新。
按粒度分:
按操作分:
原子性(Atomic):一个事务中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚到事务开始前的状态,就像这个事务从来没有执行过一样
一致性(Consistency):在事务开始之前和事务结束以后, 数据库的完整性没有被破坏
隔离性(Isolation):数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)
持久性(Durability):事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失
参考:DDBS BASE
参考:DDBS CAP
SELECT CURRENT_DATE();
驱动程序主要帮助编程语言与 MySQL 服务端进行通信,如果连接、关闭、传输指令与数据等
对于聚簇索引,当执行插入操作时,id列会自动增长,页中行记录按id顺序存放,不需要随机读取其它页的数据。因此,在这样的情况下,插入操作效率很高。
对于非聚簇索引,可能叶子节点的插入不再有序,这时就需要离散访问非聚集索引页,插入性能变低。
Innodb是怎么解决这个性能变低的情况呢?也就是采用插入缓冲。
对于非聚集类索引的插入和更新操作,不是每一次都直接插入到索引页中,而是先插入到内存中。具体做法是:如果该索引页在缓冲池中,直接插入;否则,先将其放入插入缓冲区中,再以一定的频率和索引页合并,这时,就可以将同一个索引页中的多个插入合并到一个IO操作中,大大提高写性能。
插入缓冲的启用需要满足一下两个条件:
1)索引是非聚簇索引
2)索引不适合唯一的 。
如果辅助索引是唯一的,就不能使用该技术,原因很简单,因为如果这样做,整个索引数据被切分为2部分,无法保证唯一性。
想象这么一个场景,当数据库正在从内存向磁盘写一个数据页时,数据库宕机,从而导致这个页只写了部分数据,这就是部分写失效,它会导致数据丢失。这时是无法通过redo log恢复的,因为重做日志记录的是对页的物理修改,如果页本身已经损坏,重做日志也无能为力。
从上面分析我们知道,在部分写失效的情况下,我们在应用重做日志之前,需要原始页的一个副本,两次写就是为了解决这个问题。
其原理是这样的:
1)当刷新缓冲池脏页时,并不直接写到数据文件中,而是先拷贝至内存中的两次写缓冲区。
2)接着从两次写缓冲区分两次写入磁盘共享表空间中,每次写入1MB
3)待第2步完成后,再将两次写缓冲区写入数据文件
下面是它的原理图。
这样就可以解决上文提到的部分写失效的问题,因为在磁盘共享表空间中已有数据页副本拷贝,如果数据库在页写入数据文件的过程中宕机,在实例恢复时,可以从共享表空间中找到该页副本,将其拷贝覆盖原有的数据页,再应用重做日志即可。
哈希索引是一种非常快的等值查找方法(注意:必须是等值,哈希索引对非等值查找方法无能为力),它查找的时间复杂度为常量,InnoDB采用自适用哈希索引技术,它会实时监控表上索引的使用情况,如果认为建立哈希索引可以提高查询效率,则自动在内存中的“自适应哈希索引缓冲区”建立哈希索引。
之所以该技术称为“自适应”是因为完全由InnoDB自己决定,不需要DBA人为干预。它是通过缓冲池中的B+树构造而来,且不需要对整个表建立哈希索引,因此它的数据非常快。
参考:https://www.cnblogs.com/geaozhang/p/7397699.html
优点:
减少数据库服务器需要扫描的数据量
帮助数据库服务器避免排序和临时表
将随机 I/O 变顺序I/O
提高查询速度
唯一索引,能保证数据的唯一性
缺点:
索引的创建和维护耗时随着数据量的增加而增加
对表中数据进行增删改时,索引也要动态维护,降低了数据的维护速度
增大磁盘占用
用于复制,在主从复制中,从库利用主库上的binlog进行重播,实现主从同步。
用于数据库的基于时间点的还原。
区别:
mysql的行锁是通过索引加载的,即是行锁是加在索引响应的行上的。
要是对应的SQL语句没有走索引,则会全表扫描,此时取而代之的是表锁。
表锁:不会出现死锁,发生锁冲突几率高,并发低。(表锁总是一次性获得所需的全部锁,要么全部满足,要么全部等待。所以不会产生死锁。)
行锁:会出现死锁,发生锁冲突几率低,并发高。
行锁分 共享锁 和 排它锁。
共享锁又称:读锁。当一个事务对某几行上读锁时,允许其他事务对这几行进行读操作,但不允许其进行写操作,也不允许其他事务给这几行上排它锁,但允许上读锁。
select math from zje where math>60 lock in share mode;
排它锁又称:写锁。当一个事务对某几个上写锁时,不允许其他事务写,但允许读。更不允许其他事务给这几行上任何锁。包括写锁。
select math from zje where math >60 for update;
MVCC全称是: Multiversion concurrency control,多版本控制: 指的是一种提高并发的技术。
一般我们认为MVCC有下面几个特点(也就是乐观锁的一种实现):
而InnoDB实现MVCC的方式是:
MVCC只在 READ COMMITTED 和 REPEATABLE READ 两个隔离级别下工作。其他两个隔离级别够和MVCC不兼容, 因为 READ UNCOMMITTED 总是读取最新的数据行, 而不是符合当前事务版本的数据行。而 SERIALIZABLE 则会对所有读取的行都加锁。
参考:MVCC
参考:高性能Mysql——一条SQL语句在Mysql中是如何执行的?