GoGoGo,出发咯!
一、统计 7 日内打卡次数最多的前 10 个人的姓名的 SQL
SELECT name, COUNT(*) as count
FROM sign
WHERE ctime >= UNIX_TIMESTAMP(NOW() - INTERVAL 7 DAY)
GROUP BY name
ORDER BY count DESC
LIMIT 10;
二、加入条件:打卡次数超过 100 的 SQL
SELECT name, COUNT(*) as count
FROM sign
WHERE ctime >= UNIX_TIMESTAMP(NOW() - INTERVAL 7 DAY)
GROUP BY name
HAVING count > 100
ORDER BY count DESC
LIMIT 10;
三、MySQL 查询语句的执行过程
MySQL 查询语句的执行过程一般可以分为以下几个步骤:
1. 解析:
- SQL 语句被解析成语法树,检查语法错误。
2. 优化:
- 查询优化器会生成多种执行计划,并选择成本最低的执行计划。
- 使用统计信息(如索引、表大小等)来评估不同执行计划的成本。
3. 执行:
- 根据优化后的执行计划,数据库引擎开始执行查询。
- 访问表、执行 JOIN 操作、应用 WHERE 条件等。
4. 返回结果:
- 将查询结果返回给客户端。
四、回表
1. 定义
- 回表是指在使用索引查询时,如果需要获取的字段不在索引中,数据库会根据索引中的主键或唯一索引回到原始数据表中获取所需的字段。这种操作通常会导致额外的 I/O 开销。
2. 示例
- 假设有一个索引只包含 id 和 name 字段,而你查询的 SQL 语句需要 id, name, age,那么数据库会先通过索引找到 id,然后再回表查询 age 字段。
五、用 Redis 实现一个简易的延迟队列
可以使用 Redis 的有序集合(Sorted Set)来实现延迟队列。以下是基本步骤:
1. 添加任务
- 使用当前时间加上延迟时间作为分数,将任务加入有序集合。
redis.ZAdd("delay_queue", redis.Z{
Score: float64(time.Now().UnixNano()/1e6) + 180000, // 当前时间 + 3分钟
Member: "order_id_123",
})
2. 处理任务
- 定期检查有序集合中是否有到期的任务,并处理它们。
now := float64(time.Now().UnixNano() / 1e6)
tasks := redis.ZRangeByScore("delay_queue", 0, now)
for _, task := range tasks {
// 处理任务,比如回滚库存
// ...
// 删除已处理的任务
redis.ZRem("delay_queue", task)
}
六、用 Redis 实现一个简单的分布式锁
0. 使用 Redis 来做锁的考虑
Redis 可以用作分布式锁的实现,主要是因为其支持原子性操作和高性能。使用 Redis 实现分布式锁的好处包括:
- 高可用性:Redis 提供高可用的分布式环境。
- 简易性:通过简单的命令(如 SETNX)实现锁的获取与释放。
- 过期机制:可以设置过期时间,避免死锁。
可以通过设置一个带过期时间的键来实现分布式锁。以下是基本步骤:
1. 获取锁
- 尝试设置一个键,如果成功则获得锁。
ok := redis.SetNX("lock_key", "lock_value", 10*time.Second) // 设置10秒过期
if !ok {
// 获取锁失败
}
2. 释放锁
- 释放锁时,确保只有持有锁的客户端才能释放它。
// 需要确保当前客户端是锁的持有者
if redis.Get("lock_key") == "lock_value" {
redis.Del("lock_key") // 释放锁
}
七、Redis 如果有大 key,删除的时候会有什么问题?
删除大 key 时可能会导致以下问题:
- 阻塞:删除大 key 会消耗较多的 CPU 和内存资源,可能会导致 Redis 实例阻塞,影响性能。
- 内存消耗:Redis 在删除大 key 时,可能会使用较多的内存,导致其他操作变慢。
解决方案:
- 使用 UNLINK 命令异步删除大 key。
- 将大 key 拆分为多个小 key。
八、Redis 如何保证多条既有写又有读命令的数据一致性?
Redis 通过以下机制保证数据一致性:
- 单线程模型:Redis 使用单线程处理请求,避免了并发读写冲突。
- 事务:使用 MULTI、EXEC、WATCH 等命令实现事务,确保一组操作的原子性。
示例:
redis.Multi() // 开始事务
redis.Set("key1", "value1")
redis.Set("key2", "value2")
redis.Exec() // 提交事务
九、假设又有读,又有删除的命令,如何保证不会发生数据异常?
Redis 通过以下方式保证数据一致性:
- 乐观锁:使用 WATCH 命令监视某个键,确保在执行写操作前,数据没有被其他客户端修改。
- 事务:在事务中执行读和写操作,确保操作的原子性。
示例:
redis.WATCH("key")
value := redis.Get("key")
if value != nil {
// 执行删除操作
redis.Multi() // 开启事务
redis.Del("key")
redis.Exec() // 提交事务
}
十、如何使用 Redis 实现一个限流器?
可以使用 Redis 的 INCR 命令和过期时间来实现简单的限流器。
1. 请求处理:
- 每次请求时,使用 INCR 命令增加计数器。
count := redis.Incr("rate_limit_key")
if count == 1 {
redis.Expire("rate_limit_key", 1*time.Minute) // 设置过期时间
}
2. 检查限流:
- 如果计数器超过限制,则拒绝请求。
if count > limit {
// 拒绝请求
}
十一、如何防止死锁发生?
防止死锁的方法包括:
- 资源顺序分配:确保所有进程按照相同的顺序请求资源。
- 超时机制:设置请求资源的超时时间,超过时间则放弃请求。
- 避免持有锁等待:在持有锁时避免请求其他锁,尽量减少锁的持有时间。
十二、缓存击穿、缓存穿透、缓存雪崩的区别
- 缓存击穿:某个热点数据在缓存中不存在,导致大量请求直接访问数据库,造成数据库压力过大。
- 缓存穿透:请求的数据在缓存和数据库中都不存在,导致每次请求都直接访问数据库,造成数据库压力。
- 缓存雪崩:大量缓存同时过期,导致瞬间大量请求访问数据库,造成数据库压力。
十三、InnoDB 中聚集索引和非聚集索引的区别
1. 聚集索引:
- 数据行的物理存储顺序与索引的顺序相同。
- 每个表只能有一个聚集索引,通常是主键。
- 查询效率高,因为可以直接通过索引找到数据。
2. 非聚集索引:
- 数据行的物理存储顺序与索引的顺序无关。
- 每个表可以有多个非聚集索引。
- 查询时需要通过非聚集索引查找数据,再通过主键回表。
十四、什么是乐观锁和悲观锁?
1. 乐观锁
- 假设不会发生冲突,操作时不加锁,而是在提交时检查数据是否被其他事务修改。
- 常用版本号或时间戳来实现。
2. 悲观锁
- 假设会发生冲突,操作时直接加锁,其他事务必须等待锁释放才能继续执行。
- 通常使用数据库的锁机制(如 SELECT … FOR UPDATE)。
十五、MySQL 索引失效的场景
以下是一些常见的 MySQL 索引失效场景:
1. 使用函数或运算符:在 WHERE 子句中使用函数或运算符会导致索引失效。
WHERE YEAR(date_column) = 2023
2. 类型不匹配:查询条件的类型与索引字段类型不匹配。
WHERE int_column = 'string_value'
3. 模糊查询:在 LIKE 查询中,如果前缀是通配符,索引将失效。
WHERE column LIKE '%value%'
4. NULL 值:索引字段中包含 NULL 值,查询条件中使用 IS NULL 可能导致索引失效。
5. 不等于查询:使用 != 或 < 等条件可能导致索引失效。
十六、MySQL 主从复制的过程
MySQL 主从复制的基本过程如下:
1. 主服务器:
- 主服务器记录所有的写操作到二进制日志(binlog)。
2. 从服务器:
- 从服务器连接到主服务器,获取主服务器的 binlog。
- 从服务器将 binlog 中的事件应用到自己的数据库中。
3. 同步:
- 从服务器通过 IO 线程获取 binlog,通过 SQL 线程执行事件,保持与主服务器的数据一致性。
十七、MySQL 在实际中遇到的性能优化问题
常见的 MySQL 性能优化问题包括:
- 慢查询:未优化的查询导致性能下降,可以使用 EXPLAIN 语句分析查询计划。
- 索引失效:不合理的索引使用导致查询效率低下。
- 锁竞争:高并发情况下,锁竞争导致性能瓶颈。
- 数据冗余:过多的数据冗余导致存储和查询的开销增加。
十八、重跑任务设计:全量数据还是增量数据
在设计重跑任务时,通常可以选择以下两种方式:
- 全量数据:每次处理所有数据,适合数据量不大或有完整数据恢复需求的场景。
- 增量数据:只处理自上次成功处理以来的新数据,适合数据量大且实时性要求高的场景。
推荐
- 对于大规模数据,建议使用增量数据处理,以减少资源消耗和提高效率。
十九、Redis 的 Key 的底层实现
Redis 使用字典(hash table)来实现键值存储:
- 每个键通过哈希函数计算出一个哈希值,映射到一个桶中。
- 通过链表或跳表处理哈希冲突。
- Redis 的字典是动态扩展的,以提高查找效率。
二十、Redis 的过期策略
Redis 支持多种过期策略:
- 定时删除:每次访问数据库时检查过期的键。
- 惰性删除:在访问键时,如果发现键已过期,则删除该键。
- 定期删除:周期性地随机检查一部分键,删除过期的键。