整型:tinyint(1字节),smallint(2字节),mediumint(3字节),int(4字节)
浮点数:float(4字节),double(8字节),decimal(16字节)
float和double使用二进制存储数据,decimal使用字符串存储数字
decimal可以100%精确表达一个数字,可以用在金融服务行业
第一范式:列的原子性,每个列都不可以再拆分。
例如某些数据库系统中需要用到“地址”这个属性,本来直接将“地址”属性设计成一个数据库表的字段就行。但是如果系统经常会访问“地址”属性中的“城市”部分,那么就非要将“地址”这个属性重新拆分为省份、城市、详细地址等多个部分进行存储,这样在对地址中某一部分操作的时候将非常方便。
第二范式:在第一范式的基础上,非主键列完全依赖于主键,而不能是依赖于主键的一部分(即存在联合主键时,要完全依赖于联合主键)。
第三范式:在第二范式的基础上,非主键列只依赖于主键,不依赖于其他非主键。
在设计数据库结构的时候,要尽量遵守三范式,如果不遵守,必须有足够的理由。比如性能。事实上我们经常会为了性能而妥协数据库的设计。
char长度是固定的,处理速度快,比较费空间
varchar长度是不固定的,处理速度慢,比较节省空间
char(M)每个值都占用M个字节,如果某个长度小于M,MySQL就会在它的右边用空格字符补足。在varchar(M)每个值只占用刚好够用的字节再加上一个用来记录其长度的字节(即总长度为L+1字节)。
myisam 存储引擎 建议使用固定长度,数据列代替可变长度的数据列。
memory存储引擎 目前都使用固定数据行存储,因此无论使用char varchar列都没关系,
innodb 存储引擎 建意使用varchar 类型
主键可以唯一地标识一行
主键是可以被外键有效引用的对象
保持数据的一致性
主表的外键可以有多个,子表必须把外键当做主键。
例如现在有两张表,一张学生信息表,一张学生成绩表;把学生信息表中的学号字段设为外键,连接成绩表中的学号字段,则成绩表中的学号字段要设为主键。
就是一次select多张表的数据
union all
是一个不可分割的数据库操作序列
执行的结果是将数据库从一种一致性状态变到另一种一致性状态
使用场景:买票、转账
A(Atomicity)—原子性
事务里面的东西要么全部都做,要么全不不做
C(Consistency )—一致性
做完一个事务后,会将数据库从一个一致性状态转换到另一个一致性状态。
主要指数据库从一个正确的状态转换到另外一个正确的状态,操作过程是合法的,不会违反我们给数据库定义的一些规则。
例如银行的数据库,客户的账户余额不能是负值;int型字段不能赋予一个字符串等。
I( Isolation)—隔离性
各个事务事件之间相互隔离,不互相影响
D(Durability)—持久性
持久性是指事务一旦提交,它对数据库的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。
事务是通过重做日志、回滚日志和锁来实现的。
事务的ACD是通过redo log和undo log来实现的(重做日志和回滚日志)。隔离性是通过锁来实现的。
Undo Log
实现事务的原子性。
undo log就是帮助实现undo(回滚)操作的日志。
undo log记录了sql执行的相关信息,需要回滚的时候回执行sql语句相反的操作:对于每个insert,回滚时会执行delete;对于每个delete,回滚时会执行insert;对于每个update,回滚时会执行一个相反的update,把数据改回去。
Redo Log
实现事务的持久性。redo log用于crash recovery,保证MySQL宕机也不会影响持久性。
redo log存在的背景:
数据库的数据一般是存在磁盘的,但磁盘IO很慢,所以一般会在缓存池(buffer pool)里面进行读写,再定期将缓存池中的数据刷新到磁盘(这个过程称为刷脏)。读写的过程中如果mysql宕机就会导致数据丢失,失去"持久性"。
为了解决这个问题,修改数据时会先在redo log中记录下操作,再修改缓存池中的数据,事务提交的时候会持久化redo log的数据。如果mysql宕机,重启时可以读取redo log中的数据,对数据库进行恢复。
既然redo log也需要在事务提交时将日志写入磁盘,为什么它比直接将Buffer Pool中修改的数据写入磁盘(即刷脏)要快呢?主要有以下两方面的原因:
(1)刷脏是随机IO,因为每次修改的数据位置随机,但写redo log是追加操作,属于顺序IO。
(2)刷脏是以数据页(Page)为单位的,MySQL默认页大小是16KB,一个Page上一个小修改都要整页写入;而redo log中只包含真正需要写入的部分,无效IO大大减少。
脏读(读取了未提交的数据):一个事务已经更新一份数据,另外一个事务在读取同一份数据,如果第一个事务回滚了,那第二个事务读到的数据就是错误的。
不可重复读(前后读取的结果不一样):一个事务两次查询的结果不一样。
解决方式:使用行级锁,一个事务修改一条数据的时候锁定这行数据,修改完再解锁。
幻读(前后多次读取,数据总量不一致):一个事务读了几行数据,另外一个事务插入了几行数据,那第一个事务在后面的查询过程中会发现有几列数据是它之前没查到的。
解决方式:使用表级锁,添加或者删除完数据后再解锁。
幻读和不可重复读有什么区别:
幻读是读取了其他事务新增或删除的数据,针对insert和delete操作
不可重复读是读取了其他事务修改的数据,针对update操作
数据库事务的隔离级别有4个,由低到高依次为Read uncommitted 、Read committed 、Repeatable read 、Serializable。
Read uncommitted:
一个事务可以读到其他事务还没有提交的数据
会造成脏读
Read committed:
一个事务在执行过程中,读到其他事务已经提交的数据,造成前后的查询数据不一致的问题。
解决了脏读的问题,会造成不可重复读。
“不可重复"指的是"查询的结果不可重复”,事务希望相同的两次查询,查询结果应该是重复的,但是结果查下来是不可重复的。
Repeatable read:
这个等级下,事务可以实现重复读。即一个事务在执行的过程,查询到的数据内容是保持一致的。
这个等级解决了不可重复读的问题,但是会出现幻读的问题。
Serializable:
是最高的事务隔离级别,在该级别下,事务之间完全不干扰,但是代价很高,性能很低。
这个等级解决了脏读、不可重复读、幻读的问题。
隔离级别 | 含义 | 会造成的问题 | 默认隔离级别数据库 |
---|---|---|---|
Read uncommitted | 一个事务可以读其他事务还没有提交的数据 | 脏读 | |
Read committed | 一个事务可以读其他事务提交后的数据 | 会造成不可重复读 | Oracle,SQL Server |
Repeatable read | 事务可以实现可重复读 | 会造成幻读 | MySQL |
Serializable | 事务之间完全不会相互影响 |
行级锁
共享锁
索引:目录,文件查找的结构
优点:
缺点:
where添加条件时:
上图中,根据id查询记录,因为id字段仅建立了主键索引,因此此SQL执行可选的索引只有主键索引,如果有多个,最终会选一个较优的作为检索的依据
-- 增加一个没有建立索引的字段
alter table innodb1 add sex char(1);
-- 按sex检索时可选的索引为null
EXPLAIN SELECT * from innodb1 where sex='男';
order by
对某个字段进行排序的时候,如果该字段没有索引的话,会对数据进行外部排序(将数据从硬盘读取到内存使用内部排序,最后合并排序结果)。这个过程会比较耗时,首要要从存储设备里面读取数据,还要对数据进行排序。
join
对join
语句匹配关系(on
)涉及的字段建立索引能够提高效率
可以分为主键索引,唯一索引,普通索引,全文索引
主键索引:
主键约束,唯一标识数据库中的每条记录
必须包含唯一的值
不能包含NULL值
每个表都必须有且仅有一个主键
唯一索引:
唯一索引的目的不是为了提高访问速度,只是为了避免数据出现重复
主键索引不允许有null值,唯一索引可以有;
一个表只能有一个主键,可以有多个唯一索引;
有哈希表,有序数组,N叉树;
InnoDB里面是B+树。
为什么使用B+树:
B和B+树的对比:
B树的节点里面同时存键和值,B+树只有叶子节点存放数据(比如如果是非主键索引里面的节点存放的数据就是主键的值)。
对于B树来说,把频繁访问的数据放在靠近根节点的地方将会大大提高热点数据的查询效率。这种特性使得B树在特定数据重复多次查询的场景中更加高效。
对于B+树来说,叶子节点只保存索引,所以一次读取可以从页数据里面获得更多的键,可以更快地缩小查找范围;同时因为数据只在叶子节点,所以查询性能会比较稳定;而且所有的叶子节点都通过链表连接,所以范围查询和排序的时候有很大的优势。
Hash表和B+树的对比:
Hash索引等值查询的时候速度比较快
B+树所有的数值内容都保存在叶子节点,查询性能比较稳定;叶子节点通过指针链接在一起,范围查询、排序的时候很方便;
因为Hash函数的不可预测性,不支持多列索引的最左前缀匹配
Hash肯定需要回表,B+树在主键索引,覆盖索引的时候可以不用回表
要查的列就是索引里面的列。在覆盖索引的条件下,就不用查主键索引,直接从非主键索引中就能查到记录。避免回表耗费时间。
例如创建了索引(field1, field2, field3),然后select field3 from table_name where field1=2, field2=3
这个查询就不会行使回表操作,直接返回结果。
如果一张表里面有很多数据,我需要删除的量在百万以上,应该怎么删除这些数据?
应该先删除索引,再删除需要删除的数据,再把索引加回来。
因为索引文件是单独存在的,增删改的操作都会产生对索引文件的操作,这些操作可能会对磁盘有额外的IO,降低增删改的效率。删除数据的速率和创建的索引数量是成正比的。
所以如果需要删除一张表里面大量的数据的时候,应该先删除里面的索引,再删除数据,最后再把索引加回来。
聚簇索引就是数据行记录和索引放到一起,InnoDB里面只有主键索引是聚簇索引。使用聚簇索引来查数据的时候,可以不用回表。
一张表只能有一个聚簇索引,因为一张表的数据只有一种存储方式。
非聚簇索引就是索引的结果是主键的值,需要做回表的操作。
非聚簇索引也叫二级索引、辅助索引
不一定,可以有覆盖索引
使用字段的前几个字符建立索引。
用于密码匹配、文字匹配等场景。
(建立索引的时候,默认是使用字段的全部内容)
前缀索引的创建难度在于到底选择多长的前缀以及字段的选取方式。
可以利用select count(*)/count(distinct left(password,prefixLen));
,通过从调整prefixLen
的值(从1自增)查看不同前缀长度的一个平均匹配度,接近1时就可以了(表示一个密码的前prefixLen
个字符几乎能确定唯一一条记录)
最左前缀匹配原则用于多列索引的情况,如果索引条件没有覆盖索引的最左前缀,那就不会用到这个索引。
例如创建了(c1, c2, c3)的索引,那(c2, c3, c2+c3)的查询条件是用不了索引的。
mysql会在条件里面一直向右匹配,匹配到范围查询为止。如果将精确查询放到范围查询的后面,该精确查询的索引就用不到了。
使用多个字段创建的索引是联合索引,联合索引中,如果想要命中索引要按照建立索引时的字段顺序挨个使用,否则无法命中索引。
因为索引会按照创建索引时的字段顺序来对数据进行排序。
一般情况下,将查询需求比较强的字段放在前面。
不一定,如果查询的数据范围很大,例如最终的查询结果可能占了整体数据的80,那还是不加索引比较好,不然加载索引再回表的时间也很长
B+树,假设数据量为n,一个节点假设它有m个子节点,树高就是log(m)n,每一层平均遍历m/2个节点,时间复杂度是(m/2)*log(m)n
这篇文档
关系型数据库:
非关系型数据库:not only sql,数据之间的关联比较少,更容易分散存储
因为mongodb的文档存储结构是json,mongodb是聚合型的数据库,一般就想要通过key得到value,而B树的结构就是每个节点都有Data域,比较符合mongodb的数据库特性。
wiredtiger:
支持document级别的锁
因此多个客户端可以同时更新一个colleciton中的不同document
支持MVCC
mmap:
支持到collection级别的锁
mongodb中有单字段索引、复合索引、多key索引、文本索引、唯一索引、TTL索引、稀疏索引。
普通索引和稀疏索引的区别是,普通索引对于不在该字段的documents会假定该documents的索引字段为null
而稀疏索引则会直接跳过该document,效率更高。
mongodb中没有聚簇索引的概念,数据和索引是单独分离的。
在Nosql中没有事务这个概念,每一个数据集都是原子级别的。
存储的优势:
有很多内嵌文档的形式,比如一个区域下有很多项目,项目下有不同的实例类型,不同实例类型有不同维度的数据,希望以比较直观的方式看到数据,而不是join很多表。mongodb 的文档都是json结构,比较符合需求,可以直接提取想要的数据
字段自由,方便后期扩展新的字段
操作优势:
核心需求是读表,不需要写或者修改,对数据的处理不需要有很高的事务性,一般存进去的数据就是比较直接的需求数据,不用再做更深的操作;
拓展优势:
后期数据量巨大,且数据之间没有强关联性,方便拓展,方便横向拓展
1000多万行的数据,上万GB
加了索引