文章目录
-
-
- 1. 唯一索引比普通索引快吗, 为什么?
- 2. MySQL由哪些部分组成, 分别用来做什么?
- 3. Mysql查询缓存有什么弊端, 应该什么情况下使用, 8.0版本对查询缓存有什么变更?
- 4. MyISAM和InnoDB的区别有哪些?
- 5. Mysql怎么恢复半个月前的数据?
- 6. Mysql事务的隔离级别, 分别有什么特点?
- 7. 做过哪些Mysql索引相关优化?
- 8. 简要说一下数据库三范式
- 9. 一千万条数据的表, 如何分页查询
- 10. 订单表数据量越来越大导致查询缓慢, 如何处理?
- 11. 雪花算法
- 12.一张表,里面有ID自增主键,当insert了17条记录之后,删除了第15,16,17条记录,再把Mysql重启,再insert一条记录,这条记录的ID是18还是15?
- 13.Mysql 的技术特点是什么?
- 14.Heap 表是什么?
- 15.Mysql 服务器默认端口是什么?
- 16.与Oracle相比,Mysql有什么优势?
- 17.如何区分 FLOAT 和 DOUBLE?
- 18.区分CHAR_LENGTH和LENGTH?
- 19.请简洁描述Mysql中InnoDB支持的四种事务隔离级别名称,以及逐级之间的区别?
- 20.在 Mysql 中 ENUM 的用法是什么?
- 21.如何定义 REGEXP?
- 22.CHAR 和 VARCHAR 的区别?
- 23.列的字符串类型可以是什么?
- 24.如何获取当前的 Mysql 版本?
- 25.Mysql 中使用什么存储引擎?
- 26.Mysql 驱动程序是什么?
- 27.TIMESTAMP 在 UPDATE CURRENT_TIMESTAMP 数据类型上做什么?
- 28.主键和候选键有什么区别?
- 29.如何使用 Unix shell 登录 Mysql?
- 30.myisamchk 是用来做什么的?
- 31.Mysql数据库服务器性能分析的方法命令有哪些?
- 32.如何控制 HEAP 表的最大尺寸?
- 33.MyISAM Static 和 MyISAM Dynamic 有什么区别?
- 34.federated 表是什么?
- 35.如果一个表有一列定义为 TIMESTAMP,将发生什么?
- 36.列设置为 AUTO INCREMENT 时,如果在表中达到最大 值,会发生什么情况?
- 37.怎样才能找出最后一次插入时分配了哪个自动增量?
- 38.怎么看到为表格定义的所有索引?
- 39.LIKE 声明中的%和_是什么意思?
- 40.如何在 Unix 和 Mysql 时间戳之间进行转换?
- 41.列对比运算符是什么?
- 42.我们如何得到受查询影响的行数?
- 43.LIKE 和 REGEXP 操作有什么区别?
- 44.BLOB 和 TEXT 有什么区别?
- 45.如何显示前 50 行?
- 46.可以使用多少列创建索引?
- 47.NOW()和 CURRENT_DATE()有什么区别?
- 48.什么样的对象可以使用 CREATE 语句创建?
- 49.Mysql 表中允许有多少个 TRIGGERS?
- 50.什么是非标准字符串类型?
- 51.什么是通用 SQL 函数?
- 52.Mysql支持事务吗?
- 53.Mysql里记录货币用什么字段类型好?
- 54.Mysql数据表在什么情况下容易损坏?
- 55.Mysql 有关权限的表都有哪几个?
- 56.Mysql 中有哪几种锁?
- 57.数据库中的事务是什么?
- 58.SQL 注入漏洞产生的原因?如何防止?
- 59.外连接、内连接与自连接的区别?
- 60.什么是事务回滚机制
- 61.完整性约束包括哪些?
-
1. 唯一索引比普通索引快吗, 为什么?
唯一索引不一定比普通索引快, 可能还更慢。
- 查询时, 在未使用
limit
的情况下, 在匹配到一条数据后, 唯一索引即返回, 普通索引则会继续匹配下一条数据, 发现不匹配后返回。如此看来唯一索引少了一次匹配, 但实际上这个消耗微乎其微。 - 更新时, 这个情况就比较复杂了。普通索引将记录放到
change buffer
中语句就执行完毕了. 而对唯一索引而言, 它必须要校验唯一性, 因此, 必须将数据页读入内存确定没有冲突, 然后才能继续操作. 对于写多读少的情况, 普通索引利用change buffer
有效减少了对磁盘的访问次数, 因此普通索引性能要高于唯一索引。
2. MySQL由哪些部分组成, 分别用来做什么?
- Server
连接器: 管理连接, 权限验证
分析器: 词法分析, 语法分析
优化器: 执行计划生成, 索引的选择
执行器: 操作存储引擎, 返回执行结果 - 存储引擎: 存储数据, 提供读写接口
3. Mysql查询缓存有什么弊端, 应该什么情况下使用, 8.0版本对查询缓存有什么变更?
- 查询缓存可能会失效非常频繁, 对于一个表, 只要有更新, 该表的全部查询缓存都会被清空. 因此对
于频繁更新的表来说, 查询缓存不一定能起到正面效果 - 对于读远多于写的表可以考虑使用查询缓存
- 8.0版本的查询缓存功能被移除
4. MyISAM和InnoDB的区别有哪些?
- InnoDB支持事务, MyISAM不支持
- InnoDB支持行级锁, MyISAM支持表级锁
- InnoDB支持多版本并发控制(MVVC), MyISAM不支持
- InnoDB支持外键, MyISAM不支持
- MyISAM支持全文索引, InnoDB部分版本不支持(但可以使用Sphinx插件)
5. Mysql怎么恢复半个月前的数据?
通过整库备份+binlog进行恢复. 前提是要有定期整库备份且保存了binlog日志。
6. Mysql事务的隔离级别, 分别有什么特点?
- 读未提交(RU): 一个事务还没提交时, 它做的变更就能被别的事务看到
- 读提交(RC): 一个事务提交之后, 它做的变更才会被其他事务看到
- 可重复读(RR): 一个事务执行过程中看到的数据, 总是跟这个事务在启动时看到的数据是一致的. 当然在可重复读隔离级别下, 未提交变更对其他事务也是不可见的
- 串行化(S): 对于同一行记录, 读写都会加锁. 当出现读写锁冲突的时候, 后访问的事务必须等前一个事务执行完成才能继续执行
7. 做过哪些Mysql索引相关优化?
- 尽量使用主键查询: 聚簇索引上存储了全部数据, 相比普通索引查询, 减少了回表的消耗
- Mysql5.6之后引入了索引下推优化, 通过适当的使用联合索引, 减少回表判断的消耗
- 若频繁查询某一列数据, 可以考虑利用覆盖索引避免回表
- 联合索引将高频字段放在最左边
8. 简要说一下数据库三范式
- 第一范式: 属性不可再分
- 第二范式: 在一范式的基础上, 要求数据库表中的每个实例或行必须可以被唯一区分。通常需要为
表加上一个列, 以存储各个实例的唯一标识。这个唯一属性列被称为主关键字或主键 - 第三范式: 在二范式的基础上, 要求一个数据库表中不包含已在其它表中已包含的非主关键字信息
所以第三范式具有如下特征:
1). 每一列只有一个值. 2). 每一行都能区分. 3). 每一张表都不包含其他表已经包含的非主关键字信息
9. 一千万条数据的表, 如何分页查询
数据量过大的情况下, limit offset 分页会由于扫描数据太多而越往后查询越慢。可以配合当前页最后
一条ID进行查询, SELECT * FROM T WHERE id > #{ID} LIMIT #{LIMIT} . 当然, 这种情况下ID必须
是有序的, 这也是有序ID的好处之一。
10. 订单表数据量越来越大导致查询缓慢, 如何处理?
分库分表。由于历史订单使用率并不高, 高频的可能只是近期订单, 因此, 将订单表按照时间进行拆分, 根
据数据量的大小考虑按月分表或按年分表. 订单ID最好包含时间(如根据雪花算法生成), 此时既能根据订单ID直接获取到订单记录, 也能按照时间进行查询。
11. 雪花算法
雪花算法这一在分布式架构中很常见的,但一般也不需要怎么去深入了解,一方面一般个人项目用不到分布式之类的大型架构,另一方面,就算要用到,市面上很多ID生成器也帮我们完成了这项工作。
一、为何要用雪花算法
1、问题产生的背景
现如今越来越多的公司都在用分布式、微服务,那么对应的就会针对不同的服务进行数据库拆分,然
后当数据量上来的时候也会进行分表,那么随之而来的就是分表以后id的问题。
例如之前单体项目中一个表中的数据主键id都是自增的,Mysql是利用autoincrement来实现自增,
而oracle是利用序列来实现的,但是当单表数据量上来以后就要进行水平分表,阿里java开发建议是
单表大于500w的时候就要分表,但是具体还是得看业务,如果索引用的号的话,单表千万的数据也是
可以的。水平分表就是将一张表的数据分成多张表,那么问题就来了如果还是按照以前的自增来做主
键id,那么就会出现id重复,这个时候就得考虑用什么方案来解决分布式id的问题了。
2、解决方案
2.1、数据库表
可以在某个库中专门维护一张表,然后每次无论哪个表需要自增id的时候都去查这个表的记录,然后
用for update锁表,然后取到的值加一,然后返回以后把再把值记录到表中,但是这个方法适合并发
量比较小的项目,因此每次都得锁表。
2.2、redis
因为redis是单线程的,可以在redis中维护一个键值对,然后哪个表需要直接去redis中取值然后加
一,但是这个跟上面一样由于单线程都是对高并发的支持不高,只适合并发量小的项目。
2.3、uuid
可以使用uuid作为不重复主键id,但是uuid有个问题就是其是无序的字符串,如果使用uuid当做主
键,那么主键索引就会失效。
2.4、雪花算法
雪花算法是解决分布式id的一个高效的方案,大部分互联网公司都在使用雪花算法,当然还有公司自
己实现其他的方案。
二、雪花算法
1、原理
雪花算法就是使用64位long类型的数据存储id,最高位一位存储0或者1,0代表整数,1代表负数,一般都是0,所以最高位不变,41位存储毫秒级时间戳,10位存储机器码(包括5位datacenterId和5位workerId),12存储序列号。这样最大2的10次方的机器,也就是1024台机器,最多每毫秒每台机器产生2的12次方也就是4096个id。(下面有代码实现)
但是一般我们没有那么多台机器,所以我们也可以使用53位来存储id。为什么要用53位?
因为我们几乎都是跟web页面打交道,就需要跟js打交道,js支持最大的整型范围为53位,超过这个范围就会丢失精度,53之内可以直接由js读取,超过53位就需要转换成字符串才能保证js处理正确。53存储的话,32位存储秒级时间戳,5位存储机器码,16位存储序列化,这样每台机器每秒可以生产65536个不重复的id。
2、缺点
由于雪花算法严重依赖时间,所以当发生服务器时钟回拨的问题是会导致可能产生重复的id。当然几乎没有公司会修改服务器时间,修改以后会导致各种问题,公司宁愿新加一台服务器也不愿意修改服务器时间,但是不排除特殊情况。
如何解决时钟回拨的问题?
可以对序列化的初始值设置步长,每次触发时钟回拨事件,则其初始步长就加1w,可以在下面代码的第84行来实现,将sequence的初始值设置为10000。
三、代码实现
64位的代码实现:
/**
* Twitter_Snowflake<br>
* SnowFlake的结构如下(每部分用-分开):<br>
* 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000 <br>
* 1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0<br>
* 41位时间截(毫秒级),注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截)
* 得到的值),这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下下面程序IdWorker类的startTime属性)。41位的时间截,可以使用69年,年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69<br>
* 10位的数据机器位,可以部署在1024个节点,包括5位datacenterId和5位workerId<br>
* 12位序列,毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号<br>
* 加起来刚好64位,为一个Long型。<br>
* SnowFlake的优点是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分),并且效率较高,经测试,SnowFlake每秒能够产生26万ID左右。
*/
public class SnowflakeIdWorker {
// ==============================Fields===========================================
/** 开始时间截 (2020-01-01) */
private final long twepoch = 1577808000000L;
/** 机器id所占的位数 */
private final long workerIdBits = 5L;
/** 数据标识id所占的位数 */
private final long datacenterIdBits = 5L;
/** 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数) */
private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
/** 支持的最大数据标识id,结果是31 */
private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
/** 序列在id中占的位数 */
private final long sequenceBits = 12L;
/** 机器ID向左移12位 */
private final long workerIdShift = sequenceBits;
/** 数据标识id向左移17位(12+5) */
private final long datacenterIdShift = sequenceBits + workerIdBits;
/** 时间截向左移22位(5+5+12) */
private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
/** 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095) */
private final long sequenceMask = -1L ^ (-