本文看下MySQL的基础内容以及常见的优化方式。
以二维的数据格式来存储数据的数据库叫做关系型数据库,其中关系包括一对一,一对多,多对多,都通过二位数据中的行与行关联来表达关系。
e的意思是entity,r的意思是relation,E-R图,即实体关系图,表达的是关系型数据库中的表之间的关系,一对一,一对多,多对多,如下图是一个电商系统中可能的关系图:
先来看一些重要的概念:
重要
的概念其中(学号)
,(身份证号)
,二者中的任何一个都能唯一的标识一行,因此二者中任何一个我们都叫做主键,但注意主键只能有一个。
候选集为[(学号),(身份证号)]
,我们可以添加性别到学号,添加姓名到身份证号,变为如下:[(学号,性别),(身份证号,姓名)]
。即超键肯定能作为主键(但不一定符合某些范式)
。
通过(学号)
可以推导出姓名,就可以说姓名函数依赖于学号。
如果使用(学号,姓名)
作为主键,通过学号可以推导出姓名,就可以说姓名函数依赖于(学号,姓名)
,问题的本质是把超键当做主键来用了。
比如我们使用(学号)
作为主键,则存在学号->班级编号->院系
,这里学号对院系就是传递依赖。
范式类似于设计模式 ,都是一种经验,可以直到我们更好的设计数据库,范式的英文是Normal Form,简称NF,范式有如下的6种:
1:1NF
第一范式
2:2NF
第二范式
3:3NF
第三范式
4:BCNF
BC范式,因为介于3NF和4NF之间,所以有时也叫做3.5NF
5:4NF
第四范式
6:5NF
第五范式
数据库第一范式,定义为,对于关系R,当且仅当关系R的每个属性的值域只包含原子项,接下来用人话翻译一遍,就是,对于一张表R,表R的每个列的值都不可再分,如下表:
其中name_age列就是可以在分为name和age的,因此并不是原子项,不符合第一范式,如果修改为如下,则符合第一范式:
在满足第一范式的基础上,不存在部分函数依赖,即消除非主属性对主属性的部分函数依赖,如下表:
主键为(学生编号,课程编号)
,但是存在通过学生编号就可以推导出学生姓名的情况,即存在部分函数依赖,所以不满足第二范式,因此将该表拆分为如下的两个表就可以满足第二范式了:
主键是(学生编号)
,虽然其能够决定院系,但并非直接决定,而是通过学生编号->班级编号->院系
,即是通过传递函数依赖确定的,因此此时是不符合第三范式的,可修改为如下表结构:
只有存在联合主键的时候才可能存在不满足BCNF的情况。
在满足第三范式的基础上,消除主属性对于码的传递函数依赖和部分函数依赖。来看个例子,假定某物流公司有很多仓库,每个仓库都有一个仓库管理员,每个仓库都可以存放多种物品,每种物品都有数量,设计出来后如下表:
此时码(即联合主键)
是(仓库名,管理员,物品名)
,存在如下的问题:
此时会出现的问题是,如果我们有新的仓库,比如火星仓,则必须在配备了管理员,准备了物品后,才能创建该仓库,这明显是不能满足业务的使用的,删除也是同样的道理,现在某个管理员离职,更换其他管理员,更新的成本也很高,可以修改为如下的表,使其符合BCNF:
在满足3NF的基础上,消除表内的多对多关系,如下表:
课程->学生和课程->先修课,都是1:n的关系,因此表的数据冗余度将会非常高,可修改为如下:
TODO
全称是structure query language,即结构化查询语言,是一种用来操作关系型数据库的语言,在1970年由boyce和chanberlin提出(也就是BCNF中的BC)
,sql根据执行的操作不同可以分为如下的几种:
DQL:
data query language,数据查询语言,或“数据检索语句”,即select ...。用来查询数据。
DML:
data monipulation[məˌnɪpjʊˈleɪʃ(ə)n] language,数据操纵语言,如insert,update,delete。用来增删改查数据。
DDL:
data definition language,数据定义语言,如create table,drop table,alter table等。用来创建表和修改表结构。
TCL:
transaction control language,事务控制语言,如start transaction,begin,commit,savepoint,rollback等。用来通过事务控制数据的增删改成功。
DCL:
data control language,数据控制语言,如grant,revoke等。用来控制用户对库表,以及各种增删改查等操作的权限。
前两种使用的最多的,因为我们的程序就是在查询和修改数据,第三种也会用到,但没那么多了。
在1986年有了第一个版本,SQL-86,最后一个版本是在2011年,SQL:2011,具体如下图:
其中目前使用最多的还是1992年的SQL2和1999年的SQL3。
monty是MySQL的开发,第一行代码就由其编写(据说第一行代码,其实是一行注释,可见注释还是重要啊!!!)
,之后monty和朋友合伙创建了MySQL AB公司,接着在2008年sun公司以10亿美元收购了MySQL AB公司,但monty并未随之进入sun公司,而是单起炉灶成立了另外一家公司,之后在2009年Oracle以74亿美元收购了sun公司(有没有一种大鱼吃小鱼,小鱼吃虾米的感觉)
。MySQL创世人monty担心Oracle公司将MySQL商业化,闭源,就有基于MySQL拉了另一个分支,开发了mariaDB,如下图:
MySQL的my是monty和首任妻子所生大闺女的名字,mariaDB是monty和二婚妻子所生二闺女的名字,看来不管是中国人还是老外,还真都是女儿奴啊!!!
接着我们来看下MySQL本身的发展历史。
4.0
在该版本中通过innodb支持了事务
5.0(2003年)
5.6
5.7
8.0
8.0版本相对于5.7版本发生了非常大的变化,所以直接叫做8.0版本了,按照官方的计划,是大力发展该版本,并使该版本成为时长的主流,但事与愿违,因为8.0版本的变化是在是太大,迁移的成本太高,所以用户量一直也没有太大的升高,反而,用户不断的将版本升级为5.7版本,因此当前5.7版本是最主流的MySQL版本,如果有一个新业务,也可以考虑使用该版本,那么每个版本都有哪些更新呢,也来看下:
5.7:
增加json处理功能
mgr(mysql group replication) 一种基于paxos协议的分布式解决方案
分区表
修复XA问题
8.0
json增强,优化json处理的性能
自增列持久化
utf8mb4为默认编码
8以前的版本,默认编码是utf-8,但其utf-8是假u8很多,因为很多编码都不支持,而真正的u8编码是utf8mb4,所以8默认的编码是真正的u8编码了。
尽量不让达到最大连接。
最大连接数,最大值16384,可适当调大该值,最优值为达到85%的使用量,即max_used_connection/max_connnects约等于85%最好。
当连接数达到max_connections时,用来等待可用连接时存放的临时区域,一般设置为3000左右。
(jdbc)
和交互式(mysql cli)
连接超时时长。超过会断开连接,默认是8个小时,一般不需要修改。因为innodb比较重要,目前用的最多,所以单独看下其相关的变量。
redo log buffer
->page cache
->redo log磁盘文件
,如下图:该参数可能的取值有如下三个:
0:每次只写到redo log buffer中,此时如果数据库异常重启将会导致数据丢失,因为后台线程刷盘每一秒钟执行一次,所以最多会有一秒钟的数据丢失,不建议设置该值。
1:每次都会将数据写到redo log磁盘文件中,这种方式可以保证数据不丢失,但是每次事务提交都要发生磁盘IO,所以性能不会很好,如果你的系统压力不是特别大,建议设置该值,否则不建议设置。
2:每次都会将数据写到page cache中,此时数据已经写到操作系统的缓存中,所以MySQL服务器本身的异常重启,不会丢失数据,但是如果是机器掉电,还是会丢失数据,但掉电概率相对较低,如果是对性能要求较高,可以考虑该值
一般情况下,数据安全才是第一位的,所以该值一般设置为1,如下是我司线上环境rds配置:
当然如果经过业务的分析,允许少量数据丢失的话,并且对性能要求比较高可以考虑将该值设置为2,但是不建议设置为0,因为丢失数据的风险太高了,且0在性能表现上并不比2好太多。
时刻1:事务A开始并执行一个查询,开始消耗CPU资源,innodb_thread_concurrent变为1
时刻2:在事务A的查询结束前事务B开始,并执行一个查询,开始消耗CPU资源,innodb_thread_concurrent+1变为2
时刻3:事务A查询依然没有结束,事务B被事务A的间隙锁阻塞等待,因为此时事务B不在消耗CPU资源,所以innodb_thread_concurrent-1,变为1
时刻4:事务A结束,innodb_thread_concurrent-1变为0
时刻5:事务B结束锁等待,开始执行查询,开始消耗CPU资源,innodb_thread_concurrent+1变为1
时刻6:事务B结束,innodb_thread_concurrent-1变为0
最后再看一个设置innodb_thread_concurrent为3,阻塞主第四个事务查询的例子,如下图:
实践上,不建议将该值设置为0,此时不限制线程数,可能会导致过多的线程,而导致严重上下文切换,引起性能问题,一般建议设置在64~128之间,具体的值可以根据具体压测的情况来确定。
注意的就是:如果一个线程某个时刻不占用CPU资源,则不占用innodb_thread_concurrent的计数。
(MySQL本身的限制)
,如下是我司设置的为8M:innodb_log_files_in_group*inndb_log_file_size
的值就是redo log文件的总大小。一般建议设置1到4个,正常使用默认值即可。设置为2的好处是可以分离插入数据的操作和更新删除数据的操作,提高并发性能。如下是极客时间AI给出的答案:你设计的数据不仅仅存储在你的业务数据库,还有有多个slave,数据还可能同步到大数据部门进行,所以这个数据影响的范围会进一步增大。
让人敬佩的白发程序员——MySQL/MariaDB之父Monty阿里交流会 。
范式通俗理解:1NF、2NF、3NF和BNCF 。
数据库范式详细介绍(1NF,2NF,3NF,BCNF,4NF) 。
MySQL历史,名称由来及版本 。
23 | MySQL是怎么保证数据不丢的? 。
29 | 如何判断一个数据库是不是出问题了? 。
35 | join语句怎么优化? 。