NoSQL
非关系型的数据库,键值、文档以及图形类型数据存储。天生支持分布式,数据冗余和数据分片等特性,提供可扩展的高可用高性能数据存储。
redis
Redis优缺点:读写快因为在内存中,只适合小数据量存储和读写因为在内存比磁盘小。
Redis单线程很快的原因
1、redis 是纯内存操作 2、采用单线程,避免了线程上下文切换,没有线程安全问题,3、使用io多路复用模型提高了网络请求速度,解决了网络延迟瓶颈。
键值对的值有8种数据类型
5种基本
String 字符串(最大512M) 用来存缓存复杂对象,登录用户状态信息、计数,分布式锁
list 可重复(类似java的LinkedList) 头尾两端进行push和pop操作,常用做消息队列
set 无序不可重复 (类似java的HashSet) 用于点赞(因为不可重复),共同关注(因为能求并集,交集,差集)
zset(sorted set)有序的set 存filed-value键值对,value是分数。用于做排行榜(因为按分数排序)
hash (类似HashMap<String,String>) 存简单对象,根据filed直接获取value
3种特殊
HyperLogLog(基数统计)、Bitmap (位图)、Geo(经纬度)。
Bloom filter(布隆过滤器)、Bitfield(位域)
底层是压缩列表或者眺表实现的,当元素个数小于128并且每个元素值小于64字节的时候,redis使用压缩列表作为底层数据结构,如果不满足上面的条件会使用眺表作 为底层数据结构。在redis 7.0全部使用listpack实现。眺表的时间复杂度是o(logN);
眺表在创建节点的时候,生成一个0-1的随机数,如果随机数小于0.25就增加一层。然后继续生成新的随机数,层数越高概率越低。层数最高时64
眺表对于b+树的优点:从内存上看,眺表比平衡树更灵活一些,在范围查询时,眺表比平衡树更简单一点,算法实现上,眺表比平衡树更简单一点
压缩列表底层时连续的内存块组成,有点像数组,表头有3个字段:整个压缩列表占用字节数,尾部距离起始节点偏移量,包含的节点数量,队尾有一个节点,记录结 束点。查询第一个或者最后一个o1,查询中间的时on,每个节点包含三个元素,前一个节点的长度,当前节点的类型和长度,当前节点的数据。当插入数据时,会根 据是字符串还是整数,分配内存,缺点是会发生连锁更新(如果前一个节点的长度小于254字节,用一字节保存长度,大于等于254字节,要用5字节保存这个长度,如 果一个数据更新可能会导致连锁更新),一旦发生,内存要重新分配,适用于结点数量少的情况。
quicklist是双向链表+压缩列表,减少了连锁更新带来的影响,每一个节点都带着一个压缩列表,加入元素是会检测对应位置的压缩列表能不能容纳该元素,能就加 入,不能就新建一个节点,尽量避免了连锁更新,当时没有完全解决这个问题,
listpack紧凑列表:采用了压缩列表很多优秀的设计,也是连续空间保存数据,不同编码方式表示不同大小的数据。头包含两个属性,总字节数和元素数量,末尾也有 结尾的标识,每个节点有编码类型,实际数据,总长度前面两个元素的长度。完全避免了连锁更新,
购物车信息用 String 还是 Hash 存储更好呢?
购物车中的商品频繁修改和变动,所以使用 Hash 存储。对象内还有内部对象也用Hash,用String会麻烦
mysql的数据如何与redis进行同步呢?(双写一致性)
不要求强一致的业务
1、延迟双删,先删缓存,再更新数据库,然后延时再删一次缓存。(如果部署了主从数据库还要等主库同步数据到从库后再 删,所以要延时一小会儿,),优点是不加锁,缺点是有小概率产生脏数据,延时越久概率越低
2、使用MQ,更新数据库后给redis发消息去删缓存
3、canal组件,canal服务伪装成mysql的从节点读取binlog日志,去更新缓存。优点是不需要改业务代码
要求强一致的业务
1、用Redisson的读写锁,因为缓存肯定是读多写少,所以读数据时加读锁,写数据时加写锁,这样强一致性,但加了锁性能 不高。
用Redisson的读写锁
readLock :共享锁,加锁之后,其他线程也可以读,阻塞写的线程
writeLock:独占锁,加锁之后,阻塞其他线程读和写
缓存穿透 请求的数据在缓存和数据库都没有 ,那会永远请求数据库:
1、 缓存空对象并设置ttl防止空对象占用太多内存,数据库中查不到,建立空对象缓存
2、布隆过滤(存在误判,布隆说存在可能骗人)
布隆过滤器能检查一个元素是否在一个集合中(常用的是redisson实现的布隆过滤器)
先初始化一个大数组,里面存0或1,一开始都是0,当添加key时,经过3次hash计算key,模于数组长度找到三个数组下标然后把它们改为1,这样数组的三个位置就能标明一个key的存在。查找的过程也是一样的。
1、缺点是可能误判存在,一般设置误判率为5%(数组越长误判率越低,但占用内存更多),5%以内的误判率在高并发下一般也压不崩数据库。
缓存雪崩和解决方法
指大量的键同时失效或Redis服务宕机导致高并发请求数据库
1、给key的TTL在一定区间内设置随机值,
2、部署Redis集群提高服务的可用性
缓存击穿问题也叫热点Key问题,就是一个被高并发访问且缓存重建时间长的key失效,在缓存重建期,大量请求去查数据库
1、可以用逻辑过期+互斥锁(如果是多个微服务实例还要分布式锁):给key的value加个逻辑过期,线程没查到就尝试获取锁,判断获取锁是否成功,没获取到返回 老数据。获取到锁的线程去开个子线程去查数据库,自己也返回老数据。因为别的线程也返回的老数据,持有锁的线程也返回吧,查数据库再重建缓存时间长。
上面的三个问题都可以用限流或降级兜底(防止请求过多压垮服务),也可以给业务数据添加多级缓存
linux一个进程使用内存的情况分为用户空间和内核空间,用户空间只能执行受限的命令,而不能直接调用系统资源,必须通过内核提供的接口来访问,内核空间可以执行特权命令,可以调用一切的系统资源,linux为了提高io效率,会在用户空间和内核空间都使用一个缓冲区,写数据时,把用户缓冲区拷贝到内核缓冲区再写入设备,读数据时和写数据想反,先读取设备数据到内核缓冲区再拷贝到用户缓冲区。
阻塞io就是等待数据也就是用户读取数据时,如果没有数据要阻塞等待和数据拷贝到用户空间都是阻塞的,数据拷贝完成后才不阻塞。
非阻塞io 只是第一阶段不阻塞了,当没有数据时,直接返回错误,但是还是会不断尝试去获取数据,虽然是非阻塞的,但是性能并没有得到提高,而且可能会导致cpu空转,cpu使用率增加,
io多路复用,利用单个线程监听多个socket,并在socket可以操作是发送通知,避免无效等待,是阻塞的,充分利用了cpu资源。优点是可以监听多个socket。当监听到可以操作的socket时,通知的方式又有多种,select和poll通知方式只通知有socket就绪,但是不能定位到具体的socket需要循环去遍历,epoll在通知的同时还会把已经就绪的socket写入用户空间,可以直接找到并操作
Redis网络模型就是使用io多路复用来实现网络io的高性能,使用io多路复用加事件派发,将不同类型的任务给不同的处理器,有连接应答处理器(用来处理连接请求),命令回复处理器,和命令请求处理器(接收数据转为redis命令然后把执行结果写入缓冲区,交给命令回复处理器回复),redis6.0之后还对 命令回复处理器和 接收数据转换为redis命令做了多线程优化,速度更快。
三种持久化方式有什么区别呢?
1、RDB(Redis Database Backup)快照文件,隔一段时间,将redis内存存储的数据写到磁盘上,相当于拍了快照,当redis实例宕机重启时,方便从RDB的快照文 件中恢复数据。还可以将快照 复制到其他服务器从而创建服务器副本
2、AOF( Append Only File)只追加文件,当redis执行写命令的时候,都会追加到这个文件中,redis实例宕机重启的时候,重新执行一遍文件中命令来恢复数据,
3、RDB 和 AOF 的混合持久化(Redis 4.0 新增)
保存的数据允许丢失一些使用 RDB。
保存的数据不允许丢失使用 AOF(可设置每次改动都保存)
RDB 和 AOF 的混合:RDB 快照可以进行数据库备份、更快的重启以及解决 AOF 引擎错误。
哪种恢复的比较快呢?
RDB因为是二进制文件,体积小,它恢复的比较快,但可能丢数据,
AOF恢复的速度慢一些(因为是记录了每一条命令,所以AOF文件比RDB大的多。当AOF触发阈值后会重写AOF文件,减少文件的体积。AOF数据比RDB完整性高, 但是启动速度比RDB慢,但是它丢数据的概率更小)在AOF文件中可以设置多少秒批量写入一次命令
Redis的数据ttl过期策略有哪些 ?
1、惰性删除,key过期了不管,只有当查询该key,再检查它是否过期,如果过期删掉它,没过期就返回。优点是减少cpu使用,因为只有使用时才会检查key是否过 期,缺点是占内存,如果一个key一直没被查询会一直存在内存中。
2、定期删除,每隔一段时间,检查过期的key删除
两种模式
1.SLOW模式是固定周期删除过期的key,间隔时间和每次操作时间都较长(操作速度慢,可以通过修改配置文件redis.conf 的 hz 选项来调整这个周期)
2.FAST模式不固定,但间隔时间和每次操作时间都较短(操作速度快)
而Redis是惰性删除 + 定期删除两种策略结合使用
缓存过多,内存被占满了怎么办?(使用数据淘汰策略)
默认是noeviction,内部满了,就不能添加,不删除任何key,并报错
random 随机淘汰 (又分有ttl和所有)
LRU(Least Recently Used)最近最少使用,最后一次访问时间越久远,越先淘汰。(又分有ttl和所有)
LFU(Least Frequently Used)最少频率使用。访问频率越小key淘汰优先级越高。 (又分有ttl和所有)
我们在项目设置的allkeys-lru,最近最少使用的数据淘汰,把最近访问的key留在redis中
数据淘汰策略-使用建议
数据访问频率差别大,用 allkeys-lru 策略。把最近最常访问的数据留在缓存中
数据访问频率差别不大,用 allkeys-random,随机选择淘汰。
如果业务中有置顶的需求,可以用 volatile-lru ,同时置顶数据不设置过期时间,这些数据就一直不被删除
业务中有短时高频访问的数据,可以使用 allkeys-lfu 或 volatile-lfu 策略。
数据库有1000万数据 ,Redis只能缓存20w数据, 如何保证Redis中的数据都是最近的热点数据 ?
allkeys-lru (挑选最近最少使用的数据淘汰)淘汰策略,那留下来的都是最近经常访问的热点数据
Redis分布式锁如何实现 ?
在redis中提供了一个命令setnx(SET if key not exists),由于redis的单线程的,用了命令之后,只能有一个客户端对某一个key设置值,在没有过期或删除key的时候是 其他 客户端是不能设置这个key的。
如何控制Redis实现分布式锁有效时长呢?
业务执行时长不固定,所以不好确定分布式锁的TTL。所以用的redis的一个框架redisson实现好的分布式锁。在redisson中手动加锁,并且设置锁的失效时间和等待 时间,redisson中引入了一个看门狗机制,就是说每隔一段时间就给还持有锁的业务续期,业务执行完释放锁就可以了
还有一个好处就是,在高并发下,一个业务有可能会执行很快,先客户1持有锁的时候,客户2来了以后并不会马上拒绝,它会自旋不断尝试获取锁,如果客户1释放之 后,客户2就可以马上持有锁,性能得到了提升。
redisson实现的分布式锁是可重入的吗?
是可以重入的。这样做是为了避免死锁的产生。这个重入其实在内部就是判断是否是当前线程持有的锁,如果是当前线程持有的锁就会计数,如果释放锁就会在计算上 减一。在存储数据的时候采用的hash结构,业务名设置大key,其中小key是当前线程的唯一标识,value是当前线程重入的次数
redisson实现的分布式锁能解决主从一致性的问题吗
不能的,比如,当线程1在master节点加锁成功后,master节点数据会还没来得及异步复制到slave节点,宕机了。slave节点升为新的master节点,假如现在来了一 个线程2,会在新的master节点上加锁成功,这个时候就会出现两个节点同时持有一把锁的问题。
我们可以利用redisson提供的红锁来解决这个问题,它的主要作用是,不能只在一个redis实例上创建锁,应该是在多个redis实例上创建锁,并且要求在大多数redis 节点上都成功创建锁,红锁中要求是redis的节点数量要过半。这样就能避免线程1加锁成功后master节点宕机导致线程2成功加锁到新的master节点上的问题了。因为 需要同时在多个节点上都添加锁,性能就变的很低了,并且运维成本也非常高,一般在项目中也不会直接使用红锁
如果业务非要保证数据的强一致性,这个该怎么解决呢?
用zookeeper实现的分布式锁,它是可以保证强一致性的。
redis的集群方案有三种
主从复制
一般有一个主节点和多个从节点,主节点用于写操作,从节点用于读操作,从而实现读写分离,提高redis的并发能力。主从数据同步有两步,第一步是全量同步,首先从节点携带数据集id向主节点发送建立连接的请求,主节点判断数据集id是否一致,每一个主节点都有一个数据集id,从节点继承主节点的数据集id,如果数据集id为空说明是第一次请求同步,然后主节点将这个id和一个偏移量发给从节点,从节点保存这个信息,主节点执行bgsave生成一个RDB文件,将文件同步给从节点用来同步数据,这个过程中如果有新数据写入主节点,主节点会将新来的数据存入repl_backlog一个日志中,然后发送给从节点,从节点进行数据更新
还有就是增量同步,当从节点重启或者主节点数据变化是,主节点会将数据集id和偏移量发给主节点,主节点去一个用于同步给从节点的日志中根据传入的偏移量将数据同步给从节点。
哨兵模式
主从模式有几个缺点,不能保证高可用,主节点宕机后就不能正常工作了,然后就有了这个哨兵模式,可以实现集群的故障恢复,Sentinel也就是哨兵可以监控主从节点是否正常工作,当主节点宕机后,哨兵会找一个从节点作为新的主节点,故障恢复后,也以新的master为主,当集群更换新的主节点之后,哨兵会将服务变更通知给redis客户端,哨兵每隔1s向每个实例发送ping命令当接收到pong之后表示正常工作。如果某个实例未在规定时间内响应,主观下线,当一定数量的哨兵查询到节点下线,则表示该节点客观下线。
当主节点宕机后选择新的主节点规则:判断从节点与主节点断开时间的长度,时间太长就排除该节点,判断从节点的一个权重优先级的值,优先级值越小,优先级越高,如果优先级一样,就看offset,越大数据越完整,优先级越高,最后是salve节点的id,越小优先级越高。
脑裂问题,当哨兵和主节点直接的网络不通畅时,会新选择一个从节点,会把新节点选为主节点,这个过程中redis客户端还是继续往之前的节点存储数据,当网络恢复之后,之前的主节点会被降级为从节点,将新的主节点的信息同步过去,会导致数据的丢失。解决方案就是主节点最少有一个从节点(老节点没有从节点就不能用了),数据复制和同步的延迟不能超过5s(老节点向从节点同步数据时间很长,所以也不能用了)
分片集群
主从和哨兵模式解决了高可用,高并发读的问题,使用分片集群才能解决海量数据存储和高并发写的问题,特征:集群中有多个master,每个master都能保存不同的数据,每个主节点都能有多个从节点,master直接通过ping监测彼此的状态 ,客户端可以访问任意的主节点,最终会被路由到正确的节点。集群会被分为16384个哈希槽,每个key通过crc校验对这个数取模来决定放哪个槽,每个节点都负责一部分哈希槽。
Redis集群有哪些方案, 知道嘛 ?
有三种,主从复制、哨兵模式、Redis分片集群
介绍一下主从同步
单节点Redis的并发能力有上限,要提高Redis的并发能力,可以搭建主从集群,实现读写分离。一般是一主多从,主节点负责写数据,从节点负责读数据,主节点写 入数据之后,需要把数据同步到从节点中
主从复制数据的流程
分为了两个阶段,一个是全量同步,一个是增量同步
全量同步是指从节点第一次与主节点建立连接时会用全量同步,流程是这样的
第一从节点请求主节点同步数据,其中从节点会携带自己的replication id和offset偏移量。
第二主节点判断是否是第一次请求,是第一次同步,那主节点就会发自己的数据版本信息给从节点
第三在同时主节点会执行bgsave,生成rdb发给从节点去执行,这样就保持了一致
rdb生成执行期间,主节点会把新来的命令记录到一个日志文件,最后把这个日志文件发送给从节点,这样就能保证主节点与从节点完全一致了,后面再同步数据 的时候,都是依赖于这个日志文件,这个就是全量同步
增量同步发生在从节点服务重启后,从节点还是请求主节点同步数据,主节点判断不是第一次就获取从节点offset值,然后把命令日志从节点offset值之后的数据,发 送给从节点进行数据同步
怎么保证Redis的高并发高可用
搭建主从集群,再用redis中的哨兵模式,哨兵模式可以实现主从集群的自动故障恢复,里面就包含了对主从服务的监控、自动故障恢复、通知;如果master故障,Sentinel会将一个slave提升为master。当故障实例恢复后也以新的master为主;同时Sentinel也充当Redis客户端的服务发现来源,当集群发生故障转移时,会将最新信息推送给Redis的客户端,所以一般项目都会采用哨兵的模式来保证redis的高并发高可用
你们使用redis是单点还是集群,哪种集群
嗯!,我们当时使用的是主从(1主1从)加哨兵。一般单节点不超过10G内存,如果Redis内存不足则可以给不同服务分配独立的Redis主从节点。尽量不做分片集群。因为集群维护起来比较麻烦,并且集群之间的心跳检测和数据通信会消耗大量的网络带宽,也没有办法使用lua脚本和事务
redis集群脑裂,该怎么解决呢?
嗯! 这个在项目很少见,不过脑裂的问题是这样的,我们现在用的是redis的哨兵模式集群的
有的时候由于网络等原因可能会出现脑裂的情况,就是说,由于redis master节点和redis salve节点和sentinel处于不同的网络分区,使得sentinel没有能够心跳感知到master,所以通过选举的方式提升了一个salve为master,这样就存在了两个master,就像大脑分裂了一样,这样会导致客户端还在old master那里写入数据,新节点无法同步数据,当网络恢复后,sentinel会将old master降为salve,这时再从新master同步数据,这会导致old master中的大量数据丢失。
关于解决的话,我记得在redis的配置中可以设置第一可以设置最少的salve节点个数,比如设置至少要有一个从节点才能同步数据,第二个可以设置主从数据复制和同步的延迟时间,达不到要求就拒绝请求,就可以避免大量的数据丢失
redis的分片集群有什么作用
分片集群主要解决的是,海量数据存储的问题,集群中有多个master,每个master保存不同数据,并且还可以给每个master设置多个slave节点,就可以继续增大集群的高并发能力。同时每个master之间通过ping监测彼此健康状态,就类似于哨兵模式了。当客户端请求可以访问集群任意节点,最终都会被转发到正确节点
Redis分片集群中数据是怎么存储和读取的?
嗯~,在redis集群中是这样的
Redis 集群引入了哈希槽的概念,有 16384 个哈希槽,集群中每个主节点绑定了一定范围的哈希槽范围, key通过 CRC16 校验后对 16384 取模来决定放置哪个槽,通过槽找到对应的节点进行存储。取值的逻辑是一样的
redis做为缓存,mysql的数据如何与redis进行同步呢?(双写一致性)
大部分业务数据同步可以有一定的延时
canal组件不需要更改业务代码,canal服务伪装成mysql的一个从节点,canal会读取binlog日志,去更新缓存。
redis做为缓存,数据的持久化是怎么做的?
在Redis中提供了两种数据持久化的方式1、RDB 2、AOF
这两种持久化方式有什么区别呢?
RDB是一个快照文件,它是把redis内存存储的数据写到磁盘上,当redis实例宕机恢复数据的时候,方便从RDB的快照文件中恢复数据。
AOF的含义是追加文件,当redis操作写命令的时候,都会存储这个文件中,当redis实例宕机恢复数据的时候,会从这个文件中再次执行一遍命令来恢复数据
这两种方式,哪种恢复的比较快呢?
RDB因为是二进制文件,在保存的时候体积也是比较小的,它恢复的比较快,但是它有可能会丢数据,我们通常在项目中也会使用AOF来恢复数据,虽然AOF恢复的速度慢一些,但是它丢数据的风险要小很多,在AOF文件中可以设置刷盘策略,我们当时设置的就是每秒批量写入一次命令
Redis的数据过期策略有哪些 ?
嗯~,在redis中提供了两种数据过期删除策略
第一种是惰性删除,在设置该key过期时间后,我们不去管它,当需要该key时,我们在检查其是否过期,如果过期,我们就删掉它,反之返回该key。
第二种是 定期删除,就是说每隔一段时间,我们就对一些key进行检查,删除里面过期的key
定期清理的两种模式
- SLOW模式是定时任务,执行频率默认为10hz,每次不超过25ms,以通过修改配置文件redis.conf 的 hz 选项来调整这个次数
- FAST模式执行频率不固定,每次事件循环会尝试执行,但两次间隔不低于2ms,每次耗时不超过1ms
Redis的过期删除策略惰性删除 + 定期删除两种策略进行配合使用。
Redis的数据淘汰策略有哪些 ?
嗯,这个在redis中提供了很多种,默认是noeviction,不删除任何数据,内部不足直接报错
是可以在redis的配置文件中进行设置的,里面有两个非常重要的概念,一个是LRU,另外一个是LFU
LRU的意思就是最少最近使用,用当前时间减去最后一次访问时间,这个值越大则淘汰优先级越高。
LFU的意思是最少频率使用。会统计每个key的访问频率,值越小淘汰优先级越高
我们在项目设置的allkeys-lru,挑选最近最少使用的数据淘汰,把一些经常访问的key留在redis中
数据库有1000万数据 ,Redis只能缓存20w数据, 如何保证Redis中的数据都是热点数据 ?
嗯,我想一下~~
可以使用 allkeys-lru (挑选最近最少使用的数据淘汰)淘汰策略,那留下来的都是经常访问的热点数据
Redis的内存用完了会发生什么?
嗯~,这个要看redis的数据淘汰策略是什么,如果是默认的配置,redis内存用完以后则直接报错。我们当时设置的 allkeys-lru 策略。把最近最常访问的数据留在缓存中。
Redis分布式锁如何实现 ?
嗯,在redis中提供了一个命令setnx(SET if not exists)
由于redis的单线程的,用了命令之后,只能有一个客户端对某一个key设置值,在没有过期或删除key的时候是其他客户端是不能设置这个key的
好的,那你如何控制Redis实现分布式锁有效时长呢?
嗯,的确,redis的setnx指令不好控制这个问题,我们当时采用的redis的一个框架redisson实现的。
在redisson中需要手动加锁,并且可以控制锁的失效时间和等待时间,当锁住的一个业务还没有执行完成的时候,在redisson中引入了一个看门狗机制,就是说每隔一段时间就检查当前业务是否还持有锁,如果持有就增加加锁的持有时间,当业务执行完成之后需要使用释放锁就可以了
还有一个好处就是,在高并发下,一个业务有可能会执行很快,先客户1持有锁的时候,客户2来了以后并不会马上拒绝,它会自旋不断尝试获取锁,如果客户1释放之后,客户2就可以马上持有锁,性能也得到了提升。
好的,redisson实现的分布式锁是可重入的吗?
嗯,是可以重入的。这样做是为了避免死锁的产生。这个重入其实在内部就是判断是否是当前线程持有的锁,如果是当前线程持有的锁就会计数,如果释放锁就会在计算上减一。在存储数据的时候采用的hash结构,大key可以按照自己的业务进行定制,其中小key是当前线程的唯一标识,value是当前线程重入的次数
redisson实现的分布式锁能解决主从一致性的问题吗
这个是不能的,比如,当线程1加锁成功后,master节点数据会异步复制到slave节点,此时当前持有Redis锁的master节点宕机,slave节点被提升为新的master节点,假如现在来了一个线程2,再次加锁,会在新的master节点上加锁成功,这个时候就会出现两个节点同时持有一把锁的问题。
我们可以利用redisson提供的红锁来解决这个问题,它的主要作用是,不能只在一个redis实例上创建锁,应该是在多个redis实例上创建锁,并且要求在大多数redis节点上都成功创建锁,红锁中要求是redis的节点数量要过半。这样就能避免线程1加锁成功后master节点宕机导致线程2成功加锁到新的master节点上的问题了。
但是,如果使用了红锁,因为需要同时在多个节点上都添加锁,性能就变的很低了,并且运维维护成本也非常高,所以,我们一般在项目中也不会直接使用红锁,并且官方也暂时废弃了这个红锁
好的,如果业务非要保证数据的强一致性,这个该怎么解决呢?
候选人嗯~,redis本身就是支持高可用的,做到强一致性,就非常影响性能,所以,如果有强一致性要求高的业务,建议使用zookeeper实现的分布式锁,它是可以保证强一致性的。
Redis集群有哪些方案, 知道嘛 ?
嗯~~,在Redis中提供的集群方案总共有三种主从复制、哨兵模式、Redis分片集群
那你来介绍一下主从同步
嗯,是这样的,单节点Redis的并发能力是有上限的,要进一步提高Redis的并发能力,可以搭建主从集群,实现读写分离。一般都是一主多从,主节点负责写数据,从节点负责读数据,主节点写入数据之后,需要把数据同步到从节点中
能说一下,主从同步数据的流程
嗯~~,好!主从同步分为了两个阶段,一个是全量同步,一个是增量同步
全量同步是指从节点第一次与主节点建立连接的时候使用全量同步,流程是这样的
第一从节点请求主节点同步数据,其中从节点会携带自己的replication id和offset偏移量。
第二主节点判断是否是第一次请求,主要判断的依据就是,主节点与从节点是否是同一个replication id,如果不是,就说明是第一次同步,那主节点就会把自己的replication id和offset发送给从节点,让从节点与主节点的信息保持一致。
第三在同时主节点会执行bgsave,生成rdb文件后,发送给从节点去执行,从节点先把自己的数据清空,然后执行主节点发送过来的rdb文件,这样就保持了一致
当然,如果在rdb生成执行期间,依然有请求到了主节点,而主节点会以命令的方式记录到缓冲区,缓冲区是一个日志文件,最后把这个日志文件发送给从节点,这样就能保证主节点与从节点完全一致了,后期再同步数据的时候,都是依赖于这个日志文件,这个就是全量同步
增量同步指的是,当从节点服务重启之后,数据就不一致了,所以这个时候,从节点会请求主节点同步数据,主节点还是判断不是第一次请求,不是第一次就获取从节点的offset值,然后主节点从命令日志中获取offset值之后的数据,发送给从节点进行数据同步
怎么保证Redis的高并发高可用
首先可以搭建主从集群,再加上使用redis中的哨兵模式,哨兵模式可以实现主从集群的自动故障恢复,里面就包含了对主从服务的监控、自动故障恢复、通知;如果master故障,Sentinel会将一个slave提升为master。当故障实例恢复后也以新的master为主;同时Sentinel也充当Redis客户端的服务发现来源,当集群发生故障转移时,会将最新信息推送给Redis的客户端,所以一般项目都会采用哨兵的模式来保证redis的高并发高可用
你们使用redis是单点还是集群,哪种集群
嗯!,我们当时使用的是主从(1主1从)加哨兵。一般单节点不超过10G内存,如果Redis内存不足则可以给不同服务分配独立的Redis主从节点。尽量不做分片集群。因为集群维护起来比较麻烦,并且集群之间的心跳检测和数据通信会消耗大量的网络带宽,也没有办法使用lua脚本和事务
redis集群脑裂,该怎么解决呢?
嗯! 这个在项目很少见,不过脑裂的问题是这样的,我们现在用的是redis的哨兵模式集群的
有的时候由于网络等原因可能会出现脑裂的情况,就是说,由于redis master节点和redis salve节点和sentinel处于不同的网络分区,使得sentinel没有能够心跳感知到master,所以通过选举的方式提升了一个salve为master,这样就存在了两个master,就像大脑分裂了一样,这样会导致客户端还在old master那里写入数据,新节点无法同步数据,当网络恢复后,sentinel会将old master降为salve,这时再从新master同步数据,这会导致old master中的大量数据丢失。
关于解决的话,我记得在redis的配置中可以设置第一可以设置最少的salve节点个数,比如设置至少要有一个从节点才能同步数据,第二个可以设置主从数据复制和同步的延迟时间,达不到要求就拒绝请求,就可以避免大量的数据丢失
redis的分片集群有什么作用
分片集群主要解决的是,海量数据存储的问题,集群中有多个master,每个master保存不同数据,并且还可以给每个master设置多个slave节点,就可以继续增大集群的高并发能力。同时每个master之间通过ping监测彼此健康状态,就类似于哨兵模式了。当客户端请求可以访问集群任意节点,最终都会被转发到正确节点
Redis分片集群中数据是怎么存储和读取的?
嗯~,在redis集群中是这样的
Redis 集群引入了哈希槽的概念,有 16384 个哈希槽,集群中每个主节点绑定了一定范围的哈希槽范围, key通过 CRC16 校验后对 16384 取模来决定放置哪个槽,通过槽找到对应的节点进行存储。
取值的逻辑是一样的
Redis是单线程的,但是为什么还那么快?
1、完全基于内存的,C语言编写
2、采用单线程,避免不必要的上下文切换可竞争条件
3、使用多路I/O复用模型,非阻塞IO
例如bgsave 和 bgrewriteaof 都是在后台执行操作,不影响主线程的正常使用,不会产生阻塞
能解释一下I/O多路复用模型?
嗯~~,I/O多路复用是指利用单个线程来同时监听多个Socket ,并在某个Socket可读、可写时得到通知,从而避免无效的等待,充分利用CPU资源。目前的I/O多路复用都是采用的epoll模式实现,它会在通知用户进程Socket就绪的同时,把已就绪的Socket写入用户空间,不需要挨个遍历Socket来判断是否就绪,提升了性能。
其中Redis的网络模型就是使用I/O多路复用结合事件的处理器来应对多个Socket请求,比如,提供了连接应答处理器、命令回复处理器,命令请求处理器;
在Redis6.0之后,为了提升更好的性能,在命令回复处理器使用了多线程来处理回复事件,在命令请求处理器中,将命令的转换使用了多线程,增加命令转换速度,在命令执行的时候,依然是单线程
================================================SQL
主键不能有重复,不允许为空。最多有一个主键。
外键是另一表的主键,外键可重复,可为null。可有多个(不分库分表,并发量低可以用外键)
为什么不推荐使用外键与级联?
分库分表时外键失效,
每次增、删 、改 都必须增、删 、改外键那个表的主键,会导致开发麻烦,也降低sql执行速度。
存储过程是SQL 语句的集合(类似函数方法),方便下一次调用。使用存储过程比单纯 SQL 语句执行要快,因为是预编译过的。
触发器(Trigger)可以在对表「增删改」 前或后被触发,自动执行事先写好的 SQL 代码
rollback segment(回滚空间)
DDL语句(Data Definition Language)
操作数据库对象如数据库和表的结构和字段。存储过程,视图的语句,不能回滚,不触发 trigger,命令如下
CREATE创建表 ,库、
drop: 删表 ,库
ALTER:改表名和字段
truncate (清空表中所有数据) : truncate table 表名 ,自增长 id 、索引恢复到初始大小、不产生数据库日志;
DML (Data Manipulation Language)语句
增删改表内数据的语句,能回滚。如insert,
操作会放到 rollback segment 中,事务提交之后才生效);
delete 语句
操作除数据库对象都用这些DML的关键字如变量
DQL Query
查询表内数据的语句,select
DCL Controller
增删改用户,管理用户增删改查权限的语句
SQL 语句不区分大小写,忽略空格。
MySQL
数据库3 范式:列不可再分(原子性)。非主键列必须依赖主键列(联合主键列的所有列)。非主键列不能依赖另一个非主键列。(一表一种实体)。但可以字段冗余避免连表查。
主键:不能有重复,不允许为空。只能有一个主键。
外键:是另一表的主键,可重复,可以是空值。可以有多个(不分库分表,并发量不高可以用外键)
存储引擎
存储引擎就是存储数据,建立索引,操作数据的实现方式,存储引擎是基于表的。MySQL5.5之后默认使用的存储引擎是InnoDB,支持事务,支持锁包括表锁和行锁,还支持外键,通过redolog日志实现崩溃恢复。MyISAM 就支持一个表锁。memory数据存在内存,服务器宕机数据会丢失。
事务
事务是啥?
一堆sql语句,要么提交(全部成功)要么回滚(全部失败)。
事务特性?
ACID,分别是原子性、一致性、隔离性、持久性;我举个例子A向B转账500,A扣除500元,B增加500元,原子操作体现在要么都成功,要么都失败。数据要一致,A扣了500,B必须增加500,隔离性体现在A向B转账,不能受其他事务干扰。持久性体现在事务提交后,要把数据持久化
并发事务问题:
脏读 :A事务读到了B事务还没提交的更改(如果B事务不提交而是回滚或提交前还改,那A读到的就是脏数据)
修改丢失: A事务的修改(赋值)覆盖了B事务的修改
不可重复读: 一个事务里两次读同一条记录之间,别的事务修改了这条记录。两次读到的字段数据不一样(侧重 修改一条记录)
幻读:一个事务里两次读多行数据之间,别的事务增或删了符合查询条件的记录(第二次读比第一次读,多了记录或少了记录,好像 发生了幻觉。侧重 增删结果集的记录)
解决不可重复读只用锁住满足条件的行,解决幻读要锁表/MVCC + 区间锁
事务四个隔离级别(能解决并发事务问题)
读未提交 能读事务还没提交的数据修改,会导致脏读、不可重复读、幻读。
读已提交 只能读事务已提交的数据,只能阻止脏读。会导致不可重复读或幻读。
可重复读 每次读同一字段的值都一样,除非被自己修改,能阻止脏读和不可重复读,会导致幻读。
串行化 所有的事务串行执行、不会产生并发事务问题。但性能低。完全服从 ACID 的隔离级别。
隔离级别如何实现的(排他锁和Mvcc)
行锁仅对一行或多行记录加锁
控制并发事务锁(悲观) 和 MVCC(乐观)。
读锁是事务在读时要获取的共享锁,多个事务能同时获取。会阻塞写的线程
写锁只允许一个事务获取。会阻塞其它读和写的线程。(写锁是排他锁,所以给一条记录加写锁后不能再加别的锁)
事务中的隔离性是如何保证的呢?(你解释一下MVCC)
是由锁和mvcc这两个实现的。
mvcc(Multi-Version Concurrency Control)是多版本并发控制。
指维护一个数据的多个版本,使得读写操作没有冲突。是用数据库记录的隐藏字段、undo log日志、readView(读视图)实现
每个表都有三个隐藏字段,一个是trx_id(事务id),记录插入或最后一次修改这条记录的事务id,是自增的;第二个是roll_pointer(回滚指针)字段,指向这条记录上一个版本。
undo log回滚日志,事务提交后,事务中插入语句产生的回滚日志会被删除,而更新、删除语句产生的回滚日志不会被删,因为mvcc版本访问需要。undo log中的版本链表:事务操作某行记录前,先把这行记录存到undo log,通过roll_pointer字段形成一个旧版本单链表,链表头节点是最新的旧纪录,尾是最老的旧纪录也就是刚插入时的。
readView解决的是一个事务查询选择版本的问题,在内部定义了一些匹配规则和当前的一些事务id判断该访问那个版本的数据,不同的隔离级别快照读是不一样的,最终的访问的结果不一样。如果是rc隔离级别,每一次执行快照读时生成ReadView,如果是rr隔离级别仅在事务中第一次执行快照读时生成ReadView,面复用
隔离级别基于:
行级锁仅对一行或多行记录加锁
读锁:事务读时获取的共享锁,允许多个事务持有。
写锁:只能一个事务持有。记录被加写锁不能再加别的锁(排他)
next - key lock是记录锁(Record Lock)和间隙锁(Gap Lock)的组合。为了解决在可重复读隔离级别下幻读问题
- next - key lock是MySQL而引入的一种锁机制。它
- 记录锁锁定的是某一条具体的记录,而间隙锁锁定的是一个范围(两个索引值之间的间隙)。next - key lock可以同时锁定记录和记录之间的间隙,从而有效地防止其他事务在这个范围内插入新的记录,避免了幻读的发生。
MVCC(多版本并发控制)
- MVCC是一种并发控制的方法,它在数据库中通过保存数据的多个版本来实现不同事务对同一数据的并发访问。
- 在MVCC机制下,每个事务在开始时,会创建此时数据库里数据的快照。事务执行过程中,读数据从快照里读,而写会创建新的版本。这样,不同的事务可以在不同的版本上进行操作,互不干扰,提高了并发性能。
日志
mysql的4个日志
第一个binLog(二进制日志)记录已提交事务里的DDL语句(增删改表结构)和DML语句(增删改表中记录),用于主从复制。第二个relyLog(中继日志)主从复制时,从库将主库的二进制日志内容复制到中继日志,然后执行里面的操作。第三个慢查询日志,记录执行耗时大于设定的sql语句。第四个Error Log (错误日志)记录MySQL 服务器启动、运行和停止过程中遇到的错误信息,
mysql的存储引擎innoDB的2个日志
redo log是重做日志,记录物理层面数据库对数据页的所有修改操作,在服务宕机重启时用来恢复数据,保证事务的持久性
undo log 是回滚日志,记录未提交事务的逆操作sql语句,用于事务回滚,事务执行一半数据库宕机,再启动时通过查回滚日志来回 滚。保证了事务原子性和一致性
扩展
MySQL 实例挂了或宕机了,innoDB会使用redo log恢复数据,保证数据的持久性与完整性,用undo log 回滚没执行完的事务。
数据库的数据备份、主备、主主、主从都依靠binlog来同步数据,保证数据一致性。
索引
是一种能快速查数据的有序的数据结构
优点:查数据快(因为减少了检索的数据量, 减少了 IO 次数),通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。
缺点:1、增删改表中有索引的数据时,索引也要改,会增加 SQL 执行时间。2、存储索引用会耗费物理空间。
索引分类
索引的底层数据结构?
MySQL的默认存储引擎InnoDB用B+树的数据结构来存索引,因为B+树阶数多,路径短,时间复杂度低。第二个非叶子节点只存指针,叶子节点存数据,磁盘io少。第三是B+树便于区间查询和扫库,因为叶子节点组成了一个双向链表
B树和B+树的区别?
因为B树所有子节点都会存数据,而B+树的只有叶子节点存数据(非叶子节点存数据的地址),所以在查询时B+树查找速度更快
什么是聚集索引什么是非聚集索引 ?
聚集索引是B+树的叶子节点保存了主键字段值和整行数据,有且只有一个,主键索引都是聚集索引,没主键会有隐藏的聚集索引。
非聚集索引是B+树的叶子节点只保存索引字段值和表的主键值(可以是联合主键), 一般自定义索引是非聚集索引。
覆盖索引 ? (走聚集索引或只走二级索引不回表)
select查询的列,在查询条件里的索引中能全部找到,否则会回表查询。
select * where id = xx; id = xx (走聚集索引)
(给name字段建了索引)
select id name where name = xx 是。
select id, name, age where name = xx ; 不是,name = xx(先走二级索引再走聚集索引)
如果按照二级索引查询数据的时候,尽量在返回的列中都包含添加索引的字段防止回表查询
回表查询?
先走二级索引找到对应的主键值,然后再通过主键值去聚集索引找对应的整行数据。
MYSQL超大分页怎么处理 ?
表数据量大时,我们用了limit分页查询,并且需要对数据进行排序,效率低,可以用覆盖索引和子查询来解决。
先分页查询数据按id字段字段排序,确定了id之后,再用子查询来过滤,只查询这个id列表中的数据就可以了
因为查询id的时候,走的覆盖索引,所以效率可以提升很多
索引创建原则有哪些?
大前提,就是表查询频繁且数据超10万,才创建索引
添加索引的字段是查询频繁的字段,像作为查询条件,排序条件或分组条件的字段。(回表概率大)
给经常当查询条件的字段组合创建联合索引。尽量创建联合索引,且使用越频繁越靠前(覆盖索引概率大)。
控制索引字段数量,增删改的速度变慢
创建索引的字段内容长,用前缀索引,
重复值少的字段,这样能建立唯一索引(树阶数多路径短)。
索引会失效 ?
失效指不走聚集索引或二级索引也不回表(先二后聚)。
判断这条sql是否索引失效 使用explain看执行计划
查询条件不遵循最左前缀原则(查询条件必须联合索引的第一个字段开头,且不跳过(如果跳过只有前边的字段的索引有效)),8 版本有就行
索引字段运算(比如+-*/)或使用了内置函数SubString、LENGTH。
做了隐式类型转换(比如Varchar不加单引号)
范围查询(>、<、between、like)右边的列使用不了索引 (部分失效)
%号在前面的模糊查询。(只在后面属于前缀索引)
or前后存在非索引列会全表扫描
索引列使用IS NULL,IS NOT NULL,因为索引是不索引null值
索引列使用NOT,!=时
mysql认为全表扫描更快时
or换Union
sql的优化
部署主从数据库,分担访问压力
设置一个主库一个从库,主库写从库读 ,主库同步数据到从库(主从复制,读写分离)
建表时
数值类型:选择合适字段类型,像tinyint、int 、bigint这些类型,根据实际情况节省空间。
字符串类型,char浪费空间但查询效率高和varchar不浪费空间效率低,text类型
分库分表
sql语句优化
sql语句
插入大量记录用批量插入,大量记录用load指令
update时,更新条件用索引字段且索引不能失效,不然会全表扫描默认的行锁变表锁,降低并发时性能。
联表查询,能用innerjoin ,不要用外连接,用外连接要小表驱动大表(左连接左边的就是驱动表,因为关联字段值肯定小表的更少,能减少表连接次数)
索引优化
覆盖索引
少用select *更容易覆盖索引还能减少IO,防止回表查询。
防止索引失效
参考上
数据库优化
1.索引优化
2.sql优化,查询优化**避免SELECT ***,用JOIN替代子查询,使用 LIMIT
限制返回的行数减少数据处理量
3.表结构优化,通过数据库规范化减少数据冗余,反规范化可以提高查询性能,减少JOIN操作例如,将经常一起查询的数据存储在同一个表中。
4. 数据库配置优化
- 调整缓冲区:
- 根据系统内存调整数据库的缓冲区大小(如InnoDB Buffer Pool、MySQL的query cache等),提高缓存命中率。
- 优化内存分配:
- 调整数据库的内存分配,使其更适合当前系统负载。例如,增加连接池的大小,减少连接创建的开销。
5. 事务优化
- 减少事务范围:
- 尽量减少事务的范围,只在必要时开启事务,减少锁竞争和事务日志的开销。
- 使用合适的事务隔离级别:
- 根据业务需求选择合适的事务隔离级别(如READ COMMITTED、REPEATABLE READ等),平衡数据一致性和性能。
6. 批处理优化
-
批量操作
:
- 使用批量插入、更新和删除操作,减少与数据库的交互次数,提高性能。
- 例如,使用
INSERT INTO table (column1, column2) VALUES (?, ?), (?, ?), ...;
进行批量插入。
7. 数据库设计优化
- 分区表:
- 对大数据表进行分区,可以提高查询性能,减少扫描范围。
- 例如,对日期列进行分区,查询时可以跳过不相关的分区。
- 使用合适的数据类型:
- 选择合适的数据类型可以减少存储空间,提高查询性能。例如,使用
TINYINT
而不是INT
来存储布尔值。
- 选择合适的数据类型可以减少存储空间,提高查询性能。例如,使用
8. 定期维护
- 分析和优化表:
- 定期执行
ANALYZE TABLE
和OPTIMIZE TABLE
命令,更新表的统计信息,优化存储结构。
- 定期执行
- 清理无用数据:
- 定期清理数据库中的无用数据,减少数据量,提高查询性能。
9. 使用连接池
-
连接池
:
- 使用数据库连接池减少连接创建和销毁的开销,提高系统性能。
DDL create、out(改)和drop表、索引、视图等对象。
DML(Data Manipulation Language,数据操纵语言) 对数据进行查询和修改(如 SELECT、INSERT、UPDATE 和 DELETE)。
MySQL定位慢查询?
看监控系统Skywalking 的报表,找到响应慢的接口,再分析接口里哪部分慢,这里可以看到SQL的执行时间。
开启慢日志(在MySQL的my.cnf文件中),设置超时间,记录SQL到日志文件中。
看表象:页面加载慢、接口压测响应时间长(超过1s)。
慢的原因
聚合查询 创建临时表
多表查询 优化语句
表数据量过大查询 添加索引
深度分页查询
SQL语句执行很慢, 如何分析
mysql自动的执行计划explain来去查看这条sql的执行情况,比如在这里面可以通过key和key_len检查是否命中了索引,如果本身已经添加了索引,也可以判断索引是否有失效的情况,第二个,可以通过type字段查看sql是否有进一步的优化空间,是否存在全索引扫描或全盘扫描,第三个可以通过extra建议来判断,是否出现了回表的情况,如果出现了,可以尝试添加索引或修改返回字段来修复。
MySQL主从复制原理
主库提交事务时,往binLog(归档日志)记录(DDL语句和 DML语句),从库读取主库的Binlog ,写入从库的中继日志 Relay Log 。从库执行中 继日志里的事务,就能同步主库的数据
用过MySQL的分库分表吗?
垂直分库,一个微服务一个数据库。
水平分库:一个数据库,很多表的数据量大时高并发查询速度慢,可以分几个Mysql节点。用mycat来作为数据库中间件。插入和查找记录时设置路由规则如按照记录 id对节点数量取模或根据id所在区间的方式选节点存取。让各个数据库分摊存储和读取的压力,提高了查询速度、系统的稳定性和可用性。
垂直分表,按列拆分为多张表,不常用字段(详情)单放在一张表(冷热分离),text,blob等大字段单放一张表。提高查询速度。
水平分表,单表超1千万或20G,,id取模选节点存取。解决单表数据量大增删改速度慢,减少锁表几率,io效率增加。一个库里拆分多个表。
拆分到多个库里等于水平 分库,会产生分布式事务(多节点增删改可能不会全部成功),跨节点连表查询、分页、排序。主键不能用自增在代码里生成。用Sharding- Sphear或mycat解决
锁
锁分全局(数据库中所有表)、表级锁、行锁
全局锁就是对整个数据库实例加锁,加锁后整个实例就处于只读状态(只能执行DQL语句)。后续的DML的写语句,DDL语句,已经更新操作的事务提交语句都将被阻塞。
用于做全库的逻辑备份,对所有的表进行锁定,从而获取一致性视图,保证数据的完整性。主库备份时如上,从库备份时可指定不加全局锁防止主从数据短暂不一致(能执行主库同步过来的二进制日志(binlog))
表级锁又分三类:表锁、元数据锁(meta data lock,MDL)、意向锁。表锁又分表共享读锁和表独占写锁,表共享读锁可被多个线程持有,期间不可写(本客户和其它客户端不能执行DDL和DML只能执行DQL,本客户端的非DQL直接失败,其它客户端阻塞到释放锁)。表独占写锁只能被1个线程持有,期间不可读和写。(本线程能执行DDL和DML只能执行DQL,其它客户端所有语句阻塞到释放锁)。串行化就是给表加独占写锁。元数据锁,只要不修改表结构,查加MDL共享读锁,增删改加MDL共享写锁。改表结构加MDL排他锁。意向锁,分意向共享锁(IS): 由语句select ... lock in share mode添加 。 与 表锁共享锁(read)兼容,与表锁排他锁(write)互斥。意向排他锁(IX): 由insert、update、delete、select...for update添加 。与表锁共享锁(read)及排他锁(write)都互斥,意向锁之间不会互斥。事务在执行DML操作时,会对涉及的行加行锁,同时给该表加上意向锁,其他客户端给这张表加表锁时,根据该表上所加的意向锁来判定是否可以成功加表锁,而不用逐行判断行锁情况了。一旦事务提交了,意向锁都会自动释放。行级锁,每次操作锁住对应的行数据。锁定粒度最小,发生锁冲突的概率最低,并发度最高。应用在InnoDB存储引擎中,InnoDB的数据是基于索引组织的,行锁是通过给索引上的索引项加锁来实现的,而不是对记录加的锁。对于行级锁,主要分为以下三类:
行级锁:记录锁,有s锁和x锁之分,读写互斥,写写互斥。行锁有共享锁和独占锁,s是共享锁,x是独占锁。间隙锁,只存在于可重复读的隔离级别,目的解决可重复读下的幻读问题比如我锁了3,5这个区间,那么4这个记录就不能被插入了;next-key-lock,临建锁,记录锁和间隙锁的组合,锁定一个范围和记录本身。插入意向锁:一个事务在插入数据时会判断该位置是否已经被其他事务加了间隙锁,如果有会发生阻塞,直到持有锁的事务释放锁为止,在阻塞期间会生成一个插入意向锁,表明要在某个区间插入数据,然后将插入意向锁的状态改为等待状态,等占有锁的事务处理完之后,锁变为正常状态。
mysql是怎么加行级锁的:加锁的对象是索引,加锁的基本单位是next-key lock临键锁,临键锁是前开后闭合,间隙锁是前开后开,能使用记录锁或者间隙锁解决幻读问题的场景下,临键锁会退化成记录锁或者间隙锁,
唯一索引等值查询:在事务中,当记录存在时,这个临键锁会退化成X(互斥)记录锁,当查询记录不存在时,这个锁会退化成间隙锁,使用一条命令可以查询事务执行sql中的锁,在等值查询的时候只使用记录锁就可以解决幻读的情况下会退化。当记录不存在时,查询的id=2,最近的两边的id是(1,5),这个时候会加上一个间隙锁锁住1,5,如果有记录插入2,3,4就会发生阻塞,在这种情况下仅仅使用间隙锁就能解决问题,锁是加在索引上的,不能对不存在的记录加锁,所以只能锁住这个区间。
唯一索引范围查询:扫描时对扫描到的每一个记录都加临建锁,针对大于等于的范围查询,如果等值记录存在会退化为记录锁。针对小于或者小于等于,当这个记录不在表中的时候,前面的都加临键锁,如果时id<6 (1,5,10)最后会在5,10加上间隙锁,如果时小于等于并且这个记录值存在,那么就是两个临键锁(-∞, 1] (1, 5 ] ;小于的范围查询,如果存在,第一个加临键锁,第二个加间隙锁,
非唯一索引等值查询:如果存在两个索引,当加锁的时候,会对两个索引都加锁,但是当主键索引加锁时,只有满足条件才会加锁,。当查询的记录不存在时:二级索引会在两侧挨着的地方加间隙锁,二级索引在值相同的情况下,再安装主键id的顺序存放,如果插入的主键id在间隙锁之前,可以插入成功,如果在间隙锁之间会插入失败。然后就是记录存在的情况,首先对查询到的二级索引加临键锁,然后对主键加记录锁,二级索引后面一个数据加间隙锁,为了锁住后面的id 插入 会加上一个间隙锁。
非唯一索引范围查询:加的锁全都是临键锁,没有加索引的查询也都是全部是临键锁。
死锁:两个事务即使生成的间隙锁的范围是一样的,也不会发生冲突,因为间隙锁目的是为了防止其他事务插入数据,因此间隙锁与间隙锁之间是相互兼容的。
在执行插入语句时,如果插入的记录在其他事务持有间隙锁范围内,插入语句就会被阻塞,因为插入语句在碰到间隙锁时,会生成一个插入意向锁,然后插入意向锁和间隙锁之间是互斥的关系。
如果两个事务分别向对方持有的间隙锁范围内插入一条记录,而插入操作为了获取到插入意向锁,都在等待对方事务的间隙锁释放,于是就造成了循环等待,满足了死锁的四个条件:互斥、占有且等待、不可强占用、循环等待,因此发生了死锁。
表锁主要用于控制整个表的操作。粒度大,适用于大批量操作;行锁主要用于细粒度控制,减少锁冲突,适用于频繁单行操作。
两个update语句处理同一条语句会阻塞,会加行级锁,处理不同范围,不会阻塞,锁住的范围不同。如果范围查询不是索引,会触发全表扫描,都会加上行级锁,会阻塞。
update语句执行流程:首先查询这个id的数据页是否在缓冲池中,在就直接用, 没有就将这个磁盘上的数据页从磁盘读到缓冲池,查询更新前后数据是否一样,不一样就传参个innodb更新数据。开启事务,在更新数据前,记录undolog日志,记录redologbuffer,然后更新记录,提交事务时,redolog更新到磁盘,缓冲池的数据页会用wal技术,选择合适实际更新到磁盘上。当事务提交时,开启事务的两阶段提交,这时的binlog是在缓存中,只有事务统一提交才会刷新到磁盘。然我完成所有操作。
如果语句不走索引可以加forceindex强制走索引。
低频知识
存储过程是SQL 语句的集合(类似函数方法),方便下一次调用。使用存储过程比单纯 SQL 语句执行要快,因为是预编译过的。
触发器(Trigger)可以在对表「增删改」 前或后被触发,自动执行事先写好的 SQL 代码
rollback segment(回滚空间)
drop(丢弃数据): drop table 表名 ,将整个表都丢掉
truncate (清空表中所有数据) : truncate table 表名 ,自增长 id 、索引恢复到初始大小、不产生数据库日志
(drop、truncate属于 DDL(数据定义语言)语句,立即生效,原数据不放到 rollback segment 中,不能回滚,操作不触发 trigger。而 delete 语句是 DML (数据库操作语言)语句,这个操作会放到 rollback segment 中,事务提交之后才生效)
delete(删除数据) : delete from 表名 where 列名=值,删除某一行的数据,不加 where 子句和truncate table 表名一样。产生数据库的blog
insert into对记录 ,delete 对记录 ,select可字段可记录,update 表名 set字段
字段类型简单分为三大类
数值类型整型(TINYINT、SMALLINT、MEDIUMINT、INT 和 BIGINT)、浮点型(FLOAT 和 DOUBLE)、定点型(DECIMAL)
字符串类型CHAR、VARCHAR、TINYTEXT、TEXT、MEDIUMTEXT、LONGTEXT、TINYBLOB、BLOB、MEDIUMBLOB 和 LONGBLOB 等,最常用的是 CHAR 和 VARCHAR。
日期时间类型YEAR、TIME、DATE、DATETIME 和 TIMESTAMP 等
不推荐使用 BLOB 和 TEXT 类型,不能有默认值。
DATETIME 和 TIMESTAMP 的区别是什么?
DATETIME 类型和时区无关,当前会话所设置的时区对应的时间,服务器更换地址或者更换客户端连接时区设置的话,就会导致你从数据库中读出的时间错误
TIMESTAMP 和时区有关,存时区。随服务器更换地址或者更换客户端连接时区设置自动变化。
NULL 和 '' (空字符串)的区别是什么?
NULL 代表一个不确定的值,就算是两个 NULL,它俩也不一定相等且占用空间。
聚合函数例如,SUM、AVG、MIN、MAX 等聚合函数会忽略 NULL 值结果和0值是不一样。 函数COUNT 如果是 *(COUNT(*)),会统计所有记录,即使是 NULL 值;如果是某个字段名(COUNT(列名)),只统计非空值的个数。
''的长度是 0,是不占用空间的
Boolean 类型如何表示?
MySQL 中没有布尔类型,用 TINYINT(1) 类型,可以存储 0 或 1,分别对应 false 或 true。
一、一个 SQL SELECT 语句的完整执行顺序和示例:
FROM 子句:指定数据源(表、视图等)。
WHERE 子句:筛选 FROM 子句中获取的数据。
GROUP BY 子句:将数据按照指定字段分组。
聚和函数计算:对分组后的数据进行聚合计算(如 SUM, AVG, COUNT 等)。
HAVING 子句:对聚合后的分组数据进行筛选。
计算所有的表达式:在 SELECT 子句中计算表达式。
SELECT 子句:选择需要返回的列或计算结果。
ORDER BY 子句:对结果集进行排序。
示例 SQL 语句:
假设我们有一个销售记录表 sales,包含以下字段:
sale_date:销售日期
product_id:产品ID
amount:销售金额
quantity:销售数量
我们想要查询每个产品在每个月的总销售金额和销售数量,并且只保留总销售金额大于 1000 的产品记录,最后按照销售金额降序排列。
SELECT
product_id, -- 选择的字段
EXTRACT(YEAR FROM sale_date) AS sale_year, -- 提取销售年份
EXTRACT(MONTH FROM sale_date) AS sale_month, -- 提取销售月份
SUM(amount) AS total_amount, -- 聚合计算:总销售金额
SUM(quantity) AS total_quantity -- 聚合计算:总销售数量
FROM
sales -- 数据源
WHERE
sale_date BETWEEN '2024-01-01' AND '2024-12-31' -- 筛选条件:在2024年的销售记录
GROUP BY
product_id,
EXTRACT(YEAR FROM sale_date),
EXTRACT(MONTH FROM sale_date) -- 按产品ID、年份和月份分组
HAVING
SUM(amount) > 1000 -- 筛选条件:总销售金额大于1000
ORDER BY
total_amount DESC; -- 按总销售金额降序排列
悲观锁和乐观锁的怎么实现 难度系数:⭐⭐
悲观锁:select...for update是MySQL提供的实现悲观锁的方式。
例如:select price from item where id=100 for update
此时在items表中,id为100的那条数据就被我们锁定了,其它的要执行select price from items where id=100 for update的事务必须等本次事务提交之后才能执行。这样我们可以保证当前的数据不会被其它事务修改。MySQL有个问题是select...for update语句执行中所有扫描过的行都会被锁上,因此在MySQL中用悲观锁务必须确定走了索引,而不是全表扫描,否则将会将整个数据表锁住。
乐观锁:乐观锁相对悲观锁而言,它认为数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则让返回错误信息,让用户决定如何去做。
利用数据版本号(version)机制是乐观锁最常用的一种实现方式。一般通过为数据库表增加一个数字类型的 “version” 字段,当读取数据时,将version字段的值一同读出,数据每更新一次,对此version值+1。当我们提交更新的时候,判断数据库表对应记录的当前版本信息与第一次取出来的version值进行比对,如果数据库表当前版本号与第一次取出来的version值相等,则予以更新,否则认为是过期数据,返回更新失败。
举例:
//1: 查询出商品信息
select (quantity,version) from items where id=100;
//2: 根据商品信息生成订单
insert into orders(id,item_id) values(null,100);
//3: 修改商品的库存
update items set quantity=quantity-1,version=version+1 where id=100 and version=#{version};
---------------------------------
日期类型也要“”包,默认升序是往下变大,
分页起始索引从0开始,0可不写,非那个索引
1对1,是单表垂直分表,
笛卡尔积,不设条件,每行交叉连接右表每行
查询语句顺序:
内连接有隐式的如 from 表1,表2 where e.xx=d.xx,
自连接必须起别名,unin两边字段数量和类型一样。
子查询:1、标量配合<>=!、2、单行在where后配合(字段1,...)=,<>、IN、NOT IN 3、单列配合in,all,any、4、表在from后配合(字段1,...)in或表连接,。可在where、select、from
先写出子查询
视图 给Dql语句起了别名,数据库会保存这个Dql语句不保存数据。插入视图就是插入那个表。
可用from依赖视图,创建视图
可加with xx check option插入数据时递归匹配视图和依赖视图的条件,不符合停止递归。cas向上递归,视图没加checkoption也校验。local向上递归,视图没加checkoption不校验。
视图的更新
视图可更新,视图中的行必须与基础表中的行是一对一的关系。如果视图包含以下任何一项,则该视图不可更新
聚合函数或窗口函数 (SUM()、MIN)、MAX()、COUNT()等)、DISTINCT、GROUP BY、HAVING、UNION 或者 UNION ALL
存储过程是 经过编译并存储在数据库中的一段 SQL 语句的集合,可以接收参数,也可以返回数据,封装,复用,减少网络交互次数
变量:系统变量(全局、会话)、用户变量、局部变量(存储过程里定义的)
系统变量前面加@@,用户变量前面加@
触发器是与表有关的数据库对象,在表被增删改前或后,触发并执行触发器中定义的SQL语句集合。数据库端用于确保数据的完整性
, 日志记录 , 数据校验等操作 。Mysql触发器只支持行级触发,不支持语句级触发,影响了几行执行几次。用new、old代表新老行取出字段值。
比如用户表插入后向用户日志表添加记录(把操作时间,操作的参数存入对应字段)
消息队列
rabbitMq
消息队列的4种模型
点对点模式特点:
每个消息只有一个接收者(Consumer)(即一旦被消费,消息就不再在消息队列中)
发送者和接收者间没有依赖性,发送者发送消息之后,不管有没有接收者在运行,都不会影响到发送者下次发送消息;
接收者在成功接收消息向队列应答成功,消息队列删除消息;
使用队列(Queue)作为消息通信载体;满足生产者与消费者模式,一条消息只能被一个消费者使用,未被消费的消息在队列中保留直到被消费或超时。比如:我们生产者发送 100 条消息的话,两个消费者来消费一般情况下两个消费者会按照消息发送的顺序各自消费一半(也就是你一个我一个的消费。)
发布订阅模式
每个消息可以有多个订阅者;
发布者和订阅者之间有时间上的依赖性。针对某个主题(Topic)的订阅者,它必须创建一个订阅者之后,才能消费发布者的消息。
为了消费消息,订阅者需要提前订阅该角色主题,并保持在线运行;
点对点模型(Queue-Based):
在这种模型中,消息从生产者发送到队列,然后由消费者从队列中接收。每个消息被传递给一个消费者,这种方式确保每个消息仅被处理一次。队列保证了消息的顺序性和持久性,直到消息被成功消费或被确认。
发布/订阅模型(Publish/Subscribe):
在发布/订阅模型中,消息从生产者发布到交换机(Exchange),然后交换机将消息分发给绑定到它的多个队列。每个队列可以有多个消费者,消息会被发送到所有绑定到交换机的队列中。这种模型允许一个消息被多个消费者处理。
路由模型(Routing):
路由模型使用交换机的路由键(Routing Key)来将消息分发到不同的队列。生产者发布消息时,指定一个路由键,交换机根据路由键将消息发送到匹配的队列。常见的路由类型包括直接交换机(Direct Exchange)、主题交换机(Topic Exchange)等。
通配符模型(Wildcard):
在通配符模型中,交换机使用主题(Topic)交换机,可以基于复杂的路由模式将消息分发到多个队列。例如,可以使用 logs.# 这种模式来匹配所有以 logs. 开头的路由键。这样,生产者可以发布带有特定模式的消息,消费者可以通过匹配这些模式来接收感兴趣的消息。
消息队列
优点应用解耦、异步提速、流量削峰(生产者生产速度远大于消费者消费速度)。
缺点,系统可用性降低(MQ 挂掉),系统复杂性提高(要保证消息没有被重复消费、处理消息丢失的情况、保证消息传递的顺序性,一致性问题(我上面讲了消息队 列可以实现异步,提高系统响应速度。但消费者没有正确消费消息就会导致数据不一致)
交换机类型:直连(发消息时指定key,一个交换机能有多个key,根据key绑定1或多个队列,消费者消费某队列。路由键也可以是队列名),扇出(消息发给扇出交换机, 扇出交换机会发给它绑定的所有队列,无视key,发消息时不指定key,不用key绑),主题(类似Direct,主题交换机通过routingkey路由键进行模糊匹配分发,区别 在于 Topic可以进行,Direct是完全配,Topic中,将routingkey通过.来分为多个部分,*代表一个部分,#代表n个部分)。headers交换机 匹配 AMQP 消息的 header(map)于和它绑定的队列,此外 headers 交换器和 direct 交换器完全一致,但性能差,目前几乎用不到了
消费方指定的headers中必须包含一个"x-match"的键
键"x-match"的值有2个
1.x-match =all:所有的键值对都匹配才能接受到消息
2x-match=any:有键值对匹配就能接受到消息
消息可靠性
开启生产者确认机制,如果是交换机没收到,publish_confim类型的nack,如果是队列没收到publish_return类型nack,到队列返回ack。如果消息失败后首先用回 调方法重发,然后记录日志。还可以保存到数据库中用定时任务重发,发送成功后删除表中的数据。
开启交换机,队列的持久化,消息也要持久化。
开启消费者确认机制,消费者处理消息之后,向mq发送ack回执,mq收到之后才删除消息,整合spring有使用自动ack,mq投递消息之后直接删除。
开启当消费者消费失败重试,消费异常,本地重试,设置重试次数,如果消息依然失败,将消息发送到异常交换机,交给人工处理。
1、RabbitMQ-如何保证消息不丢失 (包括传输过程中、队列中、消费者处理失败时)
第一个是开启生产者确认机制,保证消息能发到队列。
如果报错可以
回调方法即时重发,
记录日志,
保存到数据库然后定时重发,成功发送后删除表中的数据
第二个是开启持久化功能,确保消息未消费前在队列中不会丢失,其中的交换机、队列(包括死信队列)、和消息都要做持久化
第三个是开启消费者确认机制为auto,让spring监控lisner代码有没有抛异常来返回ack/nack
开启消费者失败重试,一般设置3次,如果重试3次还没有收到消息,就将失败后的消息投递到异常交换机,交由人工处理
RabbitMQ消息的重复消费问题如何解决的/消息幂等性
重复消费是消费者设置了自动确认机制,消费成功还没给MQ返回确认时,消费者宕机了,重启后会重复消费
给消息加唯一id,消费者接收到消息先根据id向数据库去重表加条记录状态为消费中并设置TTL,插入成功说明第一次消费,继续消费,消费完状态改为成功,消费失败删除记录。如果插入失败判断消费状态是否已成功,就不消费。
因为我们当时处理的支付(订单|业务唯一标识),它有一个业务的唯一标识,我们处理消息时,先到数据库查询一下,这个数据是否存在,如果不存在,说明 没有处理过,这个时候就可以正常处理这个消息了。如果已经存在这个数据了,就说明消息重复消费了,我们就不需要再消费了其实这个就是典型的幂等的问题, 比如,redis分布式锁、数据库的锁都是可以的
消息按顺序消费
同一个队列能确保按发送顺序消费,优先级队列设置顺序
死信:
消费者使用basic.reject或 basic.nack声明消费失败,并且消息的requeue参数设置为false
消息过期
消费者超时没回ack
要投递的队列满了,最早的消息成为死信,如果该队列没配死信交换机就会丢弃信息
RabbitMQ中死信交换机 ? (RabbitMQ延迟队列有了解过嘛)
延迟队列用死信交换机和TTL(消息存活时间)实现的。
发消息时指定TTL,超时未消费就会变成死信,发到队列绑定的一个死信交换机,死信交换机发到它绑定的队列(队列也设置了ttl,取最短)
RabbitMQ中安装一个死信插件,这样更方便一些,我们只需要在声明交互机的时候,指定这个就是死信交换 机,然后在发送消息的时候直接指定超时时间就行了, 相对于死信交换机+TTL要省略了一些步骤
如果有100万消息堆积在MQ , 如何解决 ?
第一:消费者开线程池,多线程消费
第二增加消费者,使用工作队列模式, 设置多个消费者消费同一个队列中的消息
第三用惰性队列,惰性队列的好处
①惰性队列接收到消息后直接存入磁盘而非内存
②消费者要消费消息时才会从磁盘中读取并加载到内存
③支持数百万条的消息存储
RabbitMQ的高可用机制有了解过嘛
搭建是镜像模式集群,使用了3台机器。镜像队列结构是一主多从,所有操作都是主节点完成,然后同步给镜像节点,如果主节点宕机后,镜像节点会替代成新的主节 点,不过在主从同步完成前,主节点就已经宕机,可能出现数据丢失
那出现丢数据怎么解决呢?
用仲裁队列,与镜像队列一样,都是主从模式,支持主从数据同步,主从同步基于Raft协议,强一致。声明队列时指定这个是仲裁队列
Kafka
Kafka是如何保证消息不丢失
嗯,这个保证机制很多,在发送消息到消费者接收消息,在每个阶段都有可能会丢失消息,所以我们解决的话也是从多个方面考虑
第一个是生产者发送消息的时候,可以使用异步回调发送,如果消息发送失败,我们可以通过回调获取失败后的消息信息,可以考虑重试或记录日志,后边再做补偿都是可以的。同时在生产者这边还可以设置消息重试,有的时候是由于网络抖动的原因导致发送不成功,就可以使用重试机制来解决
第二个在broker中消息有可能会丢失,我们可以通过kafka的复制机制来确保消息不丢失,在生产者发送消息的时候,可以设置一个acks,就是确认机制。我们可以设置参数为all,这样的话,当生产者发送消息到了分区之后,不仅仅只在leader分区保存确认,在follwer分区也会保存确认,只有当所有的副本都保存确认以后才算是成功发送了消息,所以,这样设置就很大程度了保证了消息不会在broker丢失
第三个有可能是在消费者端丢失消息,kafka消费消息都是按照offset进行标记消费的,消费者默认是自动按期提交已经消费的偏移量,默认是每隔5s提交一次,如果出现重平衡的情况,可能会重复消费或丢失数据。我们一般都会禁用掉自动提价偏移量,改为手动提交,当消费成功以后再报告给broker消费的位置,这样就可以避免消息丢失和重复消费了
Kafka中消息的重复消费问题如何解决的
kafka消费消息都是按照offset进行标记消费的,消费者默认是自动按期提交已经消费的偏移量,默认是每隔5s提交一次,如果出现重平衡的情况,可能会重复消费或丢失数据。我们一般都会禁用掉自动提价偏移量,改为手动提交,当消费成功以后再报告给broker消费的位置,这样就可以避免消息丢失和重复消费了
为了消息的幂等,我们也可以设置唯一主键来进行区分,或者是加锁,数据库的锁,或者是redis分布式锁,都能解决幂等的问题
Kafka是如何保证消费的顺序性
kafka默认存储和消费消息,是不能保证顺序性的,因为一个topic数据可能存储在不同的分区中,每个分区都有一个按照顺序的存储的偏移量,如果消费者关联了多个分区不能保证顺序性
如果有这样的需求的话,我们是可以解决的,把消息都存储同一个分区下就行了,有两种方式都可以进行设置,第一个是发送消息时指定分区号,第二个是发送消息时按照相同的业务设置相同的key,因为默认情况下分区也是通过key的hashcode值来选择分区的,hash值如果一样的话,分区肯定也是一样的
Kafka的高可用机制有了解过嘛
嗯,主要是有两个层面,第一个是集群,第二个是提供了复制机制
kafka集群指的是由多个broker实例组成,即使某一台宕机,也不耽误其他broker继续对外提供服务
复制机制是可以保证kafka的高可用的,一个topic有多个分区,每个分区有多个副本,有一个leader,其余的是follower,副本存储在不同的broker中;所有的分区副本的内容是都是相同的,如果leader发生故障时,会自动将其中一个follower提升为leader,保证了系统的容错性、高可用性
解释一下复制机制中的ISR
ISR的意思是in-sync replica,就是需要同步复制保存的follower
其中分区副本有很多的follower,分为了两类,一个是ISR,与leader副本同步保存数据,另外一个普通的副本,是异步同步数据,当leader挂掉之后,会优先从ISR副本列表中选取一个作为leader,因为ISR是同步保存数据,数据更加的完整一些,所以优先选择ISR副本列表
Kafka数据清理机制了解过嘛
嗯,了解过~~
Kafka中topic的数据存储在分区上,分区如果文件过大会分段存储segment
每个分段都在磁盘上以索引(xxxx.index)和日志文件(xxxx.log)的形式存储,这样分段的好处是,第一能够减少单个文件内容的大小,查找数据方便,第二方便kafka进行日志清理。
在kafka中提供了两个日志的清理策略
第一,根据消息的保留时间,当消息保存的时间超过了指定的时间,就会触发清理,默认是168小时( 7天)
第二是根据topic存储的数据大小,当topic所占的日志文件大小大于一定的阈值,则开始删除最久的消息。这个默认是关闭的
这两个策略都可以通过kafka的broker中的配置文件进行设置
Kafka中实现高性能的设计有了解过嘛
Kafka 高性能,是多方面协同的结果,包括宏观架构、分布式存储、ISR 数据同步、以及高效的利用磁盘、操作系统特性等。主要体现有这么几点
消息分区不受单台服务器的限制,可以不受限的处理更多的数据
顺序读写磁盘顺序读写,提升读写效率
页缓存把磁盘中的数据缓存到内存中,把对磁盘的访问变为对内存的访问
零拷贝减少上下文切换及数据拷贝
消息压缩减少磁盘IO和网络IO
分批发送将消息打包批量发送,减少网络开销
消息队列
异步处理(减少响应所需时间)。提交订单后,订单数据写入消息队列,不立即返回订单提交成功,在消息队列的订单消费者进程处理完订单后再通过电子邮件或短信通知用户订单成功。
削峰/限流(先将短时间高并发产生的事务消息发送到消息队列中,后端服务再根据自己能力消费这些消息,避免后端服务崩)
解耦需要消费的模块直接去消息队列取消息不需要调用其他模块
jms两消息模式
发布-订阅(用于解耦)生产者发消息,N个消费者订阅消息
点对点订阅(一个消息只有一个消费者)
分布式事务的解决方案之一就是 MQ 事务将消费,处理,生产消息整个过程定义为一个原子操作。
RPC(调用远程计算机上某个服务的方法)
消息队列降低系统耦合性、实现任务异步、流量削峰
rabbitMQ
可靠性 有持久化、传输确认及发布确认保证消息的可靠性。
灵活的路由 在消息进入队列之前,通过交换器来路由消息。对于典型的路由功能,RabbitMQ 己经提供了一些内置的交换器来实现。针对更复杂的路由功能,可以将多个交换器绑定在一起,也可以通过插件机制来实现自己的交换器。
扩展性 多个 RabbitMQ 节点可以组成一个集群,也可以根据实际业务情况动态地扩展集群中节点。
高可用性 队列可以在集群中的机器上设置镜像,部分节点出现问题队列仍然可用。
支持多种协议 RabbitMQ 除了原生支持 AMQP 协议,还支持 STOMP、MQTT 等多种消息中间件协议。
多语言客户端 RabbitMQ 几乎支持所有常用语言,比如 Java、Python、Ruby、PHP、C#、JavaScript 等。
易用的管理界面 RabbitMQ 提供了一个易用的用户界面,使得用户可以监控和管理消息、集群中的节点等。在安装 RabbitMQ 的时候会介绍到,安装好 RabbitMQ 就自带管理界面。
插件机制 RabbitMQ 提供了许多插件,以实现从多方面进行扩展,当然也可以编写自己的插件。感觉这个有点类似 Dubbo 的 SPI 机制
Kafka 是一个分布式流式处理平台
流平台有三个功能消息队列发布和订阅消息流。
持久化消息流消息持久化到磁盘,避免消息丢失。
流式处理平台 消息发布时进行流式处理