栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

java基础知识

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

java基础知识

文章目录
  • 面向对象
    • 什么是面向对象?
    • 封装
    • 继承
    • 多态
  • == 和equals比较
  • hashCode与equals
  • 重载和重写的区别
  • Final
  • spring是什么
  • AOP的理解
  • 谈谈你对IOC的理解
  • 零拷贝
  • RocketMQ 架构设计
  • RocketMq 事务消息原理
  • RockeMq顺序消息消费原理
  • 简述RockerMQ持久化机制
  • RocketMQ如何保证不丢消息
  • 消息发送
  • 消息消费
  • Mysql
    • 索引的基本原理
    • mysql聚族和非聚族索引的区别
    • 业务系统里面的sql耗时,慢查询
    • 最左前缀原则是什么
    • Innodb是如何实现事务的
    • redolog 和 undolog的区别
  • zk的初始化选举和崩溃选举过程
    • 初始化:没有历史数据,5个节点为例
    • 崩溃选举
  • 布隆过滤器
  • zk的数据模型
    • 分布式缓存寻址算法
  • redis的持久化机制
  • Redis单线程为什么这么快
  • redis高可用方案
    • 主从
    • Redis Cluster

面向对象 什么是面向对象?

对比面向过程,是两种不同的处理问题角度
面向过程更注重事情的每一个步骤及顺序,面向对象更注重事情有哪些参与者(对象),及各自需要做什么。

比如:实现一个模块功能
面向过程会将任务拆解成一系列的步骤(函数),1,书写技术文档 2,排期 3,代码编写 4,测试 5,上线

面向对象会拆除技术架构师 java开发工程师 测试工程师 运维工程师
架构师:调研 技术文档编写
开发工程师:代码编写
测试工程师:测试
运维工程师:上线

面向过程比较直接高效,而面向对象更容易复用、扩展和维护

面向对象

封装

封装的意义,在于明确标识出允许外部使用的所有成员函数和数据项
内部细节对外部调用透明,外部调用无需修改或者关心内部实现
eg: javabean的getset方法对外访问,因为属性的赋值智能由javabean本身决定。而不能由外部胡乱修改。

private String name;
public void setName(String name){
this.name = "tuling_"+name;
}
public String getName(){
retrun this.name;
}
该name有自己的命名规则,明显不能由外部直接赋值

继承

继承基类的方法,并做出自己的改变或扩展
子类共性的方法或者属性直接使用父类的,而不需要自己再定义,只需要扩展自己个性化的

多态

基于对象所属类的不同,外部对同一个方法的调用,实际执行的逻辑不同

== 和equals比较

==对比的是栈中的值,基本数据类型是变量值,引用类型是堆中内存对象地址。

equeals: object中默认也是采用==比较,通常会重写。

  • Object:
public boolean equals(Object obj) {
return (this == obj);
}
  • String
    String类中被重写的equals()方法其实是比较两个字符串的内容。
 public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }
public static void main(String[] args)  {
        String aa="aa";
        String a2=new String("aa");
        String a3=a2;
        System.out.println(aa==a2); //false
        System.out.println(aa==a3); //false
        System.out.println(a2==a3); //true

        System.out.println(aa.equals(a2));//true
        System.out.println(aa.equals(a3));//true
        System.out.println(a2.equals(a3)); //true
}
hashCode与equals

hashCode()的作用是获取哈希码(散列码),实际返回一个int整数。
作用是确定该对象在哈希表中的索引位置。hashCode()定义在JDK的Object.java中,java中的任何类都包含有hashCode()函数。
散列表存储的键值对(key-value),它的特点是:能根据键快速检索出对应的value,这其中就用到了散列码。

为什么要有hashCode:
以“HashSet如何检查重复”为例子来说明为什么要有hashCode。
对象加入HashSet时,HashSet会先计算出对象的HashCode值来判断对象加入的位置,看该位置时候有值,如果没有,HashSet会假设对象没有重复出现。但是如果发现有值,这时会调用equals()方法来检查两个对象是否真的相同,HashSet就不会让其加入操作成功。
如果不同的话,就会重新散列到其他位置。这样就大大减少了equals的次数,相应就大大提高了执行速度。

  • 如果两个对象相等,则hashcode一定也是相等
  • 两个对象相等,对两个对象分别调用equals方法都返回true。
  • 两个对象有相同的hashcode的值,他们不一定是相等的
  • 因此equals方法被覆盖过,则hashCode方法也必须被覆盖。
  • hashCode()的默认行为是堆上的对象产生独特值。如果没有重写hashCode(),则该class的两个对象无论如何都不会相等。
重载和重写的区别

重载:发生在同一个类中,方法名必须相同,参数类型不同,个数不同、顺序不同,方法返回值和访问修饰符可以不同,发生在编译时。
重写:发生在父子类中,方法名,参数列表必须相同,返回值范围小于等于父类,访问修饰符范围大于等于父类;如果父类方法修饰符为private则子类就不能重写该方法。

Final

最终的

  • 修饰类:表示类不可被继承
  • 修饰方法:表示方法不可被子类覆盖,但是可以重载。
  • 修饰变量:表示变量一旦被赋值就不可以更改它的值。
spring是什么

轻量级的开源J2EE框架。它是一个容器框架,用来装javabean(java对象)。
Spring是一个轻量级的控制翻转IOC和面向切面AOP的容器架构

  • 从大小与开销两方面而言Spring都是轻量级的。
  • 通过控制翻转(IOC)的技术达到松耦合的目的。
  • 提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统服务进行内聚性的开发
  • 包含并管理应用对象(Bean)的配置和生命周期,这个意义上是一个框架。
  • 将简单的组件配置、组合成为复杂的应用、这个意义上是一个框架。
AOP的理解

系统是由许多不同的组件组成的,每一个组件各负责一块特定的功能。除了实现自身的核心功能外,这些组件还经常承担着额外的职责。例如日志、事务管理和安全这样的核心服务经常融入到自身具有核心业务逻辑的组件中去。这些系统服务经常称为横切关注点,因为他们会跨越系统的多个组件。

当我们需要分散的对象引入公共行为的时候,OOP则显的无能为力。也就是说,OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。

日志代码往往水平的散步在所有的对象层次中,而它所散列到的对象的核心功能毫无关系。
在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。

AOP:将程序中的交叉业务逻辑(比如安全、日志、事务等),封装成一个切面,然后注入到目标对象(具体业务逻辑)中去。AOP可以对某个对象或某些对象的功能进行增强,比如对象中的方法进行增强,可以执行某个方法之前额外的做一些事情,在某个方法执行之后额外做的一些事情。

谈谈你对IOC的理解

容器概念、控制翻转、依赖注入
IOC容器:实际上就是个map(key,value),里面存的各种对象(在xml里配置的bean节点、@repository@service@controller@compoment),在项目启动的时候会读取配置文件里面的bean节点,根据全限定类目使用反射创建对象放到map里、扫描到有上述注解的类还是通过反射创建对象放到map里。

这个时候map里就有各种对象了,接下来我们在代码里需要用到里面的对象时,再通过DI注入(autowired,resource等注解,xml里bean节点内的ref属性,项目启动的时候会读取xml节点ref属性根据id注入,也会扫描这些注解,根据类型或id注入,id就是对象名)

  • 控制反转
    没有引入IOC容器之前,对象A依赖于对象B,那么对象A在初始化或者运行到某一点的时候,自己必须主动去创建对象B或者使用已经创建的对象B。无论是创建还是使用对象B,控制权都在自己手上。

引入IOC容器之后,对象A与对象B之间失去了直接联系,当对象A运行到需要对象B的时候,IOC容器会主动创建一个对象B注入到对象A需要的地方。

通过前后对比,不难看出来:对象A获得依赖对象B的过程,由主动行为变成了被动行为,控制权颠倒过来了,这就是“控制反转”这个名词的由来。

全部对象的控制权全部上缴给“第三方”IOC容器,所以,IOC容器成了整个系统的关键核心,它起到了一种类似“粘合剂”的作用,把系统中的所有对象粘合在一起发挥作用,如果没有这个ioc,对象与对象之间会彼此失去联系,这就是有人把IOC容器比喻成“粘合剂”的由来。

  • 依赖注入
    获得依赖对象的过程被反转了。控制被反转之后,获得依赖对象的过程由自身管理变成为了由IOC容器主动注入。依赖注入实现了IOC的方法,就是由IOC容器在运行期间,动态地将某种依赖关系注入到对象之中。
零拷贝

零拷贝指的是,应用程序在需要把内核中的一块区域数据转移到另外一块区域去时,不需要经过先复制到用户空间,再转移到目标内核区域去了,而直接实现转移。


RocketMQ 架构设计

RocketMq 事务消息原理 RockeMq顺序消息消费原理

默认是不能保证的,需要程序保证发送和消费的是同一个queue,多线程消费也无法保证
发送顺序:发送端自己业务逻辑保证先后,发往一个固定的queue,生产者可以在消费体上设置消息的顺序
发送者实现MessageQueueSeletor接口,选择一个queue进行发送,也可使用rocketmq提供的默认实现。
SelectMessageQueueByHash:按参数的hashcode与可选队列进行求余选择
SelectMessageQueueByRandom:随机选择

mq:queue本身就是顺序追加写,只需保证一个队列同一时间只有一个consumer消费,通过加锁实现,consumer上的顺序消费有一个定时任务,每隔一定时间向broker发送请求延长锁定。

消费端:
pull模式:消费者需要自己维护需要拉取queue,一次拉取的消息都是顺序的,需要消费端自己保证顺序消费。
push模式:消费实例实现自MQPushConsumer接口,提供注册监听的方法消费消息,registerMessageListener、重载方法。
MessageListenerConcurrently:并行消费
MessageListenerOrderly:串行消费,consumer会把消息放入本地队列并加锁,定时任务保证锁的同步。

简述RockerMQ持久化机制

commitLog:日志数据文件,被所有的queue共享,大小为1G,写满之后重新生成,顺序写
consumeQueue:逻辑queue,消息先到达commitLog、然后异步转发到consumeQueue,包含queue在commitLog中的物理位置偏移量Offset,消息实体内容的大小和MessageTag的hash值。大小约为600w个字节,写满之后重新生成,顺序写。
indexFile:通过key或者时间区间来查找CommitLog中的消息,文件名以创建的时间戳命名,固定的单个IndexFile大小为400M,可以保存2000W个索引。

所有队列共用一个日志数据文件,避免了kafka的分区数过多、日志文件过多导致磁盘IO读写压力较大造成的性能瓶颈,rocketmq的queue只存储少量的数据、更加轻量化,对于磁盘的访问是串行化避免磁盘竞争,缺点在于:写入是顺序写,但读是随机的,先读ConsumeQueue,再读CommitLog,会降低消费读的效率。

消息发送到broker后,会被写入commitLog,写之前加锁,保证顺序写入。然后转发到consumeQueue。

消息消费时先从consumeQueue读取消息在CommitLog中的起始物理偏移量Offset,消息大小和消息的tag的HashCode值。在重commitLog读取消息内容。

  • 同步刷盘:
    消息持久化到磁盘才会给生产者返回ack,可以保证消息可靠,但是会影响性能
  • 异步刷盘
    消息写入pageCache就返回ack给生产者,刷盘采用异步线程,降低读写延迟提高性能和吞吐。
RocketMQ如何保证不丢消息

生产者:

  • 同步阻塞的方式发送消息,加上失败重试机制,可能broker存储失败,可以通过查询确认
  • 异步发送需要重写回调方法,检查发送结果
  • ack机制,可能存储CommitLog,存储ConsumerQueue失败,此时对消费者不可见

broker:
同步刷盘、集群模式下采用同步复制,会等待slave复制完成才会返回确认。

消费者:
offset手动提交,消息消费保证幂等。

消息发送

生产者向消息队列里写入消息,不同的业务场景需要生产者采用不同的写入策略。比如同步发送,异步发送,Oneway发送,延迟发送,发送事务消息等。默认使用的是DefaultMQProducer类,发送消息要经过五个步骤。
1)设置Producer的GroupName.
2)设置InstanceName,当一个jvm需要启动多个Producer的时候,通过设置不同的InstanceName来区分,不设置的话系统使用默认名称“Default”。
3)设置发送失败重试次数,当网络出现异常的时候,这个次数影响消息的重复投递次数。想保证不丢消息,可以设置重试几次。
4)设置NameServer地址。
5)组装消息并发送。

消息发生返回状态(SendResule#SendStatus)有如下四种
Flush_Dish_timeout
Flush_slave_timeout
Slave_not_available
send_ok

不同状态在不同的刷盘策略和同步策略的配置下含义是不同的。
1,Flush_Dish_Timeout:表示没有在规定时间内完成刷盘(需要Broker的刷盘策略被设置成Sync_flush才会报这个错)。
2,Flush_slave_Timeout:表示在主备方式下,并且Broker被设置成Sync_Master方式,没有在设定时间内完成主从同步。
3,Slave_not_available:这个状态产生的场景和Flush_slave_timeout类似,表示在主备方式下,并且Broker被设置成Sync_master,但是没有找到被设置成slave的broker。
4,Send_ok:表示发送成功,发送成功的具体含义,比如消息是否已经被存储到磁盘?消息是否被同步到Slave上?消息在Slave上是否被写入磁盘?需要结合所配置刷盘策略,主从策略来定。这个状态可以理解为,没有发生上面列出的三个问题状态就是Send_Ok.
写一个高质量的生产者程序,重点在于对发送结果的处理,要充分考虑各种异常,写清对应的处理逻辑。

提升写入性能
发送一条消息处刑曲需要经过三步。
1,客户端发送请求到服务器
2,服务器处理该请求。
3,服务器向客户端返回应答。
一次消息的发送耗时是上述三个步骤的总和。
在一些对速度要求高,但是可靠性要求不高的场景下,比如日志收集类应用,可以采用Oneway方式发送。

Oneway方式发送请求不等待应答,即将数据写入客户端的Socket缓冲区就返回,不等待对方返回结果。

用这种方式发送消息的耗时可以缩短到毫秒级。

另一种提高发送速度的方法是增加Producer的并发量,使用多个Producer同时发送,我们不担心多Producer同时写会降低消息写磁盘的效率,RocketMq引入了一个并发窗口,在窗口内消息可以并发地写入DirectMem中,然后异步地将连续一段无空洞的数据刷入文件系统中。

顺序写CommitLog可以让rocketMq无论在HDD还是SSD磁盘情况下都能保持较高的写入性能。

目前在阿里内部经过调优的服务器上,写入性能达到90w+的tps,我们可以参考这个数据进行系统优化。

消息消费

简单总结消费的几个要点:
1,消息消费方式(pull和push)
2,消息消费的模式(广播模式和集群模式)
3,流量控制(可以结合sentinel来实现,后面单独讲)
4,并发线程数设置
5,消息的过滤(tag,key)taga||tagb||tagc*null

当consumer的处理速度跟不上消息的产生速度,会造成越来越多的消息积压,这个时候可以查看消费逻辑本身有没有优化的空间,除此之外还有三种方法可以提高Consumer的处理能力。
1,提高消费并行度
在同一个ConsumerGroup下(Clustering方式),可以通过增加Consumer实例的数量来提高并行度。
通过加机器,或者在已有的机器启动多个Consumer进程都可以增加Consumer是实例数。
注意:总的consumer数量不要超过topic的read queue数量,超过的consumer实例接收不到消息。

此外,通过提高单个Consumer实例中的并行处理的线程数,可以在同一个Consumer内增加并行度来提高吞吐量(设置方法是修改consumerThreadMin和consumeThreadMax)

2,以批量方式进行消费
某些业务场景下,多条消息同时处理的时间会大大小于逐个处理的时间总和,比如消费消息中涉及update某个数据库,一次update10条的时间会大大小于10次update1条数据的时间。

可以通过批量方式消费来提高消费的吞吐量。实现方法是设置Consumer的consumerMessageBatchMaxSize这个参数,默认是1,如果设置N,在消息多的时候每次收到长度为N的消息链表。

3,检测延时情况,跳过非重要消息
consumer在消费过程中,如果发现由于某种原因发生严重的消息堆积,短时间无法消除堆积,这个时候可以选择丢弃不重要的消息,使Consumer尽快追上Producer的进度。

Mysql 索引的基本原理

索引用来快速地寻找那些具有特定值得记录。如果没有索引,一般来说执行查询时遍历整张表。
索引得原理:就是把无序得数据变成有序得查询。
1,把创建了索引得列得内容进行排序
2,对排序结果生成倒排表
3,在查询得时候,先拿到倒排表内容,再取出数据地址链,从而拿到具体得数据

mysql聚族和非聚族索引的区别 业务系统里面的sql耗时,慢查询

在业务系统中,除了使用主键进行的查询,其他的都会在测试库上测试其耗时,慢查询的统计主要是运维在做,会定期将业务中的慢查询反馈给我们。

慢查询的优化首先要搞明白慢的原因是什么?是查询没有命中索引?是load了不需要的数据列?还是数据量太大?
所以优化是针对这三个方向来的。
首先分析语句,看看是否load了额外的数据,可能是查询了多余的行并且抛弃掉了,可能是加载了许多结果中并不需要的列,对语句进行分析和重写。
分析语句的执行计划,然后获得其使用索引的情况,之后修改语句或者修改索引,使得语句可以尽可能的命中索引。
如果对语句的优化已经无法进行,可以考虑表中的数据量是否太大,如果是的话可以进行横向或者纵向的分表。

最左前缀原则是什么

当一个SQL想利用索引是,就一定要提供索引所对应的字段中最左边的字段,也就是排在前面的字段,比如针对a,b,c三个字段建立了一个联合索引,那么在写一个sql时就一定要提供a字段的条件,这样才能用到联合索引,这是由于在建立a,b,c三个字段的联合索引时,底层的B+树是按照a,b,c三个字段从左往右去比较大小进行排序,所以如果想利用B+树进行快速查找也得符合这个规则。

Innodb是如何实现事务的

innodb通过Buffer Pool,LogBuffer,Redo Log,UndoLog来实现事务
1,Innodb在收到一个update语句后,会先根据条件找到数据所在的页,并将该页缓存在Buffer Pool中
2,执行update语句,修改buffer pool中的数据,也就是内存中的数据
3,针对update语句生成一个redolog对象,并存入LogBuffer中
4,针对update语句生成undolog日志,用于事务回滚
5,如果事务提交,那么则把redolog对象进行持久化,后续还有其他机制将buffer pool中所修改的数据页持久化到磁盘中。
6,如果事务回滚,则利用undolog日志进行回滚。

redolog 和 undolog的区别

redolog 重做日志:
确保事务的持久性。防止在发生故障的时间点,尚有脏页未写入到磁盘,在重写mysql服务的时候,根据redo log进行重做,从而达到事务的持久性这一特性

  • 为什么需要redolog?

我们都知道,事务的四大特性里面有一个持久性,具体来说就是只要事务提交成功,那么对数据库做的修改就被永久保存下来了,不可能因为任何原因再回到原来状态。
那么mysql是如何保证一致性呢?最简单的做法是在每次事务提交的时候,将该事务涉及修改的数据页全部刷新到磁盘中。但是这么做会有严重的性能问题,主要体现在两个方面。

1,因为innodb是以 页为单位进行磁盘交互的,而一个事务很可能只修改一个数据页里面的几个字节,这个时候将完整的数据页刷到磁盘的话,太浪费资源了
2,一个事务可能涉及修改多个数据页,并且这些数据在物理上并不连续,使用随机IO写入性能太差。

因此mysql设计了redolog ,具体来说就是只记录事务对数据页做了哪些修改,这样就能完美地解决性能问题了。

undolog 回滚日志
保存了事务发生之前的数据的一个版本,可以用于回滚,同时可以提供多版本并发控制下的读(MVCC),也即非锁定读。

zk的初始化选举和崩溃选举过程

zxid:事务id sid:节点id
先对比zxid,在对比节点id,先投自己,选票内容(zxid,sid),遇强改投。

投票箱:每个节点在本地维护自己和其他节点的投票信息,改投时需要更新信息,并广播节点状态。

  • Looking:竞选状态
  • Following:随从状态
  • Observing:观察状态,同步leader状态,不参与投票
  • Leading:领导者状态
初始化:没有历史数据,5个节点为例
  • 节点1启动,此时只有一台服务器启动,它发出去的请求没有任何响应,所以它的选举状态一直都是Locking状态。
  • 节点2启动,它与节点1进行通信,互相交换自己的选举结果,由于两者没有历史数据,所以serverId值较大的服务器2胜出,但是由于没有达到半数以上,所以服务器1,2还是继续保持Looking状态。
  • 节点3启动,与1、2节点通信交互数据,服务器3成为服务器1,2,3中的leader,此时有三台服务器选择了3,所以3成为leader。
  • 节点4启动,理论上服务器4应该时服务器1,2,3,4中最大的,但是由于前面已经有半数以上的服务器选择了服务器3,所以它只能切换为follower
  • 节点5启动,同4一样
崩溃选举
  • 变更状态,leader故障后,follower进入looking状态
  • 各节点投票,先投自己(zxid,sid),再广播投票
  • 接收到投票,对比zxid和Sid,如果本节点小,则将票改为接收投票信息,并记录投票信息,重新广播。否则本节点大,则可不做处理。
  • 统计本地投票信息,超过半数,则切换为leading状态并广播
布隆过滤器

位图:int[10],每个int类型的整数是4*8=32个bit,则int[10]一共有320bit,每个bit非0即1,初始化时都是0

添加数据时,将数据进行hash得到hash值,对应到bit位,将该bit改为1,hash函数可以定义多个,则一个数据添加会将多个(hash函数个数)bit改为1,多个hash函数的目的是减少hash碰撞的概率

查询数据时:hash函数计算得到hash值,对应到bit中,如果有一个为0,则说明数据不在bit中,如果都为1,则该数据可能在bit中。

优点:

  • 占用内存小
  • 增加和查询元素的时间复杂度为:O(k),k为哈希函数的个数,一般比较小,与数据量大小无关
  • 哈希函数相互之间没有关系,方便硬件并行运算
  • 布隆过滤器不需要存储元素本身,在某些对保密要求比较严格的场合有很大的优势
  • 数据量很大时,布隆过滤器可以表示全集
  • 使用同一组散列函数的布隆过滤器可以进行交、并、查运算

缺点:

  • 误判率,即存在假阳性(False Position),不能准确判断元素是否在集合中
  • 不能获取元素本身
  • 一般情况下不能从布隆过滤器中删除元素
zk的数据模型

zk的数据模型时一种树形结构,具有一个固定的根节点(/),可以

#Redis

分布式缓存寻址算法
  • hash算法:根据key进行hash函数运算、结果对分片取模,确定分片适合固定分片数的场景
    扩展分片或者减少分片时,所有数据都需要重新计算分片、存储
  • 一致性hash:将整个hash值得区间组织成一个闭合的圆环,计算每台服务器的hash值、映射到圆环中。使用相同的hash算法计算数据的hash值,映射到圆环,顺时针寻找,找到第一个服务器就是数据存储的服务器。
    新增或减少节点时只会影响节点到他逆时针最近的一个服务器之间的值,存在hash环倾斜的问题,即服务器分布不均匀,可以通过虚拟节点解决
  • hash slot:将数据与服务器隔离开,数据与slot映射,slot与服务器映射,数据进行hash决定存放的slot
    新增及删除节点时,将slot进行迁移即可
redis的持久化机制

RDB:redis DataBase 将某一个时刻的内存快照(Snapshot),以二进制的方式写入磁盘。

手动触发:
save命令,使Redis处于阻塞状态,直到RDB持久完成,才会响应其他客户端发来的命令,所以在生产环境一定要慎用。

bgsave命令:fork出一个子进程执行持久化,主进程只在fork过程中有短暂的阻塞,子进程创建之后,主进程就可以响应端请求了。

自动触发:

save m n:在m秒内,如果有n个健发生改变,则自动触发持久化,通过bgsave执行,如果设置多个、只要满足其一就会触发,配置文件有默认配置(可以注释掉)

flushall:用于清空redis所有的数据库,flushdb清空当前redis所在库数据(默认是0号数据库),会清空RDB文件,同时会生成dump.rdb、内容为空。

主从同步:全量同步时会自动触发bgsave命令,生成rdb发送给从节点

优点:
1,整个Redis数据库将只包含dump.rdb,方便持久化。
2,容灾性好,方便备份。
3,性能最大化,fork子进程来完成写操作,让主进程继续处理命令,所有是IO最大化。使用单独子进程进行持久化,主进程不会进行任何IO操作,保证了redis的高性能。
4,相对于数据集大时,比AOF的启动效率更高。

Redis单线程为什么这么快

Redis基于Reactor模式开发了网络事件处理器、文件事件处理器file event handler。它是单线程的,所以Redis才叫做单线程模式,它采用IO多路复用机制来同时监听多个Socket,根据Socket上的事件类型来选择对应的事件处理这个事件。可以实现高性能的网络通信模型,又可以跟内部其他单线程模块进行对接,保证了Redis内部的线程模型的简单性。

文件事件处理器的结构包含4个部分:多个Socket、IO多路复用程序、文件事件分派器以及事件处理器(命令请求处理器、命令回复处理器、连接应答处理器等)

多个Socket可能并发的产生不同的事件,IO多路复用程序会监听多个Socket,会将socket放入一个队列中排队,每次从队列中有序、同步取出一个Socket事件分派器,事件分派器把Socket给对应的事件处理器。

然后一个Socket的事件处理完之后,IO多路复用程序才会将队列中的下一个Socket给事件分派器。文件事件分派器会根据每个socket当前产生的事件,来选择对应的事件处理器来处理。

1,Redis启动初始化时,将连接应答处理器跟AE_READABLE事件关联
2,若一个客户端发起连接,会产生一个AE_READBLE事件,然后由连接应答器负责和客户端建立连接,创建客户端对应的socket,同时将这个socket的AE_READBLE事件和命令请求处理器关联,使得客户端可以向主服务器发送命令请求。
3,当客户端向Redis发请求时(不管读还是写请求),客户端socket都会产生一个AE_READABLE事件,触发命令请求处理器。处理器读取客户端的命令内容,然后传给相关程序执行。
4,当Redis服务器准备好给客户端的响应数据后,会将socket的AE_WRITABLE事件和命令回复处理器关联,当客户端准备好读取响应数据时,会在socket产生一个AE_WRITABLE事件,由对应命令回复处理器,即将准备好的响应数据写入socket,供客户端读取。
5,命令回复处理器全部写完到socket后,就会删除该socket的AE_WRITABLE事件和命令回复处理器的映射。

单线程快的原因:
1)纯内存操作
2)核心是基于非阻塞的IO多路复用机制
3)单线程反而避免了多线程的频繁上下文切换带来的性能问题

redis高可用方案 主从

哨兵模式
sentinel,哨兵是redis集群中非常重要的一个组件,主要有以下功能:

  • 集群监控:负责监控redis master和slave进程是否正常工作。
  • 消息通知:如果某个redis实例有故障,那么哨兵负责发送消息作为报警通知给管理员
  • 故障转移:如果master node挂掉了,会自动转移到slave node上。
  • 配置中心:如果故障转移发生了,通知client客户端新的master地址。

哨兵用于实现redis集群的高可用,本身也是分布式的,作为一个哨兵集群去运行,互相协同工作。

  • 故障转移时,判断一个master node是否宕机,需要大部分的哨兵都同意才行,涉及到了分布式选举
  • 即使部分哨兵节点挂掉了,哨兵集群还是能正常工作的。
  • 哨兵通常需要3个实例,来保证自己的健壮性。
  • 哨兵+redis主从的部署架构,是不保证数据零丢失的,只能保证保证redis集群的高可用性。
  • 对于哨兵+redis主从这种复杂的部署架构。
Redis Cluster

3.0版本开始正式提供。采用slot(槽)的概念,一共分成16384个槽。将请求发送到任意节点,接收到请求的节点会将查询发送的正确的节点上执行。

方案说明:

  • 通过哈希的方式,将数据分片,每个节点均分存储一定哈希槽(哈希值)区间数据,默认分配了16384(2的14次方)
  • 每份数据分片会存储在多个互为主从的多节点上
  • 数据写入先写主节点,再同步到从节点(支持配置为阻塞同步)
  • 同一分片多个节点间的数据不保持强一致性
  • 读取数据时,当客户端操作的key没有分配在该节点上时,redis会返回转向指令,指向正确的节点
  • 扩容时需要把旧节点的数据迁移一部分到新节点

在redis cluster架构下,每个redis要开放两个端口号,比如一个是6379,另外一个是16379.

16379端口号是用来进行节点间通信的,也就是cluster bus的通信,用来进行故障检测、配置更新、故障转移授权。cluster bus用了另外一种二进制的协议,gossip协议,用于节点间进行高效的数据交换,占用更少的网络带宽和处理时间

优点:

  • 无中心架构,支持动态扩容,对业务透明
  • 只能使用0号数据库
  • 不支持批量操作(pipeline管道操作)
  • 分布式逻辑和存储模块耦合等
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/878470.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号