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单个节点宕机,如何处理?还有其它方案实现分布式锁?