mysql面试常考知识点
该文章借鉴了不同平台对知识点的描述。
1、存储引擎的区别
1.1 InnoDB 和MYISAM 存储引擎的区别?
InnoDB:
- InnoDB 存储引擎支持事务、支持外键、支持非锁定读、行锁设计其设计主要面向OLTP 应用。
- InnoDB 存储引擎表采用聚集的方式存储,因此每张表的存储顺序都按主键的顺序存放,如果没有指定主键,InnoDB 存储引擎会为每一行生成一个6字节的ROWID并以此作为主键。
- InnoDB 存储引擎通过MVCC 获的高并发性,并提供了插入缓冲、二次写、自适应哈希索引和预读等高性能高可用功能
- InnoDB 存储引擎默认隔离级别为REPEATABLE_READ(重复读)并采用next-key locking(间隙锁)来避免幻读
MySIAM:
- MYISAM 存储引擎不支持事务、表锁设计、支持全文索引其设计主要面向OLAP 应用
- MYISAM 存储引擎表由frm、MYD 和MYI 组成,frm 文件存放表格定义,MYD 用来存放数据文件,MYI 存放索引文件。MYISAM 存储引擎与众不同的地方在于它的缓冲池只缓存索引文件而不缓存数据文件,数据文件的缓存依赖于操作系统。
操作区别:
- MYISAM 保存表的具体行数,不带where 是可直接返回。InnoDB 要扫描全表。
- DELETE 表时,InnoDB 是一行一行的删除,MYISAM 是先drop表,然后重建表
- InnoDB 跨平台可直接拷贝使用,MYISAM 不行
- InnoDB 表格很难被压缩,MYISAM 可以
选择:
- MyISAM相对简单所以在效率上要优于InnoDB。如果系统读多,写少。对原子性要求低。那么MyISAM最好的选择。且MyISAM恢复速度快。可直接用备份覆盖恢复。
- InnoDB 更适合系统读少,写多的时候,尤其是高并发场景。
2、索引
2.1、什么是索引?你知道Mysql 有哪些索引?
索引是对数据库表中一或多个列的值进行排序的结构,是帮助MySQL高效获取数据的数据结构
(数据库是磁盘文件,磁盘IO 的代价较高,所以采用索引减少IO 次数)
Mysql 中常用的索引有B+ 树索引(包括普通索引、唯一索引、主键索引),哈希索引,全文索引,R-TREE 索引(空间索引,主要用于地理空间数据类型,很少使用)。
Mysql 传统意义上的索引为B+ 树索引,B+ 树索引的本质就是B+ 树在数据库中的实现,由于B+ 树的高扇出性,数据库中的B+ 树的高一般为2-4层,因此查找某一键值的行记录只需2-4次IO,大概0.02~0.04秒。
(扇出性:是指该模块直接调用的下级模块的个数。扇出大表示模块的复杂度高,需要控制和协调过多的下级模块)
B+ 树索引
主要分为聚集索引和辅助索引。
聚集索引是根据每张表的主键建造的一棵B+ 树,叶子节点中存放的是整张表的行记录。一张表只能有一个聚集索引。因为聚集索引在逻辑上是连续的,所以它对于主键的排序查找和范围查找速度非常快。
辅助索引与聚集索引不同的地方在于,辅助索引不是唯一的,它的叶子节点只包含行记录的部分数据以及对应聚集索引的节点位置。通过辅助索引来查找数据时,先遍历辅助索引找到对应主键索引,再通过主键索引查找对应记录。
在MYISAM 中主键索引和辅助索引都相当上述辅助索引,索引页中存放的是主键和指向数据页的偏移量,数据页中存放的是主键和该主键所属行记录的地址空间。唯一的区别是MYISAM 中主键索引不能重复,辅助索引可以。
从使用上来说还有联合索引和覆盖索引。
联合索引是指对表上的多个列进行索引。它对对应多个列的指定获取比较快。另外一个好处是联合索引对第二个键已经排好序了,所以对两个列的排序获取可以避免多做一次排序操作。
覆盖索引其实更算一种思想,能够从辅助索引中获取信息,就不需要查询聚集索引中的数据。使用辅助索引的好处在于辅助索引包含的信息少,所以大小远小于聚集索引,因此可以大大减少IO 操作。
哈希索引是一种自适应的索引,数据库会根据表的使用情况自动生成哈希索引,我们人为是没办法干预的。
InnoDB 储存引擎采用的哈希函数为除法散列方式,采用的冲突处理方法为链地址法。它指定查询的速度很快,但是范围查询就无能为力了。
全文索引用于实现关键词搜索。但它只能根据空格分词,因此不支持中文。
2.2、索引的优缺点?那些情况适合建索引那些情况不适合建索引?
索引的优点:
- 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。
- 可以大大加快 数据的检索速度,这也是创建索引的最主要的原因。
- 可以加速表和表之间的连接,特别是在实现数据的参考完整性方面特别有意义。
- 在使用分组和排序 子句进行数据检索时,同样可以显著减少查询中分组和排序的时间。
- 通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能。
索引的缺点
- 创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加。
- 索引需要占物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果要建立聚簇索引,那么需要的空间就会更大。
- 当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,这样就降低了数据的维护速度。
扩展
聚簇索引和非聚簇索引:
哪些情况需要加索引?
- 在经常需要搜索的列上,可以加快搜索的速度;
- 在作为主键的列上,强制该列的唯一性和组织表中数据的排列结构;
- 在经常用在连接的列上,这些列主要是一些外键,可以加快连接的速度;
- 在经常需要根据范围进行搜索的列上创建索引,因为索引已经排序,其指定的范围是连续的;
- 在经常需要排序的列上创建索引,因为索引已经排序,这样查询可以利用索引的排序,加快排序查询时间;
- 在经常使用在WHERE子句中的列上面创建索引,加快条件的判断速度
哪些情况不需要加索引?
- 对于那些在查询中很少使用或者参考的列不应该创建索引。这是因为,既然这些列很少使用到,因此有索引或者无索引,并不能提高查询速度。相反,由于增加了索引,反而降低了系统的维护速度和增大了空间需求。
- 对于那些只有很少数据值的列也不应该增加索引。这是因为,由于这些列的取值很少,例如人事表的性别列,在查询的结果中,结果集的数据行占了表中数据行的很大比例,即需要在表中搜索的数据行的比例很大。增加索引,并不能明显加快检索速度。
- 对于那些定义为text, image和bit数据类型的列不应该增加索引。这是因为,这些列的数据量要么相当大,要么取值很少。
- 当修改性能远远大于检索性能时,不应该创建索引。这是因为,修改性能和检索性能是互相矛盾的。当增加索引时,会提高检索性能,但是会降低修改性能。当减少索引时,会提高修改性能,降低检索性能。因此,当修改性能远远大于检索性能时,不应该创建索引。
3、事务
3.1、什么是事务,它有哪些特性?说一说事务的隔离级别,分别解决了什么问题?
什么是事务,它有哪些特性?
事务就是一组原子性的操作,这些操作要么全部发生,要么全部不发生。事务把数据库从一种一致性状态转换成另一种一致性状态。
事务具有ACID 四种特性,即原子性(atomicity),一致性(consistency),隔离性(isolation),持久性(durability):
- 原子性,指的是事务是一个不可分割的操作,要么全都正确执行,要么全都不执行。
- 一致性,指的是事务把数据库从一种一致性状态转换成另一种一致性状态,事务开始前和事务结束后,数据库的完整性约束没有被破坏。
- 隔离性要求每个读写事务相互之间是分开的,在事务提交前对其他事务是不可见的
- 持久性,指的是事务一旦提交,其结果就是永久性的,即使宕机也能恢复。
事务的隔离级别,分别解决了什么问题?
事务有4 个隔离级别,分别是:
- 读未提交(read uncommit)
- 读已提交(read commit)
- 可重复读(repeatable read)
- 和序列化(serializable)。
隔离级别依次提高,分别解决了脏读、不可重读和幻读。
- 脏读:事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据
- 不可重复读:事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果 不一致。
- 幻读:系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读
- InnoDB 默认隔离级别为repeatable read,但是通过next-key lock 解决了幻读,保证了ACID。
不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表
3.2事务的实现原理?事务的分类?使用事务应该注意的问题?
事务的实现原理?
事务是基于重做日志(redo log)文件和回滚日志(undo log)实现的
每提交一个事务必须先将该事务的所有日志写入到重做日志文件进行持久化,数据库就可以通过重做日志来保证事务的原子性和持久性。
每当有修改事务时, 还会产生undo log,如果需要回滚, 则根据undo log反向语句进行逻辑操作,比如insert一条记录就delete一条记录。undo log主要实现数据库的一致性, 还可以用来实现MVCC。
事务的分类?
- 扁平事务
- 带有保存点的扁平事务
- 链事务
- 嵌套事务
- 分布式事务
使用事务应该注意的问题?
- 不要在循环中使用事务(循环提交会导致大量的redo log)
- 不要使用自动提交
- 不要使用自动回滚
- 长事务切分处理
4、SQL优化
4.1、sql优化的一般步骤?
a.通过show status命令了解各SQL执行的频率
show [session|global] status like “Com_%”;
- session:当前连接执行的统计结果
- global:上一次数据库启动至今的统计结果
常见的执行参数
- Com_select:执行查询的次数
- Com_insert:执行插入的次数
- Com_update:执行更新的次数
- Com_delete:执行删除的次数
- Com_rows_read:执行查询的返回行数(举一反三:inserted、updated、deleted)
- Connection:试图连接Mysql 服务器的次数
- Uptime:服务器工作时间
- Slow_queries:慢查询次数
1 mysql> show global status like " 2 Slow_queries"; 3 +---------------+-------+ 4 | Variable_name | Value | 5 +---------------+-------+ 6 | Slow_queries | 0 | 7 +---------------+-------+ 8 1 row in set
b. 定位执行效率最低的sql语句
可以通过两个办法定位效率较低的SQL 语句:
- 通过慢查询日志定位执行效率低的SQL,但是只能在查询完过后
- 使用show processlist 命令查看当前Mysql 正在进行的线程,包括线程状态、是否锁表
c.通过EXPLAIN 分析低效SQL 的执行计划
查询到效率低的sql 语句后,可以通过EXPLAIN 分析低效SQL 的执行计划。
mysql> explain select * from comment ; +----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------+ | 1 | SIMPLE | comment | NULL | ALL | NULL | NULL | NULL | NULL | 92 | 100 | NULL | +----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------+ 1 row in set mysql>
- select_type: 表示select 的类型,常见取值有SIMPLE(简单表,即不使用表连接或者子查询)、PRIMARY、UNION、SUBQUERY等。
- table: 输出结果集的表
- type: 表示Mysql 的访问方式(从上到下依次变快)
- type=all,全表扫描,Mysql 遍历全表来找到匹配的行
- type=index,Mysql 遍历整个索引来找到匹配的行
- type=range,索引范围扫面
- type=ref,使用非唯一索引扫描或唯一索引的前缀扫描
- type=eq_ref,使用唯一索引
- type-const/system,单表中最多只有一个匹配行
- type=NUll,MySQL 不用访问表或者索引就能直接得到结果
- possible_key: 表示查询时可能用到的索引
- key: 表示实际用到的索引
- key_len: 使用到索引字段的长度
- rows: 扫描行的数量
- Extra: 执行情况的说明和描述。
d.通过show profile 分析SQL
//默认不开启profile,使用时先开启profile mysql> set profiling=1; Query OK, 0 rows affected mysql> select @@profiling; +-------------+ | @@profiling | +-------------+ | 1 | +-------------+ 1 row in set //在InnoDB 下获取表行数 mysql> select count(*) from comment; +----------+ | count(*) | +----------+ | 92 | +----------+ 1 row in set // 查开执行时间 mysql> show profiles; +----------+------------+------------------------------+ | Query_ID | Duration | Query | +----------+------------+------------------------------+ | 1 | 0.0109785 | select @@profiling | | 2 | 0.05502275 | select count(*) from comment | +----------+------------+------------------------------+ 2 rows in set //具体查开每一步执行时间 mysql> show profile for query 2 ; +----------------------+----------+ | Status | Duration | +----------------------+----------+ | starting | 9.2E-5 | | checking permissions | 9E-6 | | Opening tables | 2E-5 | | init | 1.8E-5 | | System lock | 9E-6 | | optimizing | 0.054635 | //在优化处理耗时最多 | statistics | 5.6E-5 | | preparing | 1.7E-5 | | executing | 5E-6 | | Sending data | 8E-5 | | end | 5E-6 | | query end | 1.5E-5 | | closing tables | 9E-6 | | freeing items | 3.6E-5 | | cleaning up | 1.9E-5 | +----------------------+----------+ 15 rows in set mysql>
f. 还可以通过trace 分析优化器如何选择执行计划的(有兴趣可以看一下)
4.2常用的SQL 的优化
a.对大批量插入数据
- MYISAM 表
可以通过DISABLE KEY 和 ENABLE KEY 关闭和打开MYISAM 表索引的更新来提高效率。
1 ALTER TABLE table_name DISABLE KEY //在导入数据前关闭索引更新 2 loading the data 3 ALTER TABLE table_name ENABLE KEY //导入完成后开启
- InnoDB 表
- 按主键顺序导入
- 关闭唯一性检验,导入后再开启
- 关闭自动提交,导入后再开启
b.优化INSERT 语句
- 同一个客户端,应使用多个值表的insert 语句,这种方式可以大大缩减客户端与数据库之间的连接、关闭等消耗。
insert into table_name values(1,2)(1,3)(1,4)...
- 从不同的客户端插入的话,可以采用INSERT DELAYED 语句获得更高的速度。它的意思是让INSERT 语句马上执行,因为数据都被放在内存的队列中,并没有真正写入磁盘。
- 从文本文件装载一个表时,使用LOAD DATA INFILE 通常比INSERT 快20 倍。
- 将索引文件和数据文件分放在不同的磁盘上(利用建表中的选项)。
- 如果是批量插入,MYISAM 可以通过改变bulk_insert_buffer_size (插入缓存容量大小)变量值提高速度。
c 优化order by 语句
- 在MySQL 数据库中有两种排序方式:
- 通过有序索引扫描直接返回有序数据
- 通过对返回数据进行排序(Filesort 排序)
- 其中Filesort 会多排一次序,所以在使用order by 语句的时候尽量用到索引。下面的规则是可以用到索引的情况:
- where 和 order by 使用相同的索引
- order by 的顺序和索引的顺序相同
- order by 的字段都是升序或都是降序
- 同理用不到索引的情况:
- order by 的字段混合ASC 和DESC
- 用于查询行的关键字与ORDER BY 中所使用的不同
- 对不同的关键字使用order by
d. 优化group by 语句
默认情况下group by 语句对分组的数据进行排序操作,如果不需要排序操作可以通过order by null 禁止排序。
e. 优化嵌套语句
如果可以的话,特别是where 中包含索引的情况,用join 语法来代替嵌套语法(in)因为join 不需要在MySQL 的内存中创建临时表。
f. 优化or语句
对于含有or 的查询语句,如果要利用索引,则or 之间的每一个条件都必须用到索引,如果没用索引,可以考虑增加索引。否则会全表扫面。
g. 优化分页查询
一般分页查询时,通过创建覆盖索引能够比较好的提高性能。一个常见又头痛的场景是"limit 1000,20" 此时MySQL 排序出前1020 条数据后,只需要返回20条数据,查询和排序的代价都很高。有两种优化方案
- 在索引上完成排序分页的操作,最后根据主键关联查询需要的内容,让MySQL 扫描尽可能少的页面来提高分页操作。
1 mysql> explain select comment_id,comment_content from comment order by comment_create_time limit 50,5; 2 +----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+----------------+ 3 | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | 4 +----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+----------------+ 5 | 1 | SIMPLE | comment | NULL | ALL | NULL | NULL | NULL | NULL | 92 | 100 | Using filesort | 6 +----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+----------------+ 7 1 row in set 8 9 mysql> explain select a.comment_id,a.comment_content from comment a inner join(select comment_id from comment order by comment_create_time limit 50,5)b on a.comment_id=b.comment_id ; 10 +----+-------------+------------+------------+--------+---------------+---------+---------+--------------+------+----------+----------------+ 11 | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | 12 +----+-------------+------------+------------+--------+---------------+---------+---------+--------------+------+----------+----------------+ 13 | 1 | PRIMARY | <derived2> | NULL | ALL | NULL | NULL | NULL | NULL | 55 | 100 | NULL | 14 | 1 | PRIMARY | a | NULL | eq_ref | PRIMARY | PRIMARY | 4 | b.comment_id | 1 | 100 | NULL | 15 | 2 | DERIVED | comment | NULL | ALL | NULL | NULL | NULL | NULL | 92 | 100 | Using filesort | 16 +----+-------------+------------+------------+--------+---------------+---------+---------+--------------+------+----------+----------------+ 17 3 rows in set
- 把limit 查询转换成某个位置的查询,limit m,n -> limit n
h. 使用SQL 提示
SQL 提示是优化数据库的一个重要手段,常用的SQL 提示:
- USE INDEX
推荐数据库使用某个索引,可以让Mysql 不再考虑其他可用索引
1 sql语句 use index(index_name);
- IGNORE INDEX
忽视数据库某个索引,可以让Mysql 不再考虑这个索引
1 sql语句 ignore index(index_name);
- FORCE INDEX
强迫数据库使用某个索引,使用use index 数据库还是可能不用这个索引,但是force index 数据库必须使用这个索引
1 sql语句 force index(index_name);
5、sql语句 force index(index_name)
5.1、数据库的三大范式
第一范式:
确保每列的原子性(强调的是列的原子性,即列不能够再分成其他几列).如果每列(或者每个属性)都是不可再分的最小数据单元(也称为最小的原子单元),则满足第一范式.
例如:顾客表(姓名、编号、地址、……)其中"地址"列还可以细分为国家、省、市、区等。
第二范式:
在第一范式的基础上更进一层,目标是确保表中的每列都和主键相关(一是表必须有一个主键;二是没有包含在主键中的列必须完全依赖于主键,而不能只依赖于主键的部分)如果一个关系满足第一范式,并且除了主键以外的其它列,都依赖于该主键,则满足第二范式.
例如:订单表(订单编号、产品编号、定购日期、价格、……),"订单编号"为主键,"产品编号"和主键列没有直接的关系,即"产品编号"列不依赖于主键列,应删除该列。
第三范式:
在第二范式的基础上更进一层,目标是确保每列都和主键列直接相关,而不是间接相关(另外非主键列必须直接依赖于主键,不能存在传递依赖).如果一个关系满足第二范式,并且不依赖于除了主键以外的其它列,则满足第三范式.
为了理解第三范式,需要根据Armstrong公里之一定义传递依赖。假设A、B和C是关系R的三个属性,如果A-〉B且B-〉C,则从这些函数依赖中,可以得出A-〉C,如上所述,
依赖A-〉C是传递依赖。
例如:订单表(订单编号,定购日期,顾客编号,顾客姓名,……),初看该表没有问题,满足第二范式,每列都和主键列"订单编号"相关,再细看你会发现"顾客姓名"和"顾客
编号"相关,"顾客编号"和"订单编号"又相关,最后经过传递依赖,"顾客姓名"也和"订单编号"相关。为了满足第三范式,应去掉"顾客姓名"列,放入客户表中。
5.2、 什么是存储过程?有哪些优缺点?
存储过程是一些预编译的SQL语句。
优点:
- 存储过程是一个预编译的代码块,执行效率比较高
- 存储过程在服务器端运行,减少客户端的压力
- 允许模块化程序设计,只需要创建一次过程,以后在程序中就可以调用该过程任意次,类似方法的复用
- 一个存储过程替代大量T_SQL语句 ,可以降低网络通信量,提高通信速率
- 可以一定程度上确保数据安全,对于没有权限执行存储过程的用户,也可授权他们执行存储过程。
缺点:
- 可移植性不灵活(因为存储过程依赖于具体的数据库)
- 不便于调试。
- 没办法应用缓存。虽然有全局临时表之类的方法可以做缓存,但同样加重了数据库的负担。如果缓存并发严重,经常要加锁,那效率实在堪忧。
- 无法适应数据库的切割(水平或垂直切割)。数据库切割之后,存储过程并不清楚数据存储在哪个数据库中。
5.3 说一说drop、delete与truncate的区别?
- drop直接删掉表有关的一切(数据/结构/约束…),不会记录日志,为DDL(Data Definition Language,数据库定义语言)操作。
- truncate 删除表中所有数据(再插入时自增长id又从1开始),该操作也不会记录日志所以比较快,为DDL操作。只能删table。
- DELETE语句执行删除的过程是每次从表中删除一行,需要记录日志,比较慢,可以加where 语句,为DML(Data Manipulation Language, 数据操纵语言)。可以删table 和view 。
- 速度上drop > truncate > delete
5.4 什么是视图?以及视图的使用场景有哪些?
视图是一种虚拟的表,具有和物理表相同的功能,没有物理存储。可以对视图进行增,改,查,操作,试图通常是有一个表或者多个表的行或列的子集。对视图的修改不影响基本表。它使得我们获取数据更容易,相比多表查询。
使用场景:
- 只希望用户查看特定信息的列
- 来源于多个表,可以创建视图提取我们需要的信息,简化操作。
5.5 超键、候选键、主键、外键分别是什么?
超键:在关系中能唯一标识元组的属性集称为关系模式的超键。一个属性可以为作为一个超键,多个属性组合在一起也可以作为一个超键。超键包含候选键和主键。
候选键:是最小超键,即没有冗余元素的超键。
主键:数据库表中对储存数据对象予以唯一和完整标识的数据列或属性的组合。一个数据列只能有一个主键,且主键的取值不能缺失,即不能为空值(Null)。
外键:在一个表中存在的另一个表的主键称此表的外键
5.6 Mysql 的几种连接方式?
- 内连接:inner join on
组合两个表中的记录,返回关联字段相符的记录,也就是返回两个表的交集(阴影)部分。
- 左连接:left join on / left outer join on
left join 是left outer join的简写,它的全称是左外连接,是外连接中的一种。
左(外)连接,左表(a_table)的记录将会全部表示出来,而右表(b_table)只会显示符合搜索条件的记录。右表记录不足的地方均为NULL。
- 右连接:right join on / right outer join on
right join是right outer join的简写,它的全称是右外连接,是外连接中的一种。
与左(外)连接相反,右(外)连接,左表(a_table)只会显示符合搜索条件的记录,而右表(b_table)的记录将会全部表示出来。左表记录不足的地方均为NULL。
- 全连接
MySQL目前不支持此种方式,可以用其他方式替代解决。
5.7 说一说什么是Mysql 的完整性约束?
完整性约束是对字段进行限制,从而符合该字段达到我们期望的效果比如字段含有默认值,不能是NULL等, 主要有唯一、自增、主键、外键约束
1 PRIMARY KEY (PK) 标识该字段为该表的主键,可以唯一的标识记录 2 FOREIGN KEY (FK) 标识该字段为该表的外键 3 NOT NULL 标识该字段不能为空 4 UNIQUE KEY (UK) 标识该字段的值是唯一的 5 AUTO_INCREMENT 标识该字段的值自动增长(整数类型,而且为主键) 6 DEFAULT 为该字段设置默认值 7 8 UNSIGNED 无符号 9 ZEROFILL 使用0填充
参考文章:https://blog.csdn.net/qq_36906627/java/article/details/86634518