面试准备-技术【面试准备】
- Mysql
- Mysql常见面试题
- Mysql索引
- Mysql事务
- Mysql日志
- Mysql锁
- Mysql MVCC
- Mysql中的Buffer
- 数据库的三范式是什么
- MySQL对于LRU的优化
- InnoDB三大特性
- 自适应哈希索引(Adaptive Hash Index)
- 插入缓存(insert buffer) 或写缓存(Change Buffer)
- 两次写(double write)
- Mysql8.0索引新特性
- 支持降序索引
- 支持隐藏索引
- 数据库服务器优化步骤
- 1.优化步骤
- 2.查询系统参数
- 3.查询执行成本 last_query_cost,单位是需要查询的页的数量
- 4.定位执行慢的 SQL:慢查询日志 long_query_time
- 5.查看 SQL 执行成本:SHOW PROFILE
- 6.分析查询语句:EXPLAIN
- 8. 分析优化器执行计划:trace
- 9.MySQL监控分析视图-sys schema
- 索引优化
- 锁
- 再谈二进制日志(binlog)
- 主从复制
- Mysql8的新特性
- Redis
- redis为什么快
- 什么是bigkey?会有什么影响?
- Redis的数据结构
- hash的渐进式resize
- zset的使用
- zset为什么使用跳表
- redis的事务
- 使用redis如何设计分布式锁?
- redis的持久化
- 如何保证缓存与数据库双写时的数据一致性
- Redis常见性能问题和解决方案有哪些?
- Redis过期策略都有哪些?LRU算法知道吗?
- redis的集群
- Spring
- IOC
- AOP
- 循环依赖
- springboot如果要对属性文件中的账号密码加密如何实现?
- SpringBoot的优点
- Spring Boot 的核心注解是哪个?它主要由哪几个注解组成的?
- Kafka
- 为什么使用kafka
- kafka为什么这么快
- kafka如何保证高可用,一致性,有序性
2024-3-15 16:18:20
公开发布于
2024-5-24 13:25:42
Mysql
Mysql常见面试题
MySQL面试50题【mysql】
导航【mysql高级】【java提高】
mysql复习【面试】
mysql笔记【面试】
MySQL笔记【面试】
Mysql索引
物理实现:B+树和Hash 类型:聚簇索引和非聚簇索引
最左前缀法则
底层的B+树结构
当一个列的左列的值全部相等时,该列才有序
Mysql事务
事务:就是一组操作要么同时成功,要么同时失败
ACID四个特性
原子性:一组操作同时成功,同时失败
保证:redo日志
一致性:从一个一致性状态到达下一个一致性状态
保证:
约束一致性:创建表结构时所指定的外键、唯一索引等约束。
数据一致性:其他三个保证,一般都是程序员进行处理的
隔离性:两个事务不会互相影响
保证:MVCC和锁
脏写 脏读 可重复读 幻读
持久性:持久化到磁盘上
保证:undo日志
Mysql日志
redo log:重做日志
undo log:回滚日志
binlog:二进制日志
relay log:中继日志
Mysql锁
第15章 锁:3. 锁的不同角度分类:3.2 从数据操作的粒度划分:表级锁、页级锁、行锁
表锁:
① 表级别的S锁、X锁
InnoDB不会对其主动加该锁
LOCK TABLES t READ | LOCK TABLES t WRITE
② 意向锁 (intention lock)
多粒度:加行锁之前先加意向锁,标志位的思想
③ 自增锁(AUTO-INC锁)
插入自增主键的时候,不需赋值
④ 元数据锁(MDL锁)
当对一个表做增删改查操作的时候,加MDL读锁;当要对表做结构变更操作的时候,加MDL写锁。
行锁:
① 记录锁(Record Locks)
记录锁也就是仅仅把一条记录锁上,官方的类型名称为: LOCK_REC_NOT_GAP 。
② 间隙锁(Gap Locks)
gap锁的提出仅仅是为了防止插入幻影记录而提出的
③ 临键锁(Next-Key Locks)
记录锁+间隙锁
Next-Key Locks是在存储引擎 innodb 、事务级别在 可重复读 的情况下使用的数据库锁,
innodb默认的锁就是Next-Key locks。
④ 插入意向锁(Insert Intention Locks)
插入意向锁是在插入一条记录行前,由 INSERT 操作产生的一种间隙锁
事实上插入意向锁并不会阻止别的事务继续获取该记录上任何类型的锁。
死锁
元数据锁导致死锁
间隙锁导致死锁
互相申请对方所占有的资源导致死锁
where列没加索引,导致全表扫描,进而锁住全表
①超时等待,但是阈值时间不好确定
②死锁检测
死锁检测的原理是构建一个以事务为顶点、锁为边的有向图,判断有向图是否存在环,存在即有死锁
Mysql MVCC
第16章 多版本并发控制【3.事务篇】【MySQL高级】
MVCC (Multiversion Concurrency Control),多版本并发控制。顾名思义,MVCC是通过数据行的多个版本管理来实现数据库的并发控制。这项技术使得在InnoDB的事务隔离级别下执行一致性读操作有了保证。换言之,就是为了查询一些正在被另一个事务更新的行,并且可以看到它们被更新之前的值,这样在做查询的时候就不用等待另一个事务释放锁。
MVCC在MySQL InnoDB中的实现主要是为了提高数据库并发性能,用更好的方式去处理读-写冲突,做到即使有读写冲突时,也能做到不加锁,非阻塞并发读,而这个读指的就是快照读,而非当前读。当前读实际上是一种加锁的操作,是悲观锁的实现。而MVCC本质是采用乐观锁思想的一种方式。
快照读(一致性读)和 当前读(最新)
MVCC 的实现依赖于:隐藏字段、Undo Log、Read View
隐藏字段:row_id、tx_id、roll_pointer
undo log版本链
Read View
ReadView就是事务A在使用MVCC机制进行快照读操作时产生的读视图。当事务启动时,会生成数据库系统当前的一个快照,InnoDB为每个事务构造了一个数组,用来记录并维护系统当前活跃事务的ID(“活跃"指的就是,启动了但还没提交)
READ COMMITTED :每次读取数据前都生成一个ReadView
REPEATABLE READ 隔离级别的事务来说,只会在第一次执行查询语句时生成一个 ReadView ,之后的查询就不会重复生成了。
Mysql中的Buffer
总结 mysql 的所有 buffer,一网打尽就这篇了!
Buffer Pool
因为不想每次修改,就得把数据页持久到磁盘中
所以设置Buffer Pool
只要适用于普通非唯一索引,因为没有唯一性约束
所以对其的修改,不需要判断
先修改到Buffer Pool中,直到它调入内存中,再进行合并
change buffer
对于非唯一索引的一个优化
log buffer
redo log的分为内存中的redo log buffer
和文件中的redo log
doublewrite buffer
因为Mysql中的页大小和操作系统的页大小不一致
数据库的三范式是什么
/* 建表规范 */ ------------------
-- Normal Format, NF
- 每个表保存一个实体信息
- 每个具有一个ID字段作为主键
- ID主键 + 原子表
-- 1NF, 第一范式
字段不能再分,就满足第一范式。
-- 2NF, 第二范式
满足第一范式的前提下,不能出现部分依赖。
消除复合主键就可以避免部分依赖。增加单列关键字。
只能完全依赖主键,而不能依赖主键的一部分
-- 3NF, 第三范式
满足第二范式的前提下,不能出现传递依赖。
某个字段依赖于主键,而有其他字段依赖于该字段。这就是传递依赖。
将一个实体信息的数据放在一个表内实现。
只能直接依赖于主键,不能依赖于其他属性
反范式化
MySQL对于LRU的优化
普通LRU算法
LRU = Least Recently Used(最近最少使用): 就是末尾淘汰法,新数据从链表头部加入,释放空间时从末尾淘汰.
- 当要访问某个页时,如果不在Buffer Pool,需要把该页加载到缓冲池,并且把该缓冲页对应的控制块作为节点添加到LRU链表的头部。
- 当要访问某个页时,如果在Buffer Pool中,则直接把该页对应的控制块移动到LRU链表的头部
- 当需要释放空间时,从最末尾淘汰
普通LRU链表的优缺点
优点
- 所有最近使用的数据都在链表表头,最近未使用的数据都在链表表尾,保证热数据能最快被获取到。
缺点
- 如果发生全表扫描(比如:没有建立合适的索引 or 查询时使用select * 等),则有很大可能将真正的热数据淘汰掉.
- 由于MySQL中存在预读机制,很多预读的页都会被放到LRU链表的表头。如果这些预读的页都没有用到的话,这样,会导致很多尾部的缓冲页很快就会被淘汰。
改进型LRU算法
改进型LRU:将链表分为new和old两个部分,加入元素时并不是从表头插入,而是从中间midpoint位置插入(就是说从磁盘中新读出的数据会放在冷数据区的头部),如果数据很快被访问,那么page就会向new列表头部移动,如果数据没有被访问,会逐步向old尾部移动,等待淘汰。
冷数据区的数据页什么时候会被转到到热数据区呢 ?
- 如果该数据页在LRU链表中存在时间超过1s,就将其移动到链表头部 ( 链表指的是整个LRU链表)
- 如果该数据页在LRU链表中存在的时间短于1s,其位置不变(由于全表扫描有一个特点,就是它对某个页的频繁访问总耗时会很短)
- 1s这个时间是由参数
innodb_old_blocks_time
控制的
InnoDB三大特性
【MySQL】存储引擎(二):InnoDB 内存结构
自适应哈希索引(Adaptive Hash Index)
当一个数据频繁的搜索时,将为其创建hash
那什么情况下才会使用自适应Hash索引呢?如果某个数据经常会访问到,当满足一定条件的时候,就会将这个数据页的地址存放到Hash表中。这样下次查询的时候,就可以直接找到这个页面的所在位置。
需要说明的是:
1)自适应哈希索引只保存热数据(经常被使用到的数据),并非全表数据。因此数据量并不会很大,可以让自适应Hash放到缓冲池中,也就是InnoDB buffer pool,进一步提升查找效率。
2)InnoDB中的自适应Hash相当于是“索引的索引”,采用Hash索引存储的是B+树索引中的页面的地址。这也就是为什么可以称自适应Hash为索引的索引。 采用自适应Hash索引目的是可以根据SQL的查询条件加速定位到叶子节点,特别是当B+树比较深的时候,通过自适应Hash索引可以提高数据的检索效率。
3)自适应Hash采用Hash函数映射到一个哈希表中,所以对于字典类型的数据查找非常方便 哈希表是数组+链表的形式。通过Hash函数可以计算索引键值所对应的bucket(桶)的位置,如果产生Hash冲突,如果产生哈希冲突,就需要遍历链表来解决。
4)是否开启了自适应Hash,可以通过innodb_adaptive_hash_index变量来查看,比如:mysql> show variables like '%adaptive_hash_index';
所以,总结下InnoDB本身不支持Hash,但是提供自适应Hash索引,不需要用户来操作,而是存储引擎自动完成的。
插入缓存(insert buffer) 或写缓存(Change Buffer)
因为要减少磁盘IO,所以加入Buffer Pool,
InnoDB使用了一种缓冲池的技术,也就是把磁盘读到的页放到一块内存区域里面。这个内存区域就叫Buffer Pool。
缓冲的加入一般都是内存和磁盘的处理速度不匹配
思考一个问题:当需要更新一个数据页时,如果数据页在Buffe rPool中存在,那么就直接更新好了。否则的话就需要从磁盘加载到内存,再对内存的数据页进行操作。也就是说,如果没有命中缓冲池,至少要产生一次磁盘IO,有没有优化的方式呢?向下看…
对于非唯一索引,因为其不要把索引页调入内存,进行唯一性检查
所以可以先写到insert buffer中,
当其数据页调入到内存中,再进行合并数据,merge操作
Change Buffer 默认占 Buffer Pool的25%
两次写(double write)
Mysql数据页大小是16K,操作系统的页大小为4K(1K)
要刷脏页的时候,
先写到共享表空间,顺序的
再写到各自的独立表空间,分散的
Mysql8.0索引新特性
第08章 索引的创建与设计原则【2.索引及调优篇】【MySQL高级】:2. Mysql8.0索引新特性
支持降序索引
CREATE TABLE ts1(a int,b int,index idx_a_b(a asc,b desc));
支持隐藏索引
为了避免删除索引之后,又想加回来
所有有了隐藏索引
先隐藏索引,之后看起影响,考虑是否删除索引
1.创建表时直接创建
CREATE TABLE tablename(
propname1 type1[CONSTRAINT1],
propname2 type2[CONSTRAINT2],
……
propnamen typen,
INDEX [indexname](propname1 [(length)]) INVISIBLE
);
2.在已有表上创建
CREATE INDEX indexname
ON tablename(propname[(length)]) INVISIBLE;
3.修改ALTER TABLE语句创建
ALTER TABLE tablename
ADD INDEX indexname (propname [(length)]) INVISIBLE;
4.切换可视状态:
ALTER TABLE tablename ALTER INDEX index_name INVISIBLE; #切换成隐藏索引
ALTER TABLE tablename ALTER INDEX index_name VISIBLE; #切换成非隐藏索引
5.使隐藏索引对查询优化器可见
set session optimizer_switch="use_invisible_indexes=on";
set session optimizer_switch="use_invisible_indexes=off";
数据库服务器优化步骤
第09章 性能分析工具的使用【2.索引及调优篇】【MySQL高级】
SQL调优的三个步骤:慢查询、EXPLAIN和SHOW PROFILING
1.优化步骤
2.查询系统参数
3.查询执行成本 last_query_cost,单位是需要查询的页的数量
4.定位执行慢的 SQL:慢查询日志 long_query_time
4.1开启慢查询日志参数
1.开启slow_query_log
set global slow_query_log='ON';
再来查看下慢查询日志是否开启,以及慢查询日志文件的位置:
show variables like '%slow_query_log_file';
如果不指定存储路径,慢查询日志将默认存储到MySQL数据库的数据文件夹下。如果不指定文件名,默认文件名为hostname-slow.log
2.修改long_query_time阈值
set global long_query_time = 1;
4.2 查看慢查询数目
SHOW GLOBAL STATUS LIKE '%Slow_queries%';
有多少条
补充:是否慢查询sql还有个参数
min_examined_row_limit(查询扫描过的最少记录数),默认是0
4.5 慢查询日志分析工具:mysqldumpslow
#得到返回记录集最多的10个SQL
mysqldumpslow -s r -t 10 /var/lib/mysql/atguigu-slow.log
#得到访问次数最多的10个SQL
mysqldumpslow -s c -t 10 /var/lib/mysql/atguigu-slow.log
#得到按照时间排序的前10条里面含有左连接的查询语句
mysqldumpslow -s t -t 10 -g "left join" /var/lib/mysql/atguigu-slow.log
#另外建议在使用这些命令时结合 | 和more 使用 ,否则有可能出现爆屏情况
mysqldumpslow -s r -t 10 /var/lib/mysql/atguigu-slow.log | more
4.6 关闭慢查询日志
4.7 删除慢查询日志
5.查看 SQL 执行成本:SHOW PROFILE
6.分析查询语句:EXPLAIN
8. 分析优化器执行计划:trace
select * from information_schema.optimizer_trace\G
9.MySQL监控分析视图-sys schema
索引优化
防止索引失效
全值匹配
最左前缀法则
索引覆盖
索引条件下推
锁
间隙锁加锁规则(共11个案例)
间隙锁是在可重复读隔离级别下才会生效的: next-key lock 实际上是由间隙锁加行锁实现的,如果切换到读提交隔离级别 (read-committed) 的话,就好理解了,过程中去掉间隙锁的部分,也就是只剩下行锁的部分。而在读提交隔离级别下间隙锁就没有了,为了解决可能出现的数据和日志不一致问题,需要把binlog 格式设置为 row 。也就是说,许多公司的配置为:读提交隔离级别加 binlog_format=row。业务不需要可重复读的保证,这样考虑到读提交下操作数据的锁范围更小(没有间隙锁),这个选择是合理的。
next-key lock的加锁规则
总结的加锁规则里面,包含了两个 “ “ 原则 ” ” 、两个 “ “ 优化 ” ” 和一个 “bug” 。
- 原则 1 :加锁的基本单位是 next-key lock 。 next-key lock 是前开后闭区间。
- 原则 2 :查找过程中访问到的对象才会加锁。任何辅助索引上的锁,或者非索引列上的锁,最终都要回溯到主键上,在主键上也要加一把锁。
- 优化 1 :索引上的等值查询,给唯一索引加锁的时候, next-key lock 退化为行锁。也就是说如果InnoDB扫描的是一个主键、或是一个唯一索引的话,那InnoDB只会采用行锁方式来加锁
- 优化 2 :索引上(不一定是唯一索引)的等值查询,向右遍历时且最后一个值不满足等值条件的时候, next-keylock 退化为间隙锁。
- 一个 bug :唯一索引上的范围查询会访问到不满足条件的第一个值为止。
再谈二进制日志(binlog)
第17章 其他数据库日志:6. 再谈二进制日志(binlog)
两阶段提交
为了解决两份日志之间的逻辑一致问题,InnoDB存储引擎使用两阶段提交方案。原理很简单,将redo log的写入拆成了两个步骤prepare和commit,这就是两阶段提交。
还有缓存与数据库的一致性
mq订阅binlog
主从复制
2.1 原理剖析
三个线程
实际上主从同步的原理就是基于 binlog 进行数据同步的。在主从复制过程中,会基于 3 个线程 来操作,一个主库线程,两个从库线程。
二进制日志转储线程 (Binlog dump thread)是一个主库线程。当从库线程连接的时候, 主库可以将二进制日志发送给从库,当主库读取事件(Event)的时候,会在 Binlog 上 加锁 ,读取完成之后,再将锁释放掉。–>二进制日志转储线程负责将数据发送出去。
从库 I/O 线程 会连接到主库,向主库发送请求更新 Binlog。这时从库的 I/O 线程就可以读取到主库的二进制日志转储线程发送的 Binlog 更新部分,并且拷贝到本地的中继日志 (Relay log)。
从库 SQL 线程 会读取从库中的中继日志,并且执行日志中的事件,将从库中的数据与主库保持同步。
Mysql8的新特性
第01章 Linux下MySQL的安装与使用:3.登录:3.3 设置远程登录:4. Linux下修改配置
对密码进行加密
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
第05章 存储引擎:4. 引擎介绍:4.1 InnoDB 引擎:具备外键支持功能的事务存储引擎
默认的InnoDB存储引擎:支持事务,行锁,外键
第08章 索引的创建与设计原则:2. Mysql8.0索引新特性
降序索引和隐藏索引
第10章 索引优化与查询优化:10. 索引下推:10.1 使用前后的对比
Index Condition Pushdown(ICP)是MySQL 5.6中新特性,
是一种在存储引擎层使用索引过滤数据的一种优化方式。
第12章 数据库其它调优策略:5. 其它调优策略:5.3 MySQL 8.0新特性:隐藏索引对调优的帮助
隐藏索引对调优的帮助
第15章 锁:3. 锁的不同角度分类:3.1从数据操作的类型划分:读锁、写锁:1.锁定读
MySQL8.0新特性:
在5.7及之前的版本,SELECT …FOR UPDATE,如果获取不到锁,
会一直等待,直到`innodb_lock_wait_timeout`超时。
在8.0版本中,在SELECT …FOR UPDATE,SELECT …FOR SHARE后
添加`NOWAIT`、`SKIP LOCKED`语法,跳过锁等待,或者跳过锁定。
- 通过添加NOWAIT、SKIP LOCKED语法,能够立即返回。如果查询的行已经加锁:
- 那么NOWAIT会立即报错返回
- 而SKIP LOCKED也会立即返回,只是返回的结果中不包含被锁定的行。
第17章 其他数据库日志:4. 错误日志(error log):4.4 MySQL8.0新特性
错误日志的改进
Redis
redis为什么快
1.纯内存KV操作
Redis的操作都是基于内存的,CPU不是 Redis性能瓶颈,,Redis的瓶颈是机器内存和网络带宽。
在计算机的世界中,CPU的速度是远大于内存的速度的,同时内存的速度也是远大于硬盘的速度。redis的操作都是基于内存的,绝大部分请求是纯粹的内存操作,非常迅速。
2.单线程操作
使用单线程可以省去多线程时CPU上下文会切换的时间,也不用去考虑各种锁的问题,不存在加锁释放锁操作,没有死锁问题导致的性能消耗。对于内存系统来说,多次读写都是在一个CPU上,没有上下文切换效率就是最高的!既然单线程容易实现,而且 CPU 不会成为瓶颈,那就顺理成章的采用单线程的方案了
Redis 单线程指的是网络请求模块使用了一个线程,即一个线程处理所有网络请求,其他模块该使用多线程,仍会使用了多个线程。
3.I/O 多路复用
为什么 Redis 中要使用 I/O 多路复用这种技术呢?
首先,Redis 是跑在单线程中的,所有的操作都是按照顺序线性执行的,但是由于读写操作等待用户输入或输出都是阻塞的,所以 I/O 操作在一般情况下往往不能直接返回,这会导致某一文件的 I/O 阻塞导致整个进程无法对其它客户提供服务,而 I/O 多路复用就是为了解决这个问题而出现的
4.Reactor 设计模式
Redis基于Reactor模式开发了自己的网络事件处理器,称之为文件事件处理器(File Event Hanlder)。
自己补充:
高效的操作:渐进式rehash、缓存时间戳
KV键-全局hash表 两张哈希表
把一次性大量拷贝的开销,分摊到了多次处理请求的过程中
计算机组成原理的DARM的刷新操作:集中 分步 异步
使用系统时间戳的时候,要使用系统调用获取系统的毫秒时间戳
每一次都是系统调用,有CPU的上下文切换。redis由一个定时任务,每毫米更新一次时间缓存的,获取时间都是从缓存中直接拿。
什么是bigkey?会有什么影响?
bigkey是指key对应的value所占的内存空间比较大,例如一个字符串类型的value可以最大存到512MB,一个列表类型的value最多可以存储23-1个元素。
如果按照数据结构来细分的话,一般分为字符串类型bigkey和非字符串类型bigkey。
字符串类型:体现在单个value值很大,一般认为超过10KB就是bigkey,但这个值和具体的OPS相关。
非字符串类型:哈希、列表、集合、有序集合,体现在元素个数过多。
bigkey无论是空间复杂度和时间复杂度都不太友好,下面我们将介绍它的危害。
bigkey的危害
bigkey的危害体现在三个方面:
1、内存空间不均匀.(平衡):例如在Redis Cluster中,bigkey 会造成节点的内存空间使用不均匀。
2、超时阻塞:由于Redis单线程的特性,操作bigkey比较耗时,也就意味着阻塞Redis可能性增大。
3、网络拥塞:每次获取bigkey产生的网络流量较大
假设一个bigkey为1MB,每秒访问量为1000,那么每秒产生1000MB 的流量,对于普通的千兆网卡(按照字节算是128MB/s)的服务器来说简直是灭顶之灾,而且一般服务器会采用单机多实例的方式来部署,也就是说一个bigkey可能会对其他实例造成影响,其后果不堪设想。
Redis的数据结构
string
list
hash
set
zset
hash的渐进式resize
zset的使用
redis中的zset
2023/7/28 16:37:28
Redis中的ZSet(有序集合)是一种特殊的数据结构,它是一种有序的字符串集合。每个成员都与一个分数(score)相关联,该分数用于对成员进行排序。在ZSet中,成员是唯一的,但是分数可以重复。
ZSet的主要特点是可以高效地插入、删除和查找成员,同时可以根据分数范围或成员来进行范围查找和排名操作。这使得ZSet在许多应用场景下非常有用,例如排行榜、计数器、优先级队列等。
常见的ZSet操作包括:
ZADD:向有序集合中添加一个成员及其对应的分数。
ZREM:从有序集合中删除一个成员。
ZSCORE:获取成员的分数。
ZRANK:获取成员在有序集合中的排名(按分数从低到高)。
ZREVRANK:获取成员在有序集合中的排名(按分数从高到低)。
ZRANGE:根据分数范围获取有序集合中的成员列表(按分数从低到高)。
ZREVRANGE:根据分数范围获取有序集合中的成员列表(按分数从高到低)。
下面是一个使用Redis的CLI(命令行界面)示例,演示如何创建和操作一个ZSet:
# 添加成员及其分数到有序集合"my_zset"
127.0.0.1:6379> ZADD my_zset 90 "Alice"
(integer) 1
127.0.0.1:6379> ZADD my_zset 85 "Bob"
(integer) 1
127.0.0.1:6379> ZADD my_zset 95 "Charlie"
(integer) 1
# 获取成员的分数
127.0.0.1:6379> ZSCORE my_zset "Alice"
"90.0"
# 获取成员在有序集合中的排名(按分数从低到高)
127.0.0.1:6379> ZRANK my_zset "Charlie"
(integer) 2
# 获取成员在有序集合中的排名(按分数从高到低)
127.0.0.1:6379> ZREVRANK my_zset "Bob"
(integer) 1
# 根据分数范围获取有序集合中的成员列表(按分数从低到高)
127.0.0.1:6379> ZRANGE my_zset 0 -1
1) "Bob"
2) "Alice"
3) "Charlie"
请注意,以上示例中使用的是默认情况下的升序排序。如果你想要降序排序,则可以使用ZREVRANGE等命令。
ZSet在Redis中是一个非常强大的数据结构,允许你进行高效的有序数据存储和检索。
zset为什么使用跳表
2023/7/28 16:38:44
ZSet在Redis中使用跳表(Skip List)的数据结构是因为跳表在有序集合这种场景下具有很好的性能特性。
跳表是一种随机化数据结构,它是有序链表的扩展,通过添加多级索引(跳跃)来提高查询效率。跳表允许在O(log n)的时间复杂度内进行插入、删除和查找操作,其中n是跳表中元素的数量。
对于有序集合(ZSet),要求元素按照分数进行排序,同时又需要高效地进行插入和删除操作。在这种情况下,传统的数据结构如红黑树虽然能够满足有序性的要求,但插入和删除操作的时间复杂度是O(log n),不如跳表高效。
跳表的优点包括:
查询高效:跳表允许通过多级索引快速定位元素,使得查询的时间复杂度较低。
插入和删除高效:跳表对插入和删除操作进行随机化优化,平均情况下时间复杂度为O(log n)。
简单易实现:相对于平衡二叉树等复杂数据结构,跳表的实现相对简单。
Redis中的ZSet使用跳表的结构来满足上述需求,使得ZSet在插入、删除和范围查询等操作上具有较高的性能。然而,值得注意的是,跳表也会带来一定的额外空间开销,因为它需要维护多级索引,但在Redis这种内存数据库中,通常更注重性能而不是空间的绝对节省。因此,跳表是Redis中实现有序集合的一个理想选择。
redis的事务
Redis的事务是一种将多个命令打包执行的机制,它允许一系列的命令按照顺序依次执行,同时保证这些命令在执行过程中不会被其他客户端的命令所中断。事务在Redis中通过MULTI、EXEC、WATCH、DISCARD等命令来实现。
以下是事务执行的基本过程:
- MULTI:在事务开始前,使用MULTI命令来标记事务的开始。
- 依次执行多个命令:在MULTI和EXEC之间,可以依次执行多个Redis命令,这些命令会被放入一个队列中,而不是立即执行。
- EXEC:执行EXEC命令来执行之前放入队列中的所有命令。Redis会依次执行队列中的命令,如果在执行过程中没有发生错误,那么所有命令都会被执行成功。否则,如果出现错误,比如其中一个命令失败,那么整个事务将会回滚,之前执行的命令都不会生效。
- WATCH:使用WATCH命令可以监视一个或多个键,如果在事务执行之前有其他客户端对这些键进行了修改,事务将会被中断。这可以用于乐观锁机制,保证事务的原子性。
- DISCARD:在事务执行之前,使用DISCARD命令可以取消当前事务,清空事务队列中的所有命令。
事务的特点:
- 事务保证了一系列命令的原子性,要么全部执行成功,要么全部不执行。
- 事务的执行是单线程的,不会被其他客户端的操作中断。
- 事务不支持回滚点,一旦执行EXEC,整个事务会被执行,无法在执行过程中中途取消或回滚。
需要注意的是,虽然事务提供了原子性,但是在并发写入的情况下,使用WATCH命令时要小心竞态条件(race condition)。WATCH可以用于乐观锁控制,但是在实际使用中要根据具体场景谨慎使用,以避免潜在的问题。
另外,Redis的事务是一种逻辑上的事务,并非传统数据库中的ACID事务。在Redis中,没有像ROLLBACK这样的回滚操作,因为事务不会中途取消或回滚。事务更多地用于将多个命令打包执行,保证一系列操作的原子性。
使用redis如何设计分布式锁?
基于 Redis 实现的分布式锁,一个严谨的的流程如下:
1、加锁
SET lock_key $unique_id EX $expire_time NX
2、操作共享资源
3、释放锁:Lua 脚本,先 GET 判断锁是否归属自己,再DEL 释放锁
redis的持久化
Redis支持两种方式的持久化,分别是RDB(Redis Database File)和AOF(Append-Only File)。
RDB持久化: RDB持久化是将Redis在某个时间点上的数据快照(snapshot)保存到磁盘上的持久化方式。这个快照包含了Redis在生成快照时刻的所有数据,是一个二进制文件。RDB持久化适用于备份和数据恢复的场景。
RDB持久化的优点:
- 对于数据恢复来说,RDB文件较小,加载速度较快。
- RDB持久化是生成全量备份,适合用于灾难恢复。
RDB持久化的缺点:
- 如果Redis意外宕机,会丢失最后一次快照之后的所有数据。
- 生成快照时,Redis可能会阻塞客户端请求。
AOF持久化: AOF持久化是通过记录Redis服务器所执行的写命令来保证数据的持久性。AOF文件是一个追加写入的日志文件,它包含了Redis服务器执行的所有写操作指令。当Redis重启时,会重新执行AOF文件中的指令,从而将数据还原到原来的状态。
AOF持久化的优点:
AOF文件是一个追加写入的日志文件,不会导致像RDB那样的阻塞现象。
AOF持久化相对于RDB来说,数据丢失的风险更小,因为AOF记录的是每个写操作,所以在故障发生时,最多只会丢失最后一条写指令之后的数据。
AOF文件以纯文本方式记录,易于阅读和理解。
AOF持久化的缺点:
- AOF文件通常比RDB文件大,因为它记录了每个写操作指令。
- AOF文件的恢复速度可能比RDB慢,特别是当AOF文件很大时。
在Redis中,可以同时启用RDB和AOF持久化,以提供更高的数据安全性。也可以根据实际需求选择只启用其中一种持久化方式。同时,还可以根据配置选项来控制持久化的频率和策略,以平衡性能和数据持久化的要求。
如何保证缓存与数据库双写时的数据一致性
第一种方案:采用延时双删策略
具体的步骤就是:
先删除缓存;
再写数据库;
休眠500毫秒;
再次删除缓存。
第二种方案:异步更新缓存(基于订阅binlog的同步机制)
技术整体思路:
MySQL binlog增量订阅消费+消息队列+增量数据更新到redis
Redis常见性能问题和解决方案有哪些?
一、缓存穿透:就是查询一个压根就不存在的数据,即缓存中没有,数据库中也没有
解决方案:使用布隆过滤器,把数据先加载到布隆过滤器中,访问前先判断是否存在于布隆过滤器中,不存在代表这笔数据压根就不存在。
缺点:布隆过滤器是不可变的,可能一开始过滤器和数据库数据时一致的,后面数据库数据变了,或变多或变少,而对应的布隆过滤器的数据也要改变,这时会比较麻烦。
二、缓存击穿:数据库中有,缓存中没有。缓存击穿实际就是一个并发问题,一般来说查询数据,先查询缓存,有直接返回,没有再查询数据库并放到缓存中之后返回,但这种场景在并发情况下就会有问题,假设同时又100个请求执行上面
逻辑的代码,则可能会出现多个请求都查询数据库,因为大家同时执行,都查到了缓存中没有数据。
解决方案:加锁。如果是单机部署,则可以使用JVM级别的锁,如lock、synchronized。如果是集群部署,则需要使用分布式锁,如基于redis、zookeeper、mysql等实现的分布式锁。
三、缓存雪崩:大部分数据同时失效、过期,新的缓存又没来,导致大量的请求都去访问数据库而导致的服务器压力过大、宕机、系统崩溃。
解决方案:搭建高可用的redis集群,避免压力集中于一个节点;缓存失效时间错开,避免缓存同时失效而都去请求数据库。
Redis过期策略都有哪些?LRU算法知道吗?
1.noeviction:返回错误当内存限制达到,并且客户端尝试执行会让更多内存被使用的命令。
2.allkeys-lru: 尝试回收最少使用的键(LRU),使得新添加的数据有空间存放。
3.volatile-lru: 尝试回收最少使用的键(LRU),但仅限于在过期集合的键,使得新添加的数据有空间存放。
4.allkeys-random: 回收随机的键使得新添加的数据有空间存放。
5.volatile-random: 回收随机的键使得新添加的数据有空间存放,但仅限于在过期集合的键。
6.volatile-ttl: 回收在过期集合的键,并且优先回收存活时间(TTL)较短的键,使得新添加的数据有空间存放。
redis的集群
redis的主从复制
slaveof 127.0.0.1 6379
info replication
slaveof no one
redis的哨兵模式
sentinel monitor myredis 127.0.0.1 6379 1
Spring
IOC
控制反转(IoC)是Spring的核心思想之一,它的主要作用是将对象的创建过程交给Spring容器来完成,这样可以更加灵活地管理和组装对象,降低了组件之间的耦合度。
AOP
AOP则是另一个核心思想,它主要是通过在程序运行过程中动态地横向切入特定方法来实现对应用程序的功能增强,例如日志记录、事务处理等。
循环依赖
首先我们要明确一点就是如果这个对象A还没创建成功,在创建的过程中要依赖另一个对象B,而另一个对象B也是在创建中要依赖对象A,这种肯定是无解的,这时我们就要转换思路,我们先把A创建出来,但是还没有完成初始化操作,也就是这是一个半成品的对象,然后在赋值的时候先把A暴露出来,然后创建B,让B创建完成后找到暴露的A完成整体的实例化,这时再把B交给A完成A的后续操作,从而揭开了循环依赖的密码。也就是如下图:
springboot如果要对属性文件中的账号密码加密如何实现?
SpringBoot的优点
Spring Boot 优点非常多,如:
一、独立运行
Spring Boot而且内嵌了各种servlet容器,Tomcat、Jetty等,现在不再需要打成war包部署到容器
中,Spring Boot只要打成一个可执行的jar包就能独立运行,所有的依赖包都在一个jar包内。
二、简化配置
spring-boot-starter-web启动器自动依赖其他组件,简少了maven的配置。
三、自动配置
Spring Boot能根据当前类路径下的类、jar包来自动配置bean,如添加一个spring-boot-starter
web启动器就能拥有web的功能,无需其他配置。
四、无代码生成和XML配置
Spring Boot配置过程中无代码生成,也无需XML配置文件就能完成所有配置工作,这一切都是借助
于条件注解完成的,这也是Spring4.x的核心功能之一。
五、应用监控
Spring Boot提供一系列端点可以监控服务及应用,做健康检测
Spring Boot 的核心注解是哪个?它主要由哪几个注解组成的?
启动类上面的注解是@SpringBootApplication,它也是 Spring Boot 的核心注解,主要组合包含了以下 3 个注解:
● @SpringBootConfiguration:组合了 @Configuration 注解,实现配置文件的功能。
● @EnableAutoConfiguration:打开自动配置的功能,也可以关闭某个自动配置的选项,
如关闭数据源自动配置功能: @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })。
● @ComponentScan:Spring组件扫描
Kafka
为什么使用kafka
缓冲和削峰:上游数据时有突发流量,下游可能扛不住,或者下游没有足够多的机器来保证冗余,kafka在中间可以起到一个缓冲的作用,把消息暂存在kafka中,下游服务就可以按照自己的节奏进行慢慢处理。
解耦和扩展性:项目开始的时候,并不能确定具体需求。消息队列可以作为一个接口层,解耦重要的业务流程。只需要遵守约定,针对数据编程即可获取扩展能力。
冗余:可以采用一对多的方式,一个生产者发布消息,可以被多个订阅topic的服务消费到,供多个毫无关联的业务使用。
健壮性:消息队列可以堆积请求,所以消费端业务即使短时间死掉,也不会影响主要业务的正常进行。
异步通信:很多时候,用户不想也不需要立即处理消息。消息队列提供了异步处理机制,允许用户把一个消息放入队列,但并不立即处理它。想向队列中放入多少消息就放多少,然后在需要的时候再去处理它们。
kafka为什么这么快
Kafka速度的秘诀在于,它把所有的消息都变成一个批量的文件,并且进行合理的批量压缩,减少网络IO损耗,通过mmap提高I/O速度,写入数据的时候由于单个Partion是末尾添加所以速度最优;读取数据的时候配合sendfile直接暴力输出。
- 顺序写入
- 在partition文件尾部追加数据
- 读取数据时,基于offset的顺序读磁盘
- 零拷贝
- 传统IO
- 硬盘—>内核buf—>用户buf—>socket相关缓冲区—>协议引擎
- 零拷贝使得直接从内核缓冲区拷贝到 socket 缓冲区
- 在内核完成输入输出,不需要在拷贝到用户态的用户程序中
- 日志记录批处理
- 批量压缩发送消息:不一条一条发送消息。缓存一定量的消息时候再进行一次网络 IO。(RocketMQ缓存会导致频繁GC,而kafka使用堆外内存就不会这样)
- 非强制刷新缓存:确认ACK之前只记录到 IO Buffer
- 规避GC:使用大量通道Channl、本地缓冲区。
kafka如何保证高可用,一致性,有序性
高可用:kafka有多个broker机器节点,并且partition的副本会随机保存在这些broker中,即使有broker宕机了,也能从其他broker中找到副本。
一致性:partition的follower副本会同步leader副本,并将能容忍延迟的副本保存在ISR中。消费者只能消费到HW(高水印值以下的信息)。当leader挂掉之后,会选举新的leader出来。一般情况下,消费者只从leader中读取消息,follower只用于同步数据。
有序性:kafka只能保证partition的有序性,不能保证topic的有序。如果一定要完全的有序,可以让topic只有一个partition。或者根据业务需要,指定分配到同一个partition。