当前位置:首页 > SQL > 正文内容

PHP面试指南2020-MySQL常见问题

高老师4年前 (2021-03-05)SQL1112

索引相关

1. 什么是索引?

索引是一种数据结构,可以帮助我们快速的进行数据的查找.

2. 索引是个什么样的数据结构呢?

索引的数据结构和具体存储引擎的实现有关, 在MySQL中使用较多的索引有Hash索引,B+树索引等,而我们经常使用的InnoDB存储引擎的默认索引实现为:B+树索引.

3. Hash索引和B+树所有有什么区别或者说优劣呢?

首先要知道Hash索引和B+树索引的底层实现原理:

hash索引底层就是hash表,进行查找时,调用一次hash函数就可以获取到相应的键值,之后进行回表查询获得实际数据.B+树底层实现是多路平衡查找树.对于每一次的查询都是从根节点出发,查找到叶子节点方可以获得所查键值,然后根据查询判断是否需要回表查询数据.

那么可以看出他们有以下的不同:

  • hash索引进行等值查询更快(一般情况下),但是却无法进行范围查询.

因为在hash索引中经过hash函数建立索引之后,索引的顺序与原顺序无法保持一致,不能支持范围查询.而B+树的的所有节点皆遵循(左节点小于父节点,右节点大于父节点,多叉树也类似),天然支持范围.

  • hash索引不支持使用索引进行排序,原理同上.

  • hash索引不支持模糊查询以及多列索引的最左前缀匹配.原理也是因为hash函数的不可预测.AAAAAAAAB的索引没有相关性.

  • hash索引任何时候都避免不了回表查询数据,而B+树在符合某些条件(聚簇索引,覆盖索引等)的时候可以只通过索引完成查询.

  • hash索引虽然在等值查询上较快,但是不稳定.性能不可预测,当某个键值存在大量重复的时候,发生hash碰撞,此时效率可能极差.而B+树的查询效率比较稳定,对于所有的查询都是从根节点到叶子节点,且树的高度较低.

因此,在大多数情况下,直接选择B+树索引可以获得稳定且较好的查询速度.而不需要使用hash索引.

4. 上面提到了B+树在满足聚簇索引和覆盖索引的时候不需要回表查询数据,什么是聚簇索引?

在B+树的索引中,叶子节点可能存储了当前的key值,也可能存储了当前的key值以及整行的数据,这就是聚簇索引和非聚簇索引. 在InnoDB中,只有主键索引是聚簇索引,如果没有主键,则挑选一个唯一键建立聚簇索引.如果没有唯一键,则隐式的生成一个键来建立聚簇索引.

当查询使用聚簇索引时,在对应的叶子节点,可以获取到整行数据,因此不用再次进行回表查询.

5. 非聚簇索引一定会回表查询吗?

不一定,这涉及到查询语句所要求的字段是否全部命中了索引,如果全部命中了索引,那么就不必再进行回表查询.

举个简单的例子,假设我们在员工表的年龄上建立了索引,那么当进行select age from employee where age < 20的查询时,在索引的叶子节点上,已经包含了age信息,不会再次进行回表查询.

6. 在建立索引的时候,都有哪些需要考虑的因素呢?

建立索引的时候一般要考虑到字段的使用频率,经常作为条件进行查询的字段比较适合.如果需要建立联合索引的话,还需要考虑联合索引中的顺序.此外也要考虑其他方面,比如防止过多的所有对表造成太大的压力.这些都和实际的表结构以及查询方式有关.

7. 联合索引是什么?为什么需要注意联合索引中的顺序?

MySQL可以使用多个字段同时建立一个索引,叫做联合索引.在联合索引中,如果想要命中索引,需要按照建立索引时的字段顺序挨个使用,否则无法命中索引.

具体原因为:

MySQL使用索引时需要索引有序,假设现在建立了"name,age,school"的联合索引,那么索引的排序为: 先按照name排序,如果name相同,则按照age排序,如果age的值也相等,则按照school进行排序.

当进行查询时,此时索引仅仅按照name严格有序,因此必须首先使用name字段进行等值查询,之后对于匹配到的列而言,其按照age字段严格有序,此时可以使用age字段用做索引查找,,,以此类推.因此在建立联合索引的时候应该注意索引列的顺序,一般情况下,将查询需求频繁或者字段选择性高的列放在前面.此外可以根据特例的查询或者表结构进行单独的调整.

8. 创建的索引有没有被使用到?或者说怎么才可以知道这条语句运行很慢的原因?

MySQL提供了explain命令来查看语句的执行计划,MySQL在执行某个语句之前,会将该语句过一遍查询优化器,之后会拿到对语句的分析,也就是执行计划,其中包含了许多信息.
可以通过其中和索引有关的信息来分析是否命中了索引,例如possilbe_key,key,key_len等字段,分别说明了此语句可能会使用的索引,实际使用的索引以及使用的索引长度.

9. 那么在哪些情况下会发生针对该列创建了索引但是在查询的时候并没有使用呢?

  • 使用不等于查询,

  • 列参与了数学运算或者函数

  • 在字符串like时左边是通配符.类似于'%aaa'.

  • 当mysql分析全表扫描比使用索引快的时候不使用索引.

  • 当使用联合索引,前面一个条件为范围查询,后面的即使符合最左前缀原则,也无法使用索引.

以上情况,MySQL无法使用索引.

事务相关

1. 什么是事务?

理解什么是事务最经典的就是转账的栗子,相信大家也都了解,这里就不再说一边了.

事务是一系列的操作,他们要符合ACID特性.最常见的理解就是:事务中的操作要么全部成功,要么全部失败.但是只是这样还不够的.

2. ACID是什么?可以详细说一下吗?

A=Atomicity

原子性,就是上面说的,要么全部成功,要么全部失败.不可能只执行一部分操作.

C=Consistency

系统(数据库)总是从一个一致性的状态转移到另一个一致性的状态,不会存在中间状态.

I=Isolation

隔离性: 通常来说:一个事务在完全提交之前,对其他事务是不可见的.注意前面的通常来说加了红色,意味着有例外情况.

D=Durability

持久性,一旦事务提交,那么就永远是这样子了,哪怕系统崩溃也不会影响到这个事务的结果.

3. 同时有多个事务在进行会怎么样呢?

多事务的并发进行一般会造成以下几个问题:

  • 脏读: A事务读取到了B事务未提交的内容,而B事务后面进行了回滚.

  • 不可重复读: 当设置A事务只能读取B事务已经提交的部分,会造成在A事务内的两次查询,结果竟然不一样,因为在此期间B事务进行了提交操作.

  • 幻读: A事务读取了一个范围的内容,而同时B事务在此期间插入了一条数据.造成"幻觉".

4. 怎么解决这些问题呢?MySQL的事务隔离级别了解吗?

MySQL的四种隔离级别如下:

  • 未提交读(READ UNCOMMITTED)

这就是上面所说的例外情况了,这个隔离级别下,其他事务可以看到本事务没有提交的部分修改.因此会造成脏读的问题(读取到了其他事务未提交的部分,而之后该事务进行了回滚).

这个级别的性能没有足够大的优势,但是又有很多的问题,因此很少使用.

  • 已提交读(READ COMMITTED)

其他事务只能读取到本事务已经提交的部分.这个隔离级别有 不可重复读的问题,在同一个事务内的两次读取,拿到的结果竟然不一样,因为另外一个事务对数据进行了修改.

  • REPEATABLE READ(可重复读)

可重复读隔离级别解决了上面不可重复读的问题(看名字也知道),但是仍然有一个新问题,就是 幻读,当你读取id> 10 的数据行时,对涉及到的所有行加上了读锁,此时例外一个事务新插入了一条id=11的数据,因为是新插入的,所以不会触发上面的锁的排斥,那么进行本事务进行下一次的查询时会发现有一条id=11的数据,而上次的查询操作并没有获取到,再进行插入就会有主键冲突的问题.

  • SERIALIZABLE(可串行化)

这是最高的隔离级别,可以解决上面提到的所有问题,因为他强制将所以的操作串行执行,这会导致并发性能极速下降,因此也不是很常用.

5. Innodb使用的是哪种隔离级别呢?

InnoDB默认使用的是可重复读隔离级别.

6. 对MySQL的锁了解吗?

当数据库有并发事务的时候,可能会产生数据的不一致,这时候需要一些机制来保证访问的次序,锁机制就是这样的一个机制.

就像酒店的房间,如果大家随意进出,就会出现多人抢夺同一个房间的情况,而在房间上装上锁,申请到钥匙的人才可以入住并且将房间锁起来,其他人只有等他使用完毕才可以再次使用.

7. MySQL都有哪些锁呢?像上面那样子进行锁定岂不是有点阻碍并发效率了?

从锁的类别上来讲,有共享锁和排他锁.

共享锁: 又叫做读锁. 当用户要进行数据的读取时,对数据加上共享锁.共享锁可以同时加上多个.

排他锁: 又叫做写锁. 当用户要进行数据的写入时,对数据加上排他锁.排他锁只可以加一个,他和其他的排他锁,共享锁都相斥.

用上面的例子来说就是用户的行为有两种,一种是来看房,多个用户一起看房是可以接受的. 一种是真正的入住一晚,在这期间,无论是想入住的还是想看房的都不可以.

锁的粒度取决于具体的存储引擎,InnoDB实现了行级锁,页级锁,表级锁.

他们的加锁开销从大大小,并发能力也是从大到小.

表结构设计

1. 为什么要尽量设定一个主键?

主键是数据库确保数据行在整张表唯一性的保障,即使业务上本张表没有主键,也建议添加一个自增长的ID列作为主键.设定了主键之后,在后续的删改查的时候可能更加快速以及确保操作数据范围安全.

2. 主键使用自增ID还是UUID?

推荐使用自增ID,不要使用UUID.

因为在InnoDB存储引擎中,主键索引是作为聚簇索引存在的,也就是说,主键索引的B+树叶子节点上存储了主键索引以及全部的数据(按照顺序),如果主键索引是自增ID,那么只需要不断向后排列即可,如果是UUID,由于到来的ID与原来的大小不确定,会造成非常多的数据插入,数据移动,然后导致产生很多的内存碎片,进而造成插入性能的下降.

总之,在数据量大一些的情况下,用自增主键性能会好一些.

关于主键是聚簇索引,如果没有主键,InnoDB会选择一个唯一键来作为聚簇索引,如果没有唯一键,会生成一个隐式的主键.

If you define a PRIMARY KEY on your table, InnoDB uses it as the clustered index.

If you do not define a PRIMARY KEY for your table, MySQL picks the first UNIQUE index that has only NOT NULL columns as the primary key and InnoDB uses it as the clustered index.

3. 字段为什么要求定义为not null?

MySQL官网这样介绍:

NULL columns require additional space in the rowto record whether their values are NULL. For MyISAM tables, each NULL columntakes one bit extra, rounded up to the nearest byte.

null值会占用更多的字节,且会在程序中造成很多与预期不符的情况.

4. 如果要存储用户的密码散列,应该使用什么字段进行存储?

密码散列,盐,用户身份证号等固定长度的字符串应该使用char而不是varchar来存储,这样可以节省空间且提高检索效率.

存储引擎相关

1. MySQL支持哪些存储引擎?

MySQL支持多种存储引擎,比如InnoDB,MyISAM,Memory,Archive等等.在大多数的情况下,直接选择使用InnoDB引擎都是最合适的,InnoDB也是MySQL的默认存储引擎.

  1. InnoDB和MyISAM有什么区别?

  • InnoDB支持事务,而MyISAM不支持事物,崩溃后无法安全恢复,表锁非常影响性能

  • InnoDB支持行级锁,而MyISAM支持表级锁

  • InnoDB支持MVCC,实现了四个标准的隔离级别 而MyISAM不支持

  • InnoDB 表是基于聚族索引建立的,聚族索引对主键查询有很高的性能

  • InnoDB支持外键,而MyISAM不支持

  • InnoDB不支持全文索引,而MyISAM支持,可以支持复杂的查询(注意:5.6版本之后InnoDB存储引擎开始支持全文索引,5.7版本之后通过使用ngram插件开始支持中文)

零散问题

1. MySQL中的varchar和char有什么区别.

char是一个定长字段,假如申请了char(10)的空间,那么无论实际存储多少内容.该字段都占用10个字符,而varchar是变长的,也就是说申请的只是最大长度,占用的空间为实际字符长度+1,最后一个字符存储使用了多长的空间.

在检索效率上来讲,char > varchar,因此在使用中,如果确定某个字段的值的长度,可以使用char,否则应该尽量使用varchar.例如存储用户MD5加密后的密码,则应该使用char.

2. varchar(10)和int(10)代表什么含义?

varchar的10代表了申请的空间长度,也是可以存储的数据的最大长度,而int的10只是代表了展示的长度,不足10位以0填充.也就是说,int(1)和int(10)所能存储的数字大小以及占用的空间都是相同的,只是在展示时按照长度展示.

3. MySQL的binlog有有几种录入格式?分别有什么区别?

有三种格式,statement,row和mixed.

  • statement模式下,记录单元为语句.即每一个sql造成的影响会记录.由于sql的执行是有上下文的,因此在保存的时候需要保存相关的信息,同时还有一些使用了函数之类的语句无法被记录复制.

  • row级别下,记录单元为每一行的改动,基本是可以全部记下来但是由于很多操作,会导致大量行的改动(比如alter table),因此这种模式的文件保存的信息太多,日志量太大.

  • mixed. 一种折中的方案,普通操作使用statement记录,当无法使用statement的时候使用row.

此外,新版的MySQL中对row级别也做了一些优化,当表结构发生变化的时候,会记录语句而不是逐行记录.

4. 超大分页怎么处理?

超大的分页一般从两个方向上来解决.

  • 数据库层面,这也是我们主要集中关注的(虽然收效没那么大),类似于select * from table where age > 20 limit 1000000,10这种查询其实也是有可以优化的余地的. 这条语句需要load1000000数据然后基本上全部丢弃,只取10条当然比较慢. 当时我们可以修改为select * from table where id in (select id from table where age > 20 limit 1000000,10).这样虽然也load了一百万的数据,但是由于索引覆盖,要查询的所有字段都在索引中,所以速度会很快. 同时如果ID连续的好,我们还可以select * from table where id > 1000000 limit 10,效率也是不错的,优化的可能性有许多种,但是核心思想都一样,就是减少load的数据.

  • 从需求的角度减少这种请求....主要是不做类似的需求(直接跳转到几百万页之后的具体某一页.只允许逐页查看或者按照给定的路线走,这样可预测,可缓存)以及防止ID泄漏且连续被人恶意攻击.

解决超大分页,其实主要是靠缓存,可预测性的提前查到内容,缓存至redis等k-V数据库中,直接返回即可.

在阿里巴巴《Java开发手册》中,对超大分页的解决办法是类似于上面提到的第一种.

2019-07-18-11-46-19

5. 关心过业务系统里面的sql耗时吗?统计过慢查询吗?对慢查询都怎么优化过?

在业务系统中,除了使用主键进行的查询,其他的我都会在测试库上测试其耗时,慢查询的统计主要由运维在做,会定期将业务中的慢查询反馈给我们.

慢查询的优化首先要搞明白慢的原因是什么? 是查询条件没有命中索引?是load了不需要的数据列?还是数据量太大?

所以优化也是针对这三个方向来的,

  • 首先分析语句,看看是否load了额外的数据,可能是查询了多余的行并且抛弃掉了,可能是加载了许多结果中并不需要的列,对语句进行分析以及重写.

  • 分析语句的执行计划,然后获得其使用索引的情况,之后修改语句或者修改索引,使得语句可以尽可能的命中索引.

  • 如果对语句的优化已经无法进行,可以考虑表中的数据量是否太大,如果是的话可以进行横向或者纵向的分表.

6. 上面提到横向分表和纵向分表,可以分别举一个适合他们的例子吗?

横向分表是按行分表.假设我们有一张用户表,主键是自增ID且同时是用户的ID.数据量较大,有1亿多条,那么此时放在一张表里的查询效果就不太理想.我们可以根据主键ID进行分表,无论是按尾号分,或者按ID的区间分都是可以的. 假设按照尾号0-99分为100个表,那么每张表中的数据就仅有100w.这时的查询效率无疑是可以满足要求的.

纵向分表是按列分表.假设我们现在有一张文章表.包含字段id-摘要-内容.而系统中的展示形式是刷新出一个列表,列表中仅包含标题和摘要,当用户点击某篇文章进入详情时才需要正文内容.此时,如果数据量大,将内容这个很大且不经常使用的列放在一起会拖慢原表的查询速度.我们可以将上面的表分为两张.id-摘要,id-内容.当用户点击详情,那主键再来取一次内容即可.而增加的存储量只是很小的主键字段.代价很小.

当然,分表其实和业务的关联度很高,在分表之前一定要做好调研以及benchmark.不要按照自己的猜想盲目操作.

7. 什么是存储过程?有哪些优缺点?

存储过程是一些预编译的SQL语句。
1、更加直白的理解:存储过程可以说是一个记录集,它是由一些T-SQL语句组成的代码块,这些T-SQL语句代码像一个方法一样实现一些功能(对单表或多表的增删改查),然后再给这个代码块取一个名字,在用到这个功能的时候调用他就行了。
2、存储过程是一个预编译的代码块,执行效率比较高,一个存储过程替代大量T_SQL语句 ,可以降低网络通信量,提高通信速率,可以一定程度上确保数据安全

但是,在互联网项目中,其实是不太推荐存储过程的,比较出名的就是阿里的《Java开发手册》中禁止使用存储过程,我个人的理解是,在互联网项目中,迭代太快,项目的生命周期也比较短,人员流动相比于传统的项目也更加频繁,在这样的情况下,存储过程的管理确实是没有那么方便,同时,复用性也没有写在服务层那么好.

8. 说一说三个范式

第一范式: 每个列都不可以再拆分.
第二范式: 非主键列完全依赖于主键,而不能是依赖于主键的一部分.
第三范式: 非主键列只依赖于主键,不依赖于其他非主键.

在设计数据库结构的时候,要尽量遵守三范式,如果不遵守,必须有足够的理由.比如性能. 事实上我们经常会为了性能而妥协数据库的设计.

9. MyBatis中的#和$有什么区别?

乱入了一个奇怪的问题.....我只是想单独记录一下这个问题,因为出现频率太高了.

# 会将传入的内容当做字符串,而$会直接将传入值拼接在sql语句中.

所以#可以在一定程度上预防sql注入攻击

LEFT JOIN 、RIGHT JOIN、INNER JOIN

  • LEFT JOIN(左连接):获取左表所有记录,即使右表没有对应匹配的记录

  • RIGHT JOIN(右连接): 与 LEFT JOIN 相反,用于获取右表所有记录,即使左表没有对应匹配的记录

  • INNER JOIN(内连接):获取两个表中字段匹配关系的记录

UNION、UNION ALL

  • UNION 操作符用于连接两个以上的 SELECT 语句的结果组合到一个结果集合中。多个 SELECT 语句会删除重复的数据

  • UNION ALL 操作符重复数据全部显示,不去重

常用 MySQL 函数

数学函数

  • floor(x) 返回不大于 x 的最大整数值

  • ceil/ceiling(x) 返回不小于 x 的最小整数

  • round(x) 四舍五入

  • rand() 随机函数[0, 1)

  • abs(x) 返回 x 的绝对值

字符串函数

  • concat(str1, str2, ...) 将参数连接成字符串返回

  • length(str) 返回字符串长度

日期和时间函数

  • now() 当前时间

  • curdate() 当前日期

SELECT UNIX_TIMESTAMP('2019-05-07 22:55:00'); #1557240900SELECT FROM_UNIXTIME(1557240900); #2019-05-07 22:55:00

系统信息函数

  • VERSION() 返回数据库的版本号

  • LAST_INSERT_ID() 返回最后生成的 AUTO_INCREMENT 值

加密函数

  • PASSWORD(str) 对字符串 str 进行加密

  • MD5(str) 对字符串 str 进行加密

格式化函数

  • FORMAT(x, n) 可以将数字 x 进行格式化,保留到小数点后 n 位,四舍五入

SELECT FORMAT(2.7895, 2); #2.79

获取当前时间(年月日时分秒)

mysql> select now();+---------------------+| now()               |+---------------------+| 2020-05-22 10:39:23 |+---------------------+

获取当前时间戳

mysql> select unix_timestamp(now());+-----------------------+| unix_timestamp(now()) |+-----------------------+|            1590115200 |+-----------------------+1 row in set (0.00 sec)

获取年月日

mysql> SELECT CURDATE(); +------------+| CURDATE()  |+------------+| 2020-05-22 |+------------+1 row in set (0.00 sec)

sql的left join 、right join 、inner join之间的区别

left join(左联接) 返回包括左表中的所有记录和右表中联结字段相等的记录 
right join(右联接) 返回包括右表中的所有记录和左表中联结字段相等的记录
inner join(等值连接) 只返回两个表中联结字段相等的行

char和varchar 的区别

主要区别

1、char的长度是不可变的,而varchar的长度是可变的

字段b:类型char(10),     值为:abc,存储为:abc  (abc+7个空格)

字段d:类型varchar(10), 值为:abc,存储为:abc (自动变为3个的长度)

2、超出长度自动截取

字段c:类型char(3),     值为:abcdefg,存储为:abc(defg自动删除)

字段e:类型varchar(3), 值为:abcdefg,存储为:abc (defg自动删除)

3、var(10)和char(10),都表示可存10个字符,无论存放的是数字、字母还是UTF8汉字(每个汉字3字节),都可以存放10个

4、char最多可以存放255个字符

varchar的最大长度为65535个字节,varchar可存放的字符数跟编码有关

字符类型若为gbk,每个字符最多占2个字节,最大长度不能超过32766个字符

字符类型若为utf8,每个字符最多占3个字节,最大长度不能超过21845个字符

5、char和varchar的最大长度限制是mysql规定的

深度剖析

在MySQL数据库中,用的最多的字符型数据类型就是Varchar和Char.。这两种数据类型虽然都是用来存放字符型数据,但是无论从结构还是从数据的保存方式来看,两者相差很大。而且其具体的实现方式,还依赖与存储引擎。这里就以大家最常用的MYISAM存储引擎为例,谈谈这两种数据类型的差异。

这里首先需要明白的一点是,这两种数据类型,无论采用哪一种存储引起,系统存储数据的方式都是不同的。正是因为如此,我们才有必要研究两者的不同。然后在合适的情况下,采用恰当的方式。

Varchar往往用来保存可变长度的字符串。简单的说,我们只是给其固定了一个最大值,然后系统会根据实际存储的数据量来分配合适的存储空间。为此相比CHAR字符数据而言,其能够比固定长度类型占用更少的存储空间。不过在实际工作中,由于某系特殊的原因,会在这里设置例外。如管理员可以根据需要指定ROW_FORMAT=FIXED选项。利用这个选项来创建MyISAM表的话,系统将会为每一行使用固定长度的空间。此时会造成存储空间的损耗。通常情况下,VARCHAR数据类型能够节约磁盘空间,为此往往认为其能够提升数据库的性能。不过这里需要注意的是,这往往是一把双刃剑。其在提升性能的同时,往往也会产生一些副作用。如因为其长度是可变的,为此在数据进行更新时可能会导致一些额外的工作。如在更改前,其字符长度是10位(Varchar规定的最长字符数假设是50位),此时系统就只给其分配10个存储的位置(假设不考虑系统自身的开销)。更改后,其数据量达到了20位。由于没有超过最大50位的限制,为此数据库还是允许其存储的。只是其原先的存储位置已经无法满足其存储的需求。此时系统就需要进行额外的操作。如根据存储引擎不同,有的会采用拆分机制,而有的则会采用分页机制。

CHAR数据类型与VARCHAR数据类型不同,其采用的是固定长度的存储方式。简单的说,就是系统总为其分配最大的存储空间。当数据保存时,即使其没有达到最大的长度,系统也会为其分配这么多的存储空间。显然,这种存储方式会造成磁盘空间的浪费。这里笔者需要提醒的一点是,当字符位数不足时,系统并不会采用空格来填充。相反,如果在保存CHAR值的时候,如果其后面有空值,系统还会自动过滤其空格。而在进行数据比较时,系统又会将空格填充到字符串的末尾。

显然,VARCHAR与CHAR两种字符型数据类型相比,最大的差异就是前者是可变长度,而后者则是固定长度。在存储时,前者会根据实际存储的数据来分配最终的存储空间。而后者则不管实际存储数据的长度,都是根据CHAR规定的长度来分配存储空间。这是否意味着CHAR的数据类型劣于VARCHAR呢?其实不然。否则的话,就没有必要存在CHAR字符类型了。虽然VARCHAR数据类型可以节省存储空间,提高数据处理的效率。但是其可变长度带来的一些负面效应,有时候会抵消其带来的优势。为此在某些情况下,还是需要使用Char数据类型。

注意:使用Varchar数据类型,也不能够太过于慷慨。这是什么意思呢?如现在用户需要存储一个地址信息。根据评估,只要使用100个字符就可以了。但是有些数据库管理员会认为,反正Varchar数据类型是根据实际的需要来分配长度的。还不如给其大一点的呢。为此他们可能会为这个字段一次性分配200个字符的存储空间。这VARCHAR(100)与VARCHAR(200)真的相同吗?结果是否定的。虽然他们用来存储90个字符的数据,其存储空间相同。但是对于内存的消耗是不同的。对于VARCHAR数据类型来说,硬盘上的存储空间虽然都是根据实际字符长度来分配存储空间的,但是对于内存来说,则不是。其时使用固定大小的内存块来保存值。简单的说,就是使用字符类型中定义的长度,即200个字符空间。显然,这对于排序或者临时表(这些内容都需要通过内存来实现)作业会产生比较大的不利影响。所以如果某些字段会涉及到文件排序或者基于磁盘的临时表时,分配VARCHAR数据类型时仍然不能够太过于慷慨。还是要评估实际需要的长度,然后选择一个最长的字段来设置字符长度。如果为了考虑冗余,可以留10%左右的字符长度。千万不能认为其为根据实际长度来分配存储空间,而随意的分配长度,或者说干脆使用最大的字符长度。

适用情况

1、对于MyISAM表,尽量使用Char,对于那些经常需要修改而容易形成碎片的myisam和isam数据表就更是如此,它的缺点就是占用磁盘空间;

2、对于InnoDB表,因为它的数据行内部存储格式对固定长度的数据行和可变长度的数据行不加区分(所有数据行共用一个表头部分,这个标头部分存放着指向各有关数据列的指针),所以使用char类型不见得会比使用varchar类型好。事实上,因为char类型通常要比varchar类型占用更多的空间,所以从减少空间占用量和减少磁盘i/o的角度,使用varchar类型反而更有利;

3、存储很短的信息,比如门牌号码101,201……这样很短的信息应该用char,因为varchar还要占个byte用于存储信息长度,本来打算节约存储的现在得不偿失。

4、固定长度的。比如使用uuid作为主键,那用char应该更合适。因为他固定长度,varchar动态根据长度的特性就消失了,而且还要占个长度信息。

5、十分频繁改变的column。因为varchar每次存储都要有额外的计算,得到长度等工作,如果一个非常频繁改变的,那就要有很多的精力用于计算,而这些对于char来说是不需要的。


扫描二维码推送至手机访问。

版权声明:本文由高久峰个人博客发布,如需转载请注明出处。

本文链接:https://blog.20230611.cn/post/349.html

分享给朋友:

“PHP面试指南2020-MySQL常见问题” 的相关文章

MySql基本的建表原则

MySql基本的建表原则

1.定长和变长的分离 如int,char,time所占字节是固定的字段放在一张表 如varchar,text所占字节不确定的字段放在一张表中2.常用字段和不常用字段进行分离,根据查询频率来设计3.一对多的关联表可以添加冗余字段,如商品分类表 和商品表 ,在首页中需要显示每个分类商...

MySQL中的where和having的区别

MySQL中的where和having的区别

   where与having非常类似.都能筛选数据.表达式完全一致.    但是职责的确不同.where负责对表中的字段进行筛选,having负责对where筛选后的结果集再次筛选。这也就是where不能使用别名字段来筛选的原因,因为数据中没有这个字段。&n...

mysql 中文占几个字节,mysql varchar 字节数

mysql 中文占几个字节,mysql varchar 字节数

我们要明白Mysql字段的长度能存多少东西,首先需要明白Mysql是计算字节长度,还是计算字符长度。在mysql4.x的版本长度代表的是字节长度.例如在mysql4.x的版本中varchar(10)能储存的中英文长度如下:(1).采用ISO8859-1编码方式时,一个中/英文都只占一个字节;(2)....

 mysql储存引擎,mysql的5种储存引擎

mysql储存引擎,mysql的5种储存引擎

1.MyISAM 建立一个MyISAM引擎的表时,就会在本地磁盘上建立三个文件,.frm格式文件,存储表定义;.MYD格式文件,存储数据;MYI格式文件,存储索引;方便数据迁移,我只需将mysql安装目录下data文件中的表文件复制即可完成数据迁移,之前在搬迁多个dedecms中深有体会。 ...

mysql恢复数据库,mysql数据库log文件恢复数据库

mysql恢复数据库,mysql数据库log文件恢复数据库

由于在开发中不小心删除客户上传的商品数据,客户本身也不好说话,只好恢复数据库,记录下过程。原理是数据库开启bin-log功能,然后再通过备份的bin-log进行数据库恢复。先查找备份的数据库日志二进制文件。一般在mysql的data文件夹中,文件名大概为mysql-bin.00001,具体看文件的修...

 php mysql 分页优化,mysql limit优化,mysql offset优化,mysql 最快的分页方法

php mysql 分页优化,mysql limit优化,mysql offset优化,mysql 最快的分页方法

(1).首先我们需要了解limit分页的工作流程demo:  SELECT * FROM table LIMIT 10000,5 通常我们会认为以上SQL会从10000条后面取5条,正确的是取出10005条,然后再抛弃前...