Mysql基础常考题
-
事务的原理,特性,事务并发控制
-
常用的字段, 含义和区别
-
常用的数据库引擎之间区别
什么是事务?
Transaction:
-
事务是数据库并发控制的基本单位
-
事务可以看作是一系列SQL语句的集合
-
事务必须要么全部执行成功,要么全部执行失败(回滚)
-
转账操作是事务使用的一个常见场景
#Transaction 示例
session.begin()
try:
item1 = session.query(Item).get(1)
item2 = session.query(Item).get(2)
item1.foo = 'bar'
item2.bar = 'foo'
session.commit()
except:
session.rollback()
raise
事务的ACID特性
ACID是事务的四个基本特性
-
原子性(Atomicity): 一个事务中所有操作全部完成或失败
-
一致性(Consistency): 事务开始和结束之后数据完整性没有被破坏
-
隔离性(Isolation): 允许多个事务同时对数据库修改和读写
-
持久性(Durability): 事务结束之后,修改是永久的不会丢失
事务的并发控制可能产生哪些问题
如果不对事务进行并发控制,可能会产生四种异常情况
-
幻读(phantom read): 一个事务第二次查出现第一次没有出现的结果
-
非重复读(nonrepeatable read): 一个事务重复读两次得到不同的结果
-
脏读(dirty read): 一个事务读取到另一个事务没有提交的修改
-
丢失修改(lost update): 并发写入造成其中一些修改丢失
四种事务隔离级别
为了解决并发控制异常,定义了4种事务隔离级别
-
读未提交(read uncommitted) 别的事务可以读取到未提交改变
-
读已提交(read committed): 只能读取已经提交的数据
-
可重复读(repeatable read): 同一个事务先后查询结果一样
Mysql InnoDB 默认实现可重复读级别
- 串行化(Serializable): 事务完全串行化的执行,隔离级别最高,执行效率最低
如何解决高并发场景下的插入重复
高并发场景下, 写入数据库会有数据重复问题
-
使用数据库的唯一索引
-
使用队列异步写入
-
使用redis等实现分布式锁
乐观锁和悲观锁
什么是乐观锁,什么是悲观锁
-
悲观锁是先获取锁再进行操作,一锁二查三更新 select for update
-
乐观锁先修改,更新的时候发现数据已经变了就回滚(check and set)
-
乐观锁一般通过版本号或者时间戳实现
-
需要根据响应速度,冲突频率,重试代价来判断使用那一种
Mysql常用数据类型-字符串(文本)
CHAR(Length) | length bytes | A fixed-length field from 0 to 255 characters long |
---|---|---|
VARCHAR(Length) | **String length + 1 or 2 bytes ** | A variable-length field from 0 to 65535 characters long |
tinytext | String length + 1 bytes | A string with a maximum length of 255 characters |
TEXT | String length + 2 bytes | A string with a maximum length of 65535 characters |
Mysql常用数据类型-数值
TINYINT(Length) | 1 byte | Range of -128 to 127 or 0 to 255 unsigned |
SMALLINT(Length) | 2 bytes | Range of -32768 to 32767 or 0 to 65535 unsiged |
MEDIUMINT(Length) | 3 bytes | Range of -8388608 to 8388607 or 0 to 16777215 unsigned |
INT(Length) | 4 bytes | Range of -2147483648 to 2147483647 or 0 to 4294967295 |
BIGINT(Length) | 8 bytes | Range of -9223372036854775808 to 9223372036854775807 or 0 to 18446744073709551615 unsigned |
FLOAT(Length, Decimals) | 4 bytes | A small number with a floating decimal point |
DOUBLE(Length, Decimals) | 8 bytes | A large number with a floating decimal point |
Mysql常用数据类型-日期和时间
DATE | 3 bytes | ln the format of YYYY-MM-DD |
---|---|---|
DATETIME | 8 bytes | ln the format of YYYY-MM-DD HH:MM:SS |
TIMESTAMP | 4 bytes | In the format of YYYYMMDDHHMMSS; acceptable range starts in 1970 and ends in the year 2018 |
InnoDB vs MyISAM
两种引擎常见的区别
-
InnoDB支持事务
-
InnoDB支持外键
-
InnoDB支持行锁和表锁 MyISAM只支持表锁
Mysql索引原理及优化常见考题
Mysql索引
-
索引的原理,类型,结构
-
创建索引的注意事项,使用原则
-
如何排查和消除慢查询
什么是索引?
为什么需要索引?
-
索引是数据表中一个或者多个列进行排序的数据结构
-
索引能够大幅提升检索速度
-
创建,更新索引本身也会耗费空间和时间
什么是B-Tree?
查找结构进化史
-
1> 线性查找: 实现简单,太慢
-
2> 二分查找: 有序,简单,要求是有序的,插入特别慢
-
3> HASH: 查询快,占用空间,不太适合存储大规模数据
-
4> 二叉查找树: 插入和查询快(log(n)), 无法存大规模数据,复杂度退化
-
5> 平衡树: 解决bst退化的问题,树是平衡的,节点非常多的时候,依然树高很高
-
6> 多路查找树: 一个父亲多个孩子节点(度) 节点过多 树高不会特别深
-
7> 多路平衡查找树: B-Tree
什么是B-Tree,为什么要使用B-Tree
-
多路平衡查找树(每个节点最多m(m >= 2))个孩子,称为m阶或者度)
-
叶节点具有相同的深度
-
节点中的数据key从左到右是递增的
B+Tree
B+树是B-Tree的变形
-
Mysql实际使用的B+Tree作为索引的数据结构
-
只在叶子节点带有指向记录的指针(为什么? 可以增加树的度)
-
叶子节点通过指针相连,为什么?实现范围查询
是不是树的度越多越好?
Mysql索引的类型
Mysql创建索引类型
-
普通索引(CREATE INDEX)
-
唯一索引,索引列的值必须唯一(CREATE UNIQUE INDEX)
-
多列索引
-
主键索引(PRIMARY KEY) 一个表只能有一个
-
全文索引(FULLTEXT INDEX), InnoDB不支持
什么时候创建索引?
建表的时候需要根据查询需求来创建索引
-
经常用作查询条件的字段(WHERE条件)
-
经常用作表连接的字段
-
经常出现在order by, group by之后的字段
创建索引有哪些需要注意的?
最佳实践
-
非空字段 NOT NULL, Mysql很难对空值作查询优化
-
建表规范要求索引字段有默认值
-
区分度高, 离散度大,作为索引的字段尽量不要有大量相同值
-
索引的长度不要太长(比较耗费时间)
索引什么时候失效?
记忆口诀:模糊匹配,类型隐转,最左匹配
-
以 % 开头的LIKE语句,模糊搜索
-
出现隐式类型转换(在py这种动态语言查询中需要注意)
-
没有满足最左前缀原则(为什么是左配)
什么是聚集索引和非聚集索引?
聚集索引和辅助索引
聚焦索引
非聚集和聚集索引的文件存储方式
-
聚集还是非聚集指的是B+Tree叶节点存的是指针还是数据记录
-
MyISAM索引和数据分离,使用的是非聚集索引
-
InnoDB数据文件就是索引文件, 主键索引就是聚集索引
-
辅助索引先找到主键以后再根据主键找到数据
如何排查慢查询
通常缺少索引,索引不合理或者业务代码实现导致
-
slow_query_log_file 开启并且查询慢查询日志
-
通过explain排查索引问题
-
调整数据修改索引,业务代码层限制不合理访问
总结
-
索引的原理
-
B + Tree的结构
-
不同索引的区别
SQL语句编写常考题
考点聚焦
SQL语句以考察各种常用连接为重点
-
内连接(INNER JOIN): 两个表都存在匹配时,才会返回匹配行
-
外连接(LEFT/RIGHT JOIN): 返回一个表的行,即使另一个没有匹配
-
全连接(FULL JOIN): 只要某一表存在匹配就返回
内连接
INNER JOIN
-
将左表和右表能够关联起来的数据连接后返回
-
类似于求两个表的"交集"
- select * from A inner join B on a.id = b.id
示例表
id | value |
---|---|
1 | ab |
2 | a |
id | value |
---|---|
1 | ab |
3 | b |
外连接
外连接包含左连接和右连接
-
左连接返回左表中所有记录,即使右表中没有匹配的记录
-
右连接返回右表中所有记录,即使左表中没有匹配的记录
-
没有匹配的字段会设置成NULL
缓存及Redis常考面试题
缓存的使用场景,Redis的使用,缓存使用的坑
为什么使用缓存?使用场景?
Redis的常用数据类型, 使用方式
缓存使用问题,数据一致性问题,缓存穿透,击穿,雪崩问题
主要讨论内存缓存(Redis/Memchached)
-
解关系数据库(常见的是Mysql)并发访问的压力,热点数据
-
减少响应时间:内存IO速度比磁盘快
-
提升吞吐量:Redis等内存数据库单机就可以支撑很大并发
操作时间对比
操作 | 响应时间 |
---|---|
打开一个网站 | 几秒 |
在数据库中查询一条记录(有索引) | 十几毫秒 |
机械磁盘一次寻址定位 | 4毫秒 |
从机械磁盘顺序读取1MB数据 | 2毫秒 |
从SSD磁盘顺序读取1MB数据 | 0.3毫秒 |
从远程分布式缓存Redis读取一个数据 | 0.5毫秒 |
从内存中读取1MB数据 | 十几微秒 |
Java程序本地方法调用 | 几微秒 |
网络传输2KB数据 | 1微秒 |
Redis/Memcached主要区别?
对比参数 | Redis | Memcached |
---|---|---|
类型 | 1.支持内存 2.非关系型数据库 | 1.支持内存 2.key-value键值对形式 3.缓存系统 |
数据存储类型 | 1.String 2.List 3. Set 4.Hash 5.Sort Set(ZSet) | 1.文本型 2. 二进制类型[新版增加] |
查询[操作]类型 | 1.批量操作 2. 事务支持[假的事务] 3.每个类型不同的CRUD | 1.CRUD 2.少量的其它命令 |
附加功能 | 1. 发布/订阅模式 2. 主从分区 3. 序列化支持 4.脚本支持[Lua脚本] | 1. 多线程服务支持 |
网路IO模型 | 1. 单进程模式 | 1. 多线程,非阻塞IO模式 |
事件库 | 自封装简易事件库AeEvent | 贵族血统的LibEvent事件库 |
持久化支持 | 1. RDB 2. AOF | 不支持 |
Mysql + Redis就能抗很多业务
请简述Redis常用数据类型和使用场景?
考察对Redis使用的掌握程度
-
String(字符串):用来实现简单的KV键值对存储,eg:计数器
-
List(链表): 实现双向链表,eg:用户的关注,粉丝列表
-
Hash(哈希表): 用来存储彼此相关的信息的键值对
- HSET key field value
-
Set(集合): 存储不重复元素,比如用户的关注者
-
Sorted Set(有序集合): 实时信息排行榜
延伸考点:Redis内置实现
需要了解Redis各种类型的C底层实现方式
-
String: 整数或者sds(Simple Dynamic String)
-
List: ziplist or double linked list
-
ziplist: 通过一个连续的内存块实现list结构,
其中的每个entry节点头部保存前后节点长度信息,实现双向链表功能
-
Hash: ziplist or hashtable
-
set: inset or hashtable
-
SortedSet: skiplist 跳跃表
reference book
Redis 设计与实现
这些数据结构操作的时间和空间复杂度?
Redis实现的跳跃表示什么结构?
- Sorted Set为了简化实现,使用skiplist而不是平衡树实现?
Redis有哪些持久化方式?
Redis支持两种方式实现持久化
-
快照方式:把数据快照放在磁盘二进制文件中,dump.rdb
快照的实现方式是指定时间间隔把Redis数据库状态保存到一个压缩的二进制文件中
- AOF(Append Only File): 每一个写命令追加到appendonly.aof中 可以通过修改redis配置实现
什么是Redis事务?
和mysql的事务有什么不同?
-
将多个请求打包,一次性,按序执行多个命令的机制
-
Redis通过MULTI, EXEC, WATCH等命令实现事务功能
-
Python redis-py pipeline=conn.pipeline(transaction=True)
Redis如何实现分布式锁?
-
使用setnx实现加锁,可以同时通过expire添加超时时间
-
锁的value值可以使用一个随机的uuid或者特定的命名
-
释放锁的时候,通过uuid判断是否是该锁,是则执行delete释放锁
使用缓存的模式?
-
Cache Aside:同时更新缓存和数据库
-
Read/Write Through: 先更新缓存,缓存负责同步更新数据库
-
Write Behind Caching:先更新缓存,缓存定期异步更新数据库一致性问题
如何解决缓存穿透问题?
大量查询不到的数据的请求落到后端数据库,数据库压力增大
由于大量缓存查不到就去数据库取,数据库也没有要查的数据
- 解决:对于没查到返回为None的数据也缓存, 插入数据的时候删除相应缓存,或者设置较短的超时时间
如何解决缓存击穿问题?
某些非常热点的数据key过期,大量请求打到后端数据库
热点数据key 失效导致大量请求打到数据库增加数据库压力
-
分布式锁:获取锁的线程从数据库拉数据更新缓存,其它线程等待
-
异步后台更新:后台任务对过期的key自动刷新
如何解决缓存雪崩问题?
缓存不可用或者大量缓存key同时失效,大量请求直接打到数据库
-
多级缓存:不同级别的key设置不同的超时时间
-
随机超时:key的超时时间随机设置,防止同时超时
-
架构层: 提升系统可用性,监控,报警完善
Mysql与Redis思考题?
索引的理解
为什么Mysql数据库的主键使用自增的整数比较好?
使用uuid可以吗?为什么?
如果是分布式系统下怎么生成数据库的自增id呢?
Redis应用-分布式锁
redis的应用之一:实现分布式锁
思考题:
请基于redis编写代码实现一个简单的分布式锁
要求:支持超时时间参数
扩展:如果redis单个节点宕机,如何处理?还有其它方案实现分布式锁?