数据库是一种有效地管理大量的、安全的、并发的、关联的、一致的数据工具;而文件保存数据会存在安全问题,不利于查询和对数据的管理,也不利于存放海量数据;因此,引入了数据库的概念,数据库更便于我们去操纵和管理数据,做一些我们想要的改变。
简单点说,SQL是一种用于操作数据库的查询语言,而MySQL是一种数据库软件。MySQL是一个DBMS(DataBaseManagementSystem)数据库管理系统,是一个用来管理数据库文件的软件;而SQL就是用于管理DBMS数据的,即访问,更新和操作数据库中的数据。
超键:只要在关系中含有能唯一标识元组属性的集合就叫做超键
候选键:不含有多余属性,只含有能唯一标识元组的属性就叫做候选键
主键:用户从众多候选键中选出来的一个键就是主键
外键:相对于两个表或多张表而言。如果表R中属性K是其他表的主键,那么K在表R中称为外键。
1.第一范式(1NF):数据库表中的字段具有原子性,不可再分。所有关系型数据库系统都满足第一范式。
2.第二范式(2NF):在第一范式的基础上,要求实体的属性完全依赖于主关键字。即不能存在仅依赖主关键字一部分的属性。
3.第三范式:在第二范式的基础上,要求非主键列必须直接依赖于主键,不能存在传递依赖。比如一个表中有<学号,姓名,课程号,成绩>,其中成绩是由学号+课程号决定的,因此将表分为两个表<学号,姓名,课程号>,<学号,课程号,成绩>。
MySQL服务器通过权限表来控制用户对数据库的访问,这些权限表有user,db,table_priv,columns_priv和host。
权限表 | 内容 |
---|---|
user | 记录允许连接到服务器的用户帐号信息,里面的权限是全局级的 |
db | 记录各个帐号在各个数据库上的操作权限 |
table_priv | 记录数据表级的操作权限 |
columns_priv | 记录数据列级的操作权限 |
host | 配合db权限表对给定主机上数据库级操作权限作更细致的控制。此权限表不受GRANT和REVOKE语句的影响 |
1.Statement:只记录每条修改数据的SQL执行语句;但还必须记录每条语句在执行的时候的一些相关信息,确保所有语句能在slave得到和在master端执行时候相同的结果。但一些函数功能不能被复制。
2.row:不记录SQL的上下语句信息,只记录那一条记录被修改成什么了;解决了Statement的问题,但单语句更新(删除)表的行数过多,会导致形成大量binlog。
3.Mixedlevel:前两个的混合使用,一般的语句修改用Statement,一些函数复制等用row,根据执行的语句来选择使用哪种方式。
整数:smallint(2字节,0 ~ 2^16 -1)、int(4字节,0 ~ 2^32-1);
浮点数:float(4字节)、double(8字节);
定点数:decimal(m,d)(M+2字节);
日期类型:date(YYYY-MM-DD)、time(HH:MM:SS)、year(YYYY)、datetime(YYYY-MM-DD HH:MM:SS)、timestamp(YYYYMMDD HHMMSS)
字符型:char(0 ~ 255)、varchar(0 ~ 255)、text(0 ~ 65535(2^16-1))、longtext(0 ~ 2^32-1)
1.InnoDN(5.5版本之后)支持事务处理、外键与行级锁,而MyISAM(5.1版本之前)不支持;
2.InnoDB存储引擎提供了具有提交、回滚和崩溃恢复能力的事务安全存储引擎,该引擎是为了处理大数据容量的数据库系统;
3.MyISAM的读性能比InnoDB好,但修改性能比InnoDB差;
4.MyISAM可被压缩,存储空间较小,InnoDB的表需要更多的内存和存储,它会在主内存中建立其专用的缓冲池用于高速缓冲数据和索引;
5.MyISAM支持全文类型索引,而InnoDB不支持;
1.插入缓冲(insert buffer)
2.二次写(double write)
3.自适应哈希索引(ahi)
4.预读(read ahead)
索引(文件)是一种帮助mysql高效的获取数据的数据结构,简单地说,索引就相当于目录。为了方便查找书中的内容,通过对内容建立索引形成目录。索引的实现通常使用B树及其变种B+树。索引类似于空间换时间,因此获取数据时会很快。
1.InnoDB索引是“聚集索引”,“聚集”是指数据行和相邻的键值紧密地存储在一起;
2.MyISAM索引的叶子节点只保存数据记录的地址,通过读取地址来读取页,进而读取被索引的行;
3.InnoDB的主键索引非常高效,因为主键索引的叶子节点存储着行数据;
1.若数据库平台读多写少,可以选择MyISAM引擎,如博客系统、学习网站等;
2.若数据库平台更新操作(增删改)较多,则选择InnoDB;
1.当表记录较少、经常增删改的表或数据重复且分布平均的表字段不需要使用索引,用了反而占存储空间;
需要建立索引
1.主键自动建立唯一索引;
2.频繁作为查询的条件的字段应该创建索引;
3.查询中与其他表关联的字段,使用外键关系建立索引;
4.查询中排序的字段,通过索引可以大大提高排序的速度;
5.查询中统计或者分组字段;
1.组合索引:实质上是将多个字段建到一个索引里,列值的组合必须唯一
2.聚集索引:定义:数据行的物理顺序与列值(一般是主键的那一列)的逻辑顺序相同,一个表中只能拥有一个聚集索引。
3.非聚集索引:唯一索引、普通索引、主键索引、全文索引
4.UNIQUE(唯一索引):不可以出现相同的值,可以有NULL值
5.INDEX(普通索引):允许出现相同的索引内容
6.PROMARY KEY(主键索引):不允许出现相同的值
7.fulltext index(全文索引):可以针对值中的某个单词,但效率不是很好
1.在经常用作过滤器的字段上建立索引;
2.在SQL语句中经常进行GROUP BY、ORDER BY的字段上建立索引;
3.在不同值较少的字段上不必要建立索引,如性别字段,性别字段无非男女两种值,区分度不好,建立索引效果不好,要选择区分度高的字段;
4.对于经常存取的列避免建立索引;
5.用于联接的列(主健/外健)上建立索引;
1.B+树更有利于对数据库的扫描 。B 树在提高了磁盘 IO 性能的同时并没有解决元素遍历的效率低下的问题,而 B+ 树只需要遍历叶子节点就可以解决对全部关键字信息的扫描,所以对于数据库中频繁使用的 range query,B+树有着更高的性能。
2.B+树的磁盘读写代价更低 。B+树的内部结点并没有指向关键字具体信息的指针。因此其内部结点相对 B 树更小。如果把所有同一内部结点的关键字存放在同一盘块中,那么盘块所能容纳的关键字 数量也越多。一次性读入内存中的需要查找的关键字也就越多。相对来说 I/O 读写次数也就降低了。
1.所有叶子节点中的关键字按大小顺序排序;
2.相邻的叶子节点按顺序链接(相当于构成一个顺序链表);
3.B+树的内部节点只存放键,不存放值,叶子节点同时存放键和值;
1.B树的内部节点和叶子节点都存放键和值;
2.B树的叶子节点各自独立
1.内部节点中,关键字的个数与其子树的个数相同,不像 B 树种,子树的个数总比关键字个数多 1 个 ;
2.所有指向文件的关键字及其指针都在叶子节点中,不像 B 树,有的指向文件的关 键字是在内部节点中。换句话说,B+树中,内部节点仅仅起到索引的作用;
3.B+在搜索过程中,如果查询和内部节点的关键字一致,那么搜索过程不停止,而是继续向下搜索这个分支,B+为了找到这个关键字的指针;
利用最左前缀:mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,那么后面的d是用不到索引的,如果建立(a,b,d,c)的索引就可以用到。
事务是一个不可分割的数据库操作序列,也是数据库并发控制的基本单位,其执行的结果必须使数据库从一种一致性状态变到另一种一致性状态。事务是逻辑上的一组操作,要么都执行,要么都不执行。
有一个经典的栗子就是转账,比如小明要给小方转100块钱,如果成功了,那么小明的账户余额信息和小方的账户余额信息都需要更新;但是如果转账过程中突然银行系统崩溃,导致小明的余额少了,小方的余额多了,这就不对了。事务就是保证这两个关键操作要么都成功,要么都要失败。
1.原子性(Atomic): 事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用;
2.一致性(Consistent): 执行事务前后,数据保持一致,多个事务对同一个数据读取的结果是相同的;
3.隔离性(Isolated): 并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的;
4.持久性(Durable): 一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。
1.脏读(Drity Read):当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。
2.幻读:一指当事务不是独立执行时发生的一种现象。例如一个事务对一个表插入了一行新的数据,同时,第二个事务也对该表进行了更新,后来第一个事物发现数据不一致,就会产生幻觉。例如,一个编辑人员更改作者提交的文档,但当生产部门将其更改内容合并到该文档的主复本时,发现作者已将未编辑的新材料添加到该文档中。如果在编辑人员和生产部门完成对原始文档的处理之前,任何人都不能将新材料添加到文档中,则可以避免该问题。
3.不可重复读:一个事务还没有结束时,另外一个事务也访问该同一数据。例如,一个编辑人员两次读取同一文档,但在两次读取之间,作者重写了该文档。当编辑人员第二次读取文档时,文档已更改。原始读取不可重复。如果只有在作者全部完成编写后编辑人员才可以读取文档,则可以避免该问题。
READ Uncommitted未提交读:事务中的修改,即使未提交,对其他事务也都是可见的;
Read Committed提交读:一个事务从开始直到提交之前,所做的任何修改对其他事务都是不可见的;大多数数据库默认采用此级别;
Repeatable Read可重复读:解决了脏读问题;该级别保证了在同一事务中多次读取同样记录的结果是一致的;MySQL默认为此级别;
Serializable可串行化:最高的隔离级别。该级别会在读取的每一行数据上都加锁,导致大量的超时和锁争用的问题。但用的比较少,只有在非常需要确保数据的一致性而且可以接受没有并发的情况下,才考虑此级别。
为了防止数据库中的数据被多个用户共享访问,而影响到数据的一致性和有效性,若双方已锁定一部分资源但也都需要对方已锁定的资源时,无法在有限的时间内完全获得所需的资源,就会处于无限的等待状态,从而造成其对资源需求的死锁。因此引入了锁机制,用于管理对共享资源的并发访问。
有两种锁,一种是Latch闩(shuan)锁(轻量级的锁),它要求锁定的时间非常短,若持续时间长,则其应用性能会很差。在InnoDB引擎中,Latch分为rwlock(读写锁)和mutex(互斥锁),其目的是用来保证并发线程操作临界资源的正确性,并且通常没有死锁检测的机制。
还有一种是Lock,它的对象是事务,用来锁定数据库中的对象,如表、页、行。并且lock的对象commit或rollback后进行释放(不同事物隔离级别释放的时间可能不同)。
在MySQL中的引擎中主要是Lock。
Lock锁根据粒度主要分为表锁、行锁、页锁,不同的存储引擎拥有的锁粒度都不同。
1.表锁:表级锁就是一次会将整个表进行锁定,是个存储引擎中最大颗粒度的锁机制;它的特点是实现逻辑简单,资源消耗较少,获取锁和释放锁的速度很快,很好的解决了死锁问题。使用表锁的主要是MyISAM、MEMORY,CSV等一些非事务性存储引擎。
2.行锁:MySQL中锁定粒度最小的锁机制,特点是发生锁定资源争用的概率小,能给予应用程序尽可能大的并发处理能力而提高一些需要高并发应用系统的整体性能,但资源消耗较大。使用行锁的主要是InnoDB引擎。
3.页锁:它的锁定颗粒度介于表锁和行锁之间,它的并发能力与资源开销也是介于两者之间,另外,它与行锁一样,会发生死锁。使用页锁的主要是BerkeleyDB存储引擎。
从类别上锁分为:共享锁(S Lock)和排它锁(X Lock)。
共享锁: 又叫做读锁。 当用户要进行数据的读取时,对数据加上共享锁。共享锁可以同时加上多个。
排他锁: 又叫做写锁。 当用户要进行数据的写入时,对数据加上排他锁。排他锁只可以加一个,他和其他的排他锁,共享锁都相斥。
当数据对象被加上排它锁时,其他的事务不能对它读取和修改。加了共享锁的数据对象可以被其他事务读取,但不能修改。数据库利用这两种基本的锁类型来对数据库的事务进行并发控制。
锁的粒度具体取决于存储引擎,InnoDB实现了行锁,页锁,表锁;他们的资源开销从大到小,并发性能也从大到小。
InnoDB是通过索引来实现行锁的。
例如:select * from tab_with_index where id = 1 for update;
for update 可以根据条件来完成行锁锁定,并且 id 是有索引键的列,如果 id 不是索引键那么InnoDB将完成表锁,并发将无从谈起。
1.Record lock:单个行上记录的范围。它会锁住索引记录,如果InnoDB存储引擎建立的时候没有设置任何一个索引,这时InnoDB存储引擎会使用隐式的主键来进行锁定;
2.Gap lock:间隙锁,锁定一个范围,但不包含记录本身;
3.Next-Key lock:Record+Gap,锁定范围+记录本身;InnoDB对于行的查询都是采用这种锁定算法。
1.对于事物之间由于资源访问顺序导致的死锁,则可以约定以相同的顺序访问表;
2.对于并发修改同一记录导致的死锁,可以使用乐观锁或悲观锁进行控制;
3.对于非常容易产生死锁的业务部分,可以尝试使用升级锁定颗粒度,通过表级锁定来减少死锁产生的概率;
4.避免多个事务同时执行表级锁,对于有全表扫描的SQL语句,建立相应的索引进行优化。
1.乐观锁(Optimistic Lock):从名字上看,就是很乐观,每次去拿数据的时候认为别人不会改,所以不会上锁,但是在提交更新的时候会判断一下在此期间别人有没有去更新数据,乐观也严谨。乐观锁适用于读多写少的场景,这样可以提高吞吐量。乐观锁总是假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。
乐观锁通过使用数据版本(Version)记录机制(最常用)和时间戳(timestamp)来实现。
前者是通过为数据库表增加一个数字类型“version”字段来实现。当读取数据时,将version字段的值一同读出,数据没更新一次,version值加1.当我们提交更新的时候,判断数据库表对应记录的当前版本信息与第一次取出来的version值进行比对,如果数据库表当前版本号与第一次取出来的version值相等,则予以更新,否则认为是过期数据。
时间戳的方式与前者实现方式差不多,同样是在需要乐观锁控制的table中增加一个字段,名称无所谓,字段类型使用时间戳(timestamp), 和上面的version类似,也是在更新提交的时候检查当前数据库中数据的时间戳和自己更新前取到的时间戳进行对比,如果一致则OK,否则就是版本冲突。
2.悲观锁(Pessimistic Lock):就是比较悲观,每次去拿数据的时候都认为别人会修改,所以每次拿数据的时候都会对数据上锁,这样别人想拿这个数据的时候就会先block,然后获得锁之后再对数据进行操作。总的来说就是悲观锁总是假定会发生并发冲突,然后屏蔽一切可能违反数据完整性的操作。
悲观锁大多数情况下依靠数据库的锁机制实现,例如使用Select … for update语句,以保证操作最大程度的独占性。但如果采用悲观锁,则整个操作过程中,数据库始终处于加锁状态(从操作员读出数据、开始修改直至提交修改结果的全过程,甚至还包括操作员中途去煮咖啡的时间),而面对成百上千万的并发,就会导致灾难性的后果,因此采用悲观锁进行控制时要考虑清楚。
视图(View)是从一个或多个基本表(或视图)导出的表,它与基本表不同,是一个虚表,数据库中只存放视图的定义,而不存放视图对应的数据;当基本表中的数据发生变化,从视图中查询出的数据也就随之改变;单表视图一般用于查询和修改,会改变基本表的数据;多表视图一般用于查询,不会改变基本表的数据。
1.视图能够简化用户的操作;
2.视图使用户能以多种角度看待同一数据;
3.视图对重构数据库提供了一定程度的逻辑独立性;
4.视图能够对机密数据提供安全保护;
当需要多次重复输入相同的语句、需要不同的表字段聚合,进行信息重组、安全需要或需要兼容老的表等情况,就可以利用视图来简化sql查询,提高开发效率。
不过事物皆有两面性,视图也存在它的缺点。视图的性能差,数据库必须把视图查询转化成对基本表的查询,如果这个视图是由一个复杂的多表查询所定义,那么,即使是视图的一个简单查询,数据库也要把它变成一个复杂的结合体,需要花费一定的时间;视图也存在着修改限制,当用户试图修改视图的某些信息时,数据库必须把它转化为对基本表的某些信息的修改,对于简单的视图来说,这是很方便的,但是,对于比较复杂的试图,可能是不可修改的。
存储过程是用户定义的一系列SQL语句的集合,涉及特定表或其他对象(表、视图、索引、序列、目录、同义词、数据库用户、存储过程、函数、触发器等)的任务,用户可以调用存储过程;而函数通常是数据库已定义的方法,它接收参数并返回某种类型的值并且不涉及特定用户表。
1.存储过程功能复杂强大,可以执行包括修改表等一系列数据库操作;而函数实现的功能针对性比较强;
2.对于存储过程来说可以返回参数,如记录集,而函数只能返回值或者表对象。函数只能返回一个变量;而存储过程可以返回多个。存储过程的参数可以有IN,OUT,INOUT三种类型,而函数只能有IN类;
3.存储过程可以使用非确定函数,而不允许用户在定义函数非确定函数;
4.存储过程一般作为一个独立的部分来执行(EXECUTE语句执行),而函数可以作为查询语句的一部分来调用(SELECT调用);
例如在数据库中有两个表,一个用户信息表,一个账号表,账号表用来账号的注册,密码的修改等操作,信息表用来保存用户 的信息(姓名、年龄等);当账号在账号表中注册成功后,如何在用户表中同时添加用户的相关信息以保证数据的准确性和实时性呢。通常我们在注册成功时可以insert用户的信息到用户信息表中,但如果用户数量过多,SQL语句则过于繁琐,怎么办。这时我们就可以使用触发器。
触发器(trigger)是一种特殊的存储过程,它可以在对一个表上进行INSERT、UPDATE和DELETE操作中的任一种或几种操作时被自动调用执行。它SQL server 提供给程序员和数据分析员来保证数据完整性的一种方法。
当需要实现多个表的级联更改或实时监控表中字段的更改作出处理时就可以使用触发器来完成。
Before Insert、After Insert、Before Update、After Update、Before Delete、After Delete
随着系统规模的不断增加,数据量和并发量不断增大,整个系统架构中最先受到冲击而形成瓶颈的,定然是数据库,数据库中数据存储的位置(磁盘),数据量的大以及系统的吞吐量都影响了数据的访问速度和读写速度,因此数据库层面的优化也显得尤为重要。
1.当只有一行数据时使用limit 1。查询时如果已知只会得到一条数据,则加上limit 1会增加性能。因为mysql数据库引擎会在找到一条结果后停止搜索,而不是继续查询下一条或直至所有记录查询完毕。
2.选择正确的数据库引擎。MySQL中都MyISAM和InnoDB都各有利弊,因此选择正确的引擎很重要。MyISAM适用于读多写少的应用,而且它对于select count(*)类似操作速度非常快;InnoDB是一个复杂的存储引擎,它在写多的应用中占优势,并且它支持很多高级应用(事务等)。
3.用not exists代替not in。Not exists用到了连接能够发挥已经建立好的索引的作用,not in不能使用索引。Not in是最慢的方式要同每条记录比较,在数据量比较大的操作中不建议使用这种方式。
1.将字段很多的表分解成多个表。因为字段很多的表中存在一些使用频率低的字段,这些字段的存在就会大大影响访问速度;
2.增加一些中间表。对于需要经常联合多个表进行查询时,可以建立一个中间表,将需要通过联合查询的数据插入到中间表,来提高效率;
3.读写分离。在数据库并发大的情况下,最好的做法就是进行横向扩展,增加机器,以提升抗并发能力,而且还兼有数据备份功能。