存储引擎指定了表的类型,即如何存储和索引数据,是否支持事务,同时存储引擎也决定了表在计算机中的存储方式。
-- 查看数据库支持的存储引擎
show engines;
或者
show variables like 'have%';
-- 查看当前默认的存储引擎
show variables like 'storage_engine%';
MySQL中常用的四种存储引擎分别是: MyISAM、InnoDB、MEMORY、ARCHIVE。
InnoDB是MySQL默认的事务型存储引擎,使用最广泛,基于聚簇索引建立的。InnoDB内部做了很多优化,如能够自动在内存中创建自适应hash索引,以加速读操作。
优点:支持事务和崩溃修复能力;引入了行级锁和外键约束。
缺点:占用的数据空间相对较大。
适用场景:需要事务支持,并且有较高的并发读写频率。
数据以紧密格式存储。对于只读数据,或者表比较小、可以容忍修复操作,可以使用MyISAM引擎。MyISAM会将表存储在两个文件中,数据文件.MYD和索引文件.MYI。
优点:访问速度快。
缺点:MyISAM不支持事务和行级锁,不支持崩溃后的安全恢复,也不支持外键。
适用场景:对事务完整性没有要求;表的数据都会只读的。
MEMORY引擎将数据全部放在内存中,访问速度较快,但是一旦系统奔溃的话,数据都会丢失,MEMORY引擎默认使用哈希索引,将键的哈希值和指向数据行的指针保存在哈希索引中。
优点:访问速度较快。
缺点:哈希索引数据不是按照索引值顺序存储,无法用于排序。不支持部分索引匹配查找,因为哈希索引是使用索引列的全部内容来计算哈希值的。只支持等值比较,不支持范围查询。当出现哈希冲突时,存储引擎需要遍历链表中所有的行指针,逐行进行比较,直到找到符合条件的行。
ARCHIVE存储引擎非常适合存储大量独立的、作为历史记录的数据。ARCHIVE提供了压缩功能,拥有高效的插入速度,但是这种引擎不支持索引,所以查询性能较差。
MyISAM
只有表级锁。适用场景:
第一范式1NF:确保数据库表字段的原子性,1NF是对属性的原子性约束,要求属性具有原子性,不可再分解,数据表中的每一列(每个字段)都不可以再拆分。
例:字段
userInfo
:广东省 10086'
,依照第一范式必须拆分成userInfo
:广东省
userTel
:10086
两个字段。
第二范式2NF:首先要满足第一范式,另外包含两部分内容,一是表必须有一个主键;二是非主键列必须完全依赖于主键,而不能只依赖于主键的一部分。2NF是对记录的惟一性约束,要求记录有惟一标识,即实体的惟一性;
例:假定选课关系表为student_course(student_no, student_name, age, course_name, grade, credit),主键为(student_no, course_name)。其中学分完全依赖于课程名称,姓名年龄完全依赖学号,不符合第二范式,会导致数据冗余(学生选n门课,姓名年龄有n条记录)、插入异常(插入一门新课,因为没有学号,无法保存新课记录)等问题。
可以拆分成三个表:学生:student(stuent_no, student_name, 年龄);课程:course(course_name, credit);选课关系:student_course_relation(student_no, course_name, grade)。
第三范式3NF:首先要满足第二范式,另外非主键列必须直接依赖于主键,不能存在传递依赖。即不能存在:非主键列 A 依赖于非主键列 B,非主键列 B 依赖于主键的情况。3NF是对字段冗余性的约束,即任何字段不能由其他字段派生出来,它要求字段没有冗余。
假定学生关系表为Student(student_no, student_name, age, academy_id, academy_telephone),主键为"学号",其中学院id依赖于学号,而学院地点和学院电话依赖于学院id,存在传递依赖,不符合第三范式。
可以把学生关系表分为如下两个表:学生:(student_no, student_name, age, academy_id);学院:(academy_id, academy_telephone)。
2NF和3NF的区别?
三范式化设计的优缺点:
优点:可以尽量得减少数据冗余,使得更新快,体积小
缺点:对于查询需要多个表进行关联,减少写得效率增加读得效率,更难进行索引优化。
参考:一条SQL语句在Mysql中是如何执行的
MySQL主要分为 Server 层和存储引擎层:
Server 层基本组件
参考:一条SQL语句在Mysql中是如何执行的
查询语句的执行流程:连接器权限校验---》查询缓存---》分析器---》优化器---》权限校验---》执行器---》引擎。
例:
select
*
from
user
where
id > 1
and
name
=
'大彬'
;
更新语句的执行流程:分析器、权限校验、执行器、引擎、redo log
(prepare
状态)、binlog
、redo log
(commit
状态)
例:
update
user
set
name
=
'大彬'
where
id = 1;
为什么记录完redo log,不直接提交,而是先进入prepare状态?
假设先写redo log直接提交,然后写binlog,写完redo log后,机器挂了,binlog日志没有被写入,那么机器重启后,这台机器会通过redo log恢复数据,但是这个时候binlog并没有记录该数据,后续进行机器备份的时候,就会丢失这一条数据,同时主从同步也会丢失这一条数据。
MySQL日志主要包括查询日志、慢查询日志、事务日志、错误日志、二进制日志等。其中比较重要的是 bin log
(二进制日志)和 redo log
(重做日志)和 undo log
(回滚日志)。
bin log是MySQL数据库级别的文件,记录对MySQL数据库执行修改的所有操作,不会记录select和show语句,主要用于恢复数据库和同步数据库,数据库主从同步就是基于此日志文件。
redo log是innodb引擎级别,用来记录innodb存储引擎的事务日志,不管事务是否提交都会记录下来,用于数据恢复。当数据库发生故障,innoDB存储引擎会使用redo log恢复到发生故障前的时刻,以此来保证数据的完整性。将参数innodb_flush_log_at_tx_commit设置为1,那么在执行commit时会将redo log同步写到磁盘。
除了记录redo log外,当进行数据修改时还会记录undo log,undo log用于数据的撤回操作,它保留了记录修改前的内容。通过undo log可以实现事务回滚,并且可以根据undo log回溯到某个特定的版本的数据,实现MVCC。
当MySQL单表记录数过大时,数据库的性能会明显下降,一些常见的优化措施如下:
可以从这几个维度回答这个问题:
- MyISAM支持表锁,InnoDB支持表锁和行锁,默认为行锁
- 表级锁:开销小,加锁快,不会出现死锁。锁定粒度大,发生锁冲突的概率最高,并发量最低
- 行级锁:开销大,加锁慢,会出现死锁。锁力度小,发生锁冲突的概率小,并发度最高
# 在 mysql的安装配置文件my.cnf中【mysqlid】位置处设置参数,然后重启【永久有效】,my.cnf
的位置一般再/etc/my.cnf
# 常用参数:
slow_query_log = "ON" # 是否开启慢查询日志,默认OFF,开启则设置为 ON。
slow_query_log_file = "/mydata/mysqllog/slowquery.log" # 慢查询日志文件存储位置(绝对路径)。
log-slow-queries = /var/lib/mysql/slowquery.log (指定日志文件存放位置,可以为空,系统会给一个缺省的文件host_name-slow.log)
log_queries_not_using_indexes = "ON" # 是否把没有使用到索引的SQL记录到日志中,默认OFF,开启则设置为 ON。
long_query_time = 2 # 超过多少秒的查询才会记录到日志中,注意单位是秒, 默认是10秒。
常用的主键有三种:用的最多的是自增ID、还有uuid、雪花ID等 。如果是数据量不是很大的情况下不需要分库分表的时,使用自增ID为主键最合适。如果是分布式存储(分库、分表等)就要考虑使用uuid做主键,可以对其进行改造变成有序的uuid既可。
优点:
缺点:
自增ID回溯
的问题,这个问题直到最新版本的MySQL 8.0才修复。局部唯一
,只在当前数据库实例中唯一,而不是全局唯一,在任意服务器间都是唯一的。对于目前分布式系统来说,这简直就是噩梦。last_insert_id()
的函数才能知道刚才插入的自增值,这需要多一次的网络交互。在海量并发的系统中,多1条SQL,就多一次性能上的开销。数据库服务器端
生成。对于高并发的负载,innodb在按主键进行插入的时候会造成明显的锁争用,主键的上界会成为争抢的热点,因为所有的插入都发生在这里,并发插入会导致间隙锁竞争。不适合自增ID为主键的情况:
UUID是Universally Unique Identifier的缩写,即通用唯一标识符。它是通过MAC地址,时间戳,命名空间,随机数,伪随机数来保证生成ID的唯一性,有着固定的大小(128bit)。它的唯一性和一致性特点使得可以无需注册过程就能够产生一个新的UUID。UUID可以被用作多种用途,既可以用来短时间内标记一个对象,也可以可靠的辨别网络中的持久性对象。
MySQL中的UUID组成 = [时间低位+时间中位+时间高位](16字节)- 时钟序列(4字节) - MAC地址(12字节)
优点:
缺点:
隐私安全
的问题,因为UUID包含了MAC地址,也就是机械的物理地址。CREATE TABLE `jinjiang`.`test_table`(
`uuid` BINARY(16) NOT NULL,
`uuid_string` VARCHAR(64),
`num` INT(4),
PRIMARY KEY (`uuid`)
) ENGINE=INNODB;
-- 定义一个变量
SET @uuid_string = UUID();
-- 交换时间高位、时间地位的顺序
INSERT INTO test_table(`uuid`, uuid_string, num) VALUE(uuid_to_bin(@uuid_string, 1), @uuid_string, 1);
-- 不交换
INSERT INTO test_table(`uuid`, uuid_string, num) VALUE(uuid_to_bin(@uuid_string), @uuid_string, 2);
-- 查询
SELECT `uuid`, HEX(`uuid`), uuid_string, num FROM test_table ORDER BY num;
执行结果如图: