电脑CPU哪个好?我们在组装电脑时,首先选的就是CPU,因为CPU是中央处理器,是电脑的核心部件!那么,电脑CPU哪个好呢?今天小编将给大家推荐10款DIY高性价比CPU!请大家往下看!
DIY装机高性价比CPU推荐:
参考价格:459元
推荐理由:经典神U、亲民高性价比
奔腾G4560号称是2017年Intel平台经典神U,这款七代奔腾CPU最大的亮点在于首次采用了六代/七代i3一样的双核四线程设计,中介了以往奔腾CPU双核双线程历史,其仅仅是牺牲了核心显卡性能,而CPU性能可以媲美六代i3 6100,性能上依然主流,但价格比i3便宜不少,对于中低端入门独显用户来说,无疑是极具性价比的,加之性能依然可满足大多数普通用户3D游戏需求,堪称经典神U,至今依然值得推荐。
参考价格:940元
推荐理由:千元内最具性价比桌面CPU
R5 1400是AMD去年推出的一款中端主流锐龙处理器,原生四核八线程,规格媲美Intel酷睿i7,而价格却便宜不少。另外,R5 1400还支持超频,性能轻松超越七代i5 7500,而价格明显更实惠,加之搭配普通B350主板即可超频,CPU、主板价格都不贵,是主流独显游戏装机,性价比之选。
参考价格:1359元
推荐理由:加量不加价
i5 8400是Intel去年十月初上市的全新八代酷睿主流处理器,原生六核刘线程,相比七代i5性能提升明显,而售价变化不大,可谓是加量不加价。八代i5-8400具备单核性能强、功耗低等特点,目前稍显不足的地方在于只能搭配高端Z370主板,不过最佳搭配的应该是B360主板,这款主板今年即将上市,因此i5 8400也将是Intel今年中端平台的主流之选。
参考价格:619元
推荐理由:核显主流、性能与i3十分接近
奔腾G4600可以看作是奔腾G4560的高配版,主要是未阉割核心显卡,内置了和i3、i5一样的HD630核心显卡,并且CPU主频相比前者更高,综合性能无疑更强,只不过价格相比奔腾G4560贵了不少。这款CPU更适合核显入门用户,无需配独显就可以满足CF/LOL/魔兽世界等一般性的3D网络游戏需求,对于性能要求不高的入门用户,可以省去独显,无疑也是不错的选择。
参考价格:969元
推荐理由:主流默秒全
i3 8100是Intel去年十月初上市的全新第八大酷睿i3处理器,相比双核四线程的七代i3,升级为了实原生四核四线程,性能提升明显。目前,i3 8100盒装售价偏贵,不过散片售价已经不足700元,性价比超高,如果再结合CPU主板套餐,会更超值。
参考价格:1949元
推荐理由:八核利器、性价比力压对手i7
R7 1700是AMD去年三月份上市的锐龙系列高端处理器,原生八核十六线程,性能强悍。这款CPU除了单核性能不低七代i7外,多核性能可以秒杀,并且价格更实惠,让高端CPU也能有更好的性价比。
参考价格:1399元
推荐理由:让i7情可以堪、性价比高
R5 1600是AMD去年四月份上市的一款中高端六核处理器,凭借出色的性能、适中的价位,成为中端游戏PC中甜品级处理器。R5 1600价位上比i7 7700更低,但性能还更强,可以说是一款性价比很高的中高端处理器,颇有游戏玩家青睐。
参考价格:2849元
推荐理由:八代旗舰CPU、单核多线程性能均很强
i7-8700K是Intel平台去年十月份推出的八代酷睿旗舰系列高端处理器,首次将i7推向了六核十二线程,性能强劲,面向主流游戏玩家。可以说,这款高端i7,相比前代产品不再是挤牙膏,性能上有了大幅提升,只不过售价不便宜,不过目前这款CPU散片价仅2200元,性价比还是不错的,对于Intel粉高端玩家来说,也将是2018主流装机之选。
参考价格:2449元
推荐理由:AMD翻身把歌唱
R7 1800X是AMD去年三月份发布的AMD锐龙系列处理器中性能最强的一款,也是AMD历代处理器中的高端翻身之作。在2017年3月份之前,AMD处理器一直都知只是在中低端占据一定份额,高端几乎是Intel的天下,而锐龙R7 1800X无疑终结了这个历史,性能媲美Intel高端i7,并且性价比更高。
参考价格:1349元
推荐理由:中端性价比之选
i5 7500是去年初Intel推出的一款中端主流四核处理器,具备低功耗、单核/多线程性能主流等特点,一度成为2017年甜品级中端主流CPU。随着八代i5上市,i5 7500热度虽然逐渐降低,但价格愈发便宜,并且可选的B250主板价位亲民。相比八代i5目前只能选择高价Z370主板,在B360主板没有上市之间,i5 7500依然是过渡高性价比装机之选。
文章最后附上CPU天梯图,秒懂电脑CPU性能哪个好,各型号处理器性能之间的关系。
以上就是小编要给大家推荐的10款DIY高性价比CPU,以上CPU价格均为盒装价格,价位上都不是最实惠的,因为如今装机选择CPU主板套餐或者散片价格往往更实惠不少,此外硬件价格可能每日都有变动,因此以上处理器价格仅供参考。
购买就不用说了,建议尽量找认识的商家或者论坛的商家,他们会给予一定保修服务。当然,和二手CPU一样,买回来P95验货也是必须的。
又如一个纯渲染机的需求,平台价格限制在3000附近。这样我们仅需要考虑多线程性能,也就是比较最低睿频频率*核心数量的数值再加上一定比例的超线程技术加成。我们可以很容易地得出需要购入这样HCC中低频CPU的结论。
再如游戏为主、兼顾视频处理用户,经过考虑,E5-v3的游戏性能并不高,尤其在网游方面,为其频率投入大量资金并不值得,所以转而选择I7-5820k这样的6核心可超频处理器。
选购一颗合适、可靠的处理器就是这么简单。PC行业里的很多东西甚至远远比不上专业课一章的知识和思维量,说一种硬件“水深”“复杂”,说自己“没时间”,很多时候都是自己给自己设立了一堵墙;而又有多少人沿着墙走了很远,最终抛弃了众多自己认为“水深”的东西,却在x东上购买了山寨的所谓行货?又有多少人自认为“不是那个消费层次”而因为使商品成倍溢价的无良代理,自己进行了大幅的妥协?
嗯,为什么同样的东西,比如主板、SSD、内存,在美国的价格就比国内代理甚至能便宜到一半以内? 水深的,究竟是这些有知识就能掌握的东西,还是那些利用着“水浅”的外表将附加值与信仰一倍又一倍往上炒的笑面虎?
不论是业余还是专业,既然你已经决定踏入PC的领域,那摆在你面前的就是一个严峻的问题:是被奸商欺你不懂被骗,还是以“便宜没好货”为心理安慰眼睁睁地看着钱被炒货者与溢价的代理骗走?
不学习,是被坑可靠性还是被坑价格性能,自己选吧。
其实,我们在使用PC,而不是PC在玩我们。不管散片还是盒装、零售还是OEM、服务器还是桌面级、驰名天下还是鲜有人知,都是为我们创造价值而服务的,所以哪一种产品更能带来价值就应选择哪一种。掌握基础知识,了解并避开真正的禁区,比在所谓“哲学”上的焦虑,容易也省时的多。很多事情自己去详细了解了才知道它们实际上多么简单。
撇开这些事不谈,有件事情我们必须关注一下:
声明,本人能力有限,只是列出来参考,不对之处欢迎指正。
不可以,因为String类有final修饰符,而final修饰的类是不能被继承的,实现细节不允许改变。平常我们定义的String str=”a”;其实和String str=new String(“a”)还是有差异的。
1、ArrayList是基于索引的数据接口,它的底层是数组。它可以以O(1)时间复杂度对元素进行随机访问。与此对应,LinkedList是以元素列表的形式存储它的数据,每一个元素都和它的前一个和后一个元素链接在一起,在这种情况下,查找某个元素的时间复杂度是O(n)。
2、相对于ArrayList,LinkedList的插入,添加,删除操作速度更快,因为当元素被添加到集合任意位置的时候,不需要像数组那样重新计算大小或者是更新索引。
3、LinkedList比ArrayList更占内存,因为LinkedList为每一个节点存储了两个引用,一个指向前一个元素,一个指向下一个元素。
此题考察的是类加载器实例化时进行的操作步骤(加载–>连接->初始化)。
父类非静态变量(父类实例成员变量)、
子类非静态变量(子类实例成员变量)、
参阅我的博客《深入理解类加载》:
hashMap是线程不安全的,HashMap是数组+链表+红黑树(JDK1.8增加了红黑树部分)实现的,采用哈希表来存储的,
JAVA8 的 ConcurrentHashMap 为什么放弃了分段锁,有什么问题吗,如果你来设计,你如何设计。
1、抽象类和接口都不能直接实例化,如果要实例化,抽象类变量必须指向实现所有抽象方法的子类对象,接口变量必须指向实现所有接口方法的类对象。
2、抽象类要被子类继承,接口要被类实现。
3、接口只能做方法申明,抽象类中可以做方法申明,也可以做方法实现
4、接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。
5、抽象类里的抽象方法必须全部被子类所实现,如果子类不能全部实现父类抽象方法,那么该子类只能是抽象类。同样,一个实现接口的时候,如不能全部实现接口方法,那么该类也只能为抽象类。
7、抽象类里可以没有抽象方法
8、如果一个类里有抽象方法,那么这个类只能是抽象类
9、抽象方法要被实现,所以不能是静态的,也不能是私有的。
10、接口可继承接口,并可多继承接口,但类只能单根继承。
继承指的是一个类(称为子类、子接口)继承另外的一个类(称为父类、父接口)的功能,并可以增加它自己的新功能的能力,继承是类与类或者接口与接口之间最常见的关系;在Java中此类关系通过关键字extends明确标识,在设计时一般没有争议性;
聚合是关联关系的一种特例,他体现的是整体与部分、拥有的关系,即has-a的关系,此时整体与部分之间是可分离的,他们可以具有各自的生命周期,部分可以属于多个整体对象,也可以为多个整体对象共享;比如计算机与CPU、公司与员工的关系等;表现在代码层面,和关联关系是一致的,只能从语义级别来区分;
IO是面向流的,NIO是面向缓冲区的
懒汉式单例,饿汉式单例,双重检查等
同时复写hashcode和equals方法,优势可以添加自定义逻辑,且不必调用超类的实现。
访问修饰符,主要标示修饰块的作用域,方便隔离防护
同一个类 同一个包 不同包的子类 不同包的非子类
public: Java语言中访问限制最宽的修饰符,一般称之为“公共的”。被其修饰的类、属性以及方法不
仅可以跨类访问,而且允许跨包(package)访问。
private: Java语言中对访问权限限制的最窄的修饰符,一般称之为“私有的”。被其修饰的类、属性以
及方法只能被该类的对象访问,其子类不能访问,更不能允许跨包访问。
protect: 介于public 和 private 之间的一种访问修饰符,一般称之为“保护形”。被其修饰的类、
属性以及方法只能被类本身的方法及子类访问,即使子类在不同的包中也可以访问。
default:即不加任何访问修饰符,通常称为“默认访问模式“。该模式下,只允许在同一个包中进行访
类加载无须等到“首次使用该类”时加载,jvm允许预加载某些类。。。。
泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数,泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,以提高代码的重用率
通常这个值是对象头部的一部分二进制位组成的数字,具有一定的标识对象的意义存在,但绝不定于地址。
作用是:用一个数字来标识对象。比如在HashMap、HashSet等类似的集合类中,如果用某个对象本身作为Key,即要基于这个对象实现Hash的写入和查找,那么对象本身如何实现这个呢?就是基于hashcode这样一个数字来完成的,只有数字才能完成计算和对比操作。
hashcode只能说是标识对象,在hash算法中可以将对象相对离散开,这样就可以在查找数据的时候根据这个key快速缩小数据的范围,但hashcode不一定是唯一的,所以hash算法中定位到具体的链表后,需要循环链表,然后通过equals方法来对比Key是否是一样的。
底层是基于hashmap实现的
什么是序列化,怎么序列化,为什么序列化,反序列化会遇到什么问题,如何解决。
如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常。 如果虚拟机在动态扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常。
对象诞生即新生代->eden,在进行minor gc过程中,如果依旧存活,移动到from,变成Survivor,进行标记代数,如此检查一定次数后,晋升为老年代,
首先分析是什么类型的内存溢出,对应的调整参数或者优化代码。
内存屏障:为了保障执行顺序和可见性的一条cpu指令
重排序:为了提高性能,编译器和处理器会对执行进行重拍
happen-before:操作间执行的顺序关系。有些操作先发生。
主内存:共享变量存储的区域即是主内存
工作内存:每个线程copy的本地内存,存储了该线程以读/写共享变量的副本
Java程序在运行状态可以动态的获取类的所有属性和方法,并实例化该类,调用方法的功能
最大转为老年代检查次数20
Cms回收开启时机:内存占用80%
命令JVM不基于运行时收集的数据来启动CMS垃圾收集周期
硬件上选择,操作系统选择,版本选择,jdk选择,配置jvm参数,配置connector的线程数量,开启gzip压缩,trimSpaces,集群等
核心组件:bean,context,core,单例注入是通过单例beanFactory进行创建,生命周期是在创建的时候通过接口实现开启,循环注入是通过后置处理器,aop其实就是通过反射进行动态代理,pointcut,advice等。
阻塞式io,非阻塞io,io复用模型,信号驱动io模型,异步io模型。
对于单进程多线程,每个线程处理多个fd的情况,select是不适合的。
1.所有的线程均是从1-32*max进行扫描,每个线程处理的均是一段fd值,这样做有点浪费
2.1024上限问题,一个处理多个用户的进程,fd值远远大于1024
所以这个时候应该采用poll,
poll传递的是数组头指针和该数组的长度,只要数组的长度不是很长,性能还是很不错的,因为poll一次在内核中申请4K(一个页的大小来存放fd),尽量控制在4K以内
epoll还是poll的一种优化,返回后不需要对所有的fd进行遍历,在内核中维持了fd的列表。select和poll是将这个内核列表维持在用户态,然后传递到内核中。但是只有在2.6的内核才支持。
epoll更适合于处理大量的fd ,且活跃fd不是很多的情况,毕竟fd较多还是一个串行的操作
控制权的转换,根据优先级切换上下文(用户,寄存器,系统)
Linux 实现并没有区分这两个概念(进程和线程)
1. 进程:程序的一次执行
2. 线程:CPU的基本调度单位
一个进程可以包含多个线程。
Volatile利用内存栅栏机制来保持变量的一致性。不能代替锁,其只具备数据可见性一致性,不具备原子性。
新建,可运行,运行中, 睡眠,阻塞,等待,死亡。
Sleep依旧持有锁,并在指定时间自动唤醒。wait则释放锁。
首先两者都保持了并发场景下的原子性和可见性,区别则是synchronized的释放锁机制是交由其自身控制,且互斥性在某些场景下不符合逻辑,无法进行干预,不可人为中断等。
而lock常用的则有ReentrantLock和readwritelock两者,添加了类似锁投票、定时锁等候和可中断锁等候的一些特性。此外,它还提供了在激烈争用情况下更佳的性能。
newSingleThreadExecutor返回以个包含单线程的Executor,将多个任务交给此Exector时,这个线程处理完一个任务后接着处理下一个任务,若该线程出现异常,将会有一个新的线程来替代。
newFixedThreadPool返回一个包含指定数目线程的线程池,如果任务数量多于线程数目,那么没有没有执行的任务必须等待,直到有任务完成为止。
newCachedThreadPool根据用户的任务数创建相应的线程来处理,该线程池不会对线程数目加以限制,完全依赖于JVM能创建线程的数量,可能引起内存不足。
ThreadLocal的作用是提供线程内的局部变量,这种变量在线程的生命周期内起作用,减少同一个线程内多个函数或者组件之间一些公共变量的传递的复杂度。
首先这两个方法只能在同步代码块中调用,wait会释放掉对象锁,等待notify唤醒。
CountDownLatch是一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它运行一个或者多个线程一直处于等待状态。
CyclicBarrier要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续运行。
CyclicBarrier初始化的时候,设置一个屏障数。线程调用await()方法的时候,这个线程就会被阻塞,当调用await()的线程数量到达屏障数的时候,主线程就会取消所有被阻塞线程的状态。
前者是递减,不可循环,后者是递加,可循环用
LinkedBlockingQueue 是一个基于单向链表的、范围任意的(其实是有界的)、FIFO 阻塞队列。
ConcurrentLinkedQueue是一个基于链接节点的无界线程安全队列,它采用先进先出的规则对节点进行排序,当我们添加一个元素的时候,它会添加到队列的尾部,当我们获取一个元素时,它会返回队列头部的元素。它采用了“wait-free”算法来实现,该算法在Michael & Scott算法上进行了一些修改, Michael & Scott算法的详细信息可以参见参考资料一。
死锁问题是多线程特有的问题,它可以被认为是线程间切换消耗系统性能的一种极端情况。在死锁时,线程间相互等待资源,而又不释放自身的资源,导致无穷无尽的等待,其结果是系统任务永远无法执行完成。死锁问题是在多线程开发中应该坚决避免和杜绝的问题。
一般来说,要出现死锁问题需要满足以下条件:
1. 互斥条件:一个资源每次只能被一个线程使用。
2. 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
3. 不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺。
4. 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
只要破坏死锁 4 个必要条件之一中的任何一个,死锁问题就能被解决。
此问题的本质是保持顺序执行。可以使用executors
HTTP 1.0主要有以下几点变化:
请求和相应可以由于多行首部字段构成
响应对象前面添加了一个响应状态行
响应对象不局限于超文本
服务器与客户端之间的连接在每次请求之后都会关闭
实现了Expires等传输内容的缓存控制
这时候开始有了请求及返回首部的概念,开始传输不限于文本(其他二进制内容)
HTTP 1.1加入了很多重要的性能优化:持久连接、分块编码传输、字节范围请求、增强的缓存机制、传输编码及请求管道。
客户端发送一个 TCP 的 SYN 标志位置1的包,指明客户端打算连接的服务器的端口,以及初始序号 X,保存在包头的序列号(Sequence Number)字段里。
发送完毕后,客户端进入 SYN_SEND
状态。
发送完毕后,服务器端进入 SYN_RCVD
状态。
客户端再次发送确认包(ACK),SYN 标志位为0,ACK 标志位为1,并且把服务器发来 ACK 的序号字段+1,放在确定字段中发送给对方,并且在数据段放写ISN的+1
发送完毕后,客户端进入 ESTABLISHED
状态,当服务器端接收到这个包时,也进入 ESTABLISHED
状态,TCP 握手结束。
假设客户端想要关闭连接,客户端发送一个 FIN 标志位置为1的包,表示自己已经没有数据可以发送了,但是仍然可以接受数据。
发送完毕后,客户端进入 FIN_WAIT_1 状态。
服务器端确认客户端的 FIN 包,发送一个确认包,表明自己接受到了客户端关闭连接的请求,但还没有准备好关闭连接。
发送完毕后,服务器端进入 CLOSE_WAIT 状态,客户端接收到这个确认包之后,进入 FIN_WAIT_2 状态,等待服务器端关闭连接。
服务器端准备好关闭连接时,向客户端发送结束连接请求,FIN 置为1。
发送完毕后,服务器端进入 LAST_ACK 状态,等待来自客户端的最后一个ACK。
客户端接收到来自服务器端的关闭请求,发送一个确认包,并进入 TIME_WAIT状态,等待可能出现的要求重传的 ACK 包。
服务器端接收到这个确认包之后,关闭连接,进入 CLOSED 状态。
客户端等待了某个固定时间(两个最大段生命周期,2MSL,2 Maximum Segment Lifetime)之后,没有收到服务器端的 ACK ,认为服务器端已经正常关闭连接,于是自己也关闭连接,进入 CLOSED 状态。
两次后会重传直到超时。如果多了会有大量半链接阻塞队列。
1xx:信息,请求收到,继续处理
2xx:成功,行为被成功地接受、理解和采纳
3xx:重定向,为了完成请求,必须进一步执行的动作
4xx:客户端错误,请求包含语法错误或者请求无法实现
5xx:服务器错误,服务器不能实现一种明显无效的请求
Dns解析–>端口分析–>tcp请求–>服务器处理请求–>服务器响应–>浏览器解析—>链接关闭
加密方式是tls/ssl,底层是通过对称算法,非对称,hash算法实现
客户端发起HTTPS请求 –》2. 服务端的配置 –》
3. 传送证书 —》4. 客户端解析证书 5. 传送加密信息 6. 服务段解密信息 7. 传输加密后的信息 8. 客户端解密信息
分流 – 限流–异步–公平性(只能参加一次)–用户体验(第几位,多少分钟,一抢完)
30分钟关闭 可以借助redis的发布订阅机制 在失效时进行后续操作,其他mq也可以
首先分布式锁实现常见的有数据库锁(表记录),缓存锁,基于zk(临时有序节点可以实现的)的三种
Redis适用于对性能要求特别高的场景。redis可以每秒执行10w次,内网延迟不超过1ms
缺点是数据存放于内存,宕机后锁丢失。
锁无法释放?使用Zookeeper可以有效的解决锁无法释放的问题,因为在创建锁的时候,客户端会在ZK中创建一个临时节点,一旦客户端获取到锁之后突然挂掉(Session连接断开),那么这个临时节点就会自动删除掉。其他客户端就可以再次获得锁。
非阻塞锁?使用Zookeeper可以实现阻塞的锁,客户端可以通过在ZK中创建顺序节点,并且在节点上绑定监听器,一旦节点有变化,Zookeeper会通知客户端,客户端可以检查自己创建的节点是不是当前所有节点中序号最小的,如果是,那么自己就获取到锁,便可以执行业务逻辑了。
不可重入?使用Zookeeper也可以有效的解决不可重入的问题,客户端在创建节点的时候,把当前客户端的主机信息和线程信息直接写入到节点中,下次想要获取锁的时候和当前最小的节点中的数据比对一下就可以了。如果和自己的信息一样,那么自己直接获取到锁,如果不一样就再创建一个临时的顺序节点,参与排队。
单点问题?使用Zookeeper可以有效的解决单点问题,ZK是集群部署的,只要集群中有半数以上的机器存活,就可以对外提供服务。
可以使用filter过滤处理
优点是可以管理多机事务,拥有无线扩展性 确定是易用性难,承担延时风险
一致性hash是一种分布式hash实现算法。满足平衡性 单调性 分散性 和负载。
REST 指的是一组架构约束条件和原则。满足这些约束条件和原则的应用程序或设计就是 RESTful。
服务器内核调优(tcp,文件数),客户端调优,框架选择(netty)
缓存雪崩可能是因为数据未加载到缓存中,或者缓存同一时间大面积的失效,从而导致所有请求都去查数据库,导致数据库CPU和内存负载过高,甚至宕机。
1,采用加锁计数,或者使用合理的队列数量来避免缓存失效时对数据库造成太大的压力。这种办法虽然能缓解数据库的压力,但是同时又降低了系统的吞吐量。
2,分析用户行为,尽量让失效时间点均匀分布。避免缓存雪崩的出现。
3,如果是因为某台缓存服务器宕机,可以考虑做主备,比如:redis主备,但是双缓存涉及到更新事务的问题,update可能读到脏数据,需要好好解决。
MESI是四种缓存段状态的首字母缩写,任何多核系统中的缓存段都处于这四种状态之一。我将以相反的顺序逐个讲解,因为这个顺序更合理:
失效(Invalid)缓存段,要么已经不在缓存中,要么它的内容已经过时。为了达到缓存的目的,这种状态的段将会被忽略。一旦缓存段被标记为失效,那效果就等同于它从来没被加载到缓存中。
共享(Shared)缓存段,它是和主内存内容保持一致的一份拷贝,在这种状态下的缓存段只能被读取,不能被写入。多组缓存可以同时拥有针对同一内存地址的共享缓存段,这就是名称的由来。
独占(Exclusive)缓存段,和S状态一样,也是和主内存内容保持一致的一份拷贝。区别在于,如果一个处理器持有了某个E状态的缓存段,那其他处理器就不能同时持有它,所以叫“独占”。这意味着,如果其他处理器原本也持有同一缓存段,那么它会马上变成“失效”状态。
已修改(Modified)缓存段,属于脏段,它们已经被所属的处理器修改了。如果一个段处于已修改状态,那么它在其他处理器缓存中的拷贝马上会变成失效状态,这个规律和E状态一样。此外,已修改缓存段如果被丢弃或标记为失效,那么先要把它的内容回写到内存中——这和回写模式下常规的脏段处理方式一样。
哈希(Hash)算法,即散列函数。 它是一种单向密码体制,即它是一个从明文到密文的不可逆的映射,只有加密过程,没有解密过程。 同时,哈希函数可以将任意长度的输入经过变化以后得到固定长度的输出
Paxos算法是莱斯利·兰伯特(Leslie Lamport,就是 LaTeX 中的”La”,此人现在在微软研究院)于1990年提出的一种基于消息传递的一致性算法。
整个ZAB协议主要包括消息广播和崩溃恢复两个过程,进一步可以分为三个阶段,分别是:
组成ZAB协议的每一个分布式进程,都会循环执行这三个阶段,将这样一个循环称为一个主进程周期。
点击编辑的时候,利用redis进行加锁setNX完了之后 expire 一下
也可以用版本号进行控制
逐级排查(网络,磁盘,内存,cpu),数据库,日志,中间件等也可通过监控工具排查。
单例, 代理,模板,策略,命令
Dubbo[]是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案。
在集群负载均衡时,Dubbo提供了多种均衡策略,缺省为random随机调用。
LeastActive LoadBalance:最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差。使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。
ConsistentHash LoadBalance:一致性Hash,相同参数的请求总是发到同一提供者。当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。
快速失败,只发起一次调用,失败立即报错。
1)服务消费方(client)调用以本地调用方式调用服务;
2)client stub接收到调用后负责将方法、参数等组装成能够进行网络传输的消息体;
3)client stub找到服务地址,并将消息发送到服务端;
5)server stub根据解码结果调用本地的服务;
6)本地服务执行并将结果返回给server stub;
7)server stub将返回结果打包成消息并发送至消费方;
8)client stub接收到消息,并进行解码;
9)服务消费方得到最终结果。
异步模式使用与服务器多核,并发严重的场景
可提高服务吞吐量大,不容易受到冲击,可以采用并发策略,提高响应时间
缓存数据过期后的更新如何设计。
失效:应用程序先从cache取数据,没有得到,则从数据库中取数据,成功后,放到缓存中。
命中:应用程序从cache中取数据,取到后返回。
更新:先把数据存到数据库中,成功后,再让缓存失效。
一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。
子类型必须能够替换掉它们的父类型。
高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。即针对接口编程,不要针对实现编程
建立单一接口,不要建立庞大臃肿的接口,尽量细化接口,接口中的方法尽量少
说要尽量的使用合成和聚合,而不是继承关系达到复用的目的
迪米特法则其根本思想,是强调了类之间的松耦合,类之间的耦合越弱,越有利于复用,一个处在弱耦合的类被修改,不会对有关系的类造成影响,也就是说,信息的隐藏促进了软件的复用。
一个类只负责一项职责,应该仅有一个引起它变化的原因
可以通过token值进行防止重复提交,存放到redis中,在表单初始化的时候隐藏在表单中,添加的时候在移除。判断这个状态即可防止重复提交。
如何看待缓存的使用(本地缓存,集中式缓存),简述本地缓存和集中式缓存和优缺点。本地缓存在并发使用时的注意事项。
·未提交读(Read Uncommitted):允许脏读,也就是可能读取到其他会话中未提交事务修改的数据
·提交读(Read Committed):只能读取到已经提交的数据。Oracle等多数数据库默认都是该级别 (不重复读)
·可重复读(Repeated Read):可重复读。在同一个事务内的查询都是事务开始时刻一致的,InnoDB默认级别。在SQL标准中,该隔离级别消除了不可重复读,但是还存在幻象读
·串行读(Serializable):完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞
MyISAM: 拥有较高的插入,查询速度,但不支持事务
InnoDB :5.5版本后Mysql的默认数据库,事务型数据库的首选引擎,支持ACID事务,支持行级锁定
Memory :所有数据置于内存的存储引擎,拥有极高的插入,更新和查询效率。但是会占用和数据量成正比的内存空间。并且其内容会在Mysql重新启动时丢失
Merge :将一定数量的MyISAM表联合而成一个整体,在超大规模数据存储时很有用
Archive :非常适合存储大量的独立的,作为历史记录的数据。因为它们不经常被读取。Archive拥有高效的插入速度,但其对查询的支持相对较差
Federated: 将不同的Mysql服务器联合起来,逻辑上组成一个完整的数据库。非常适合分布式应用
Cluster/NDB :高冗余的存储引擎,用多台数据机器联合提供服务以提高整体性能和安全性。适合数据量大,安全和性能要求高的应用
CSV: 逻辑上由逗号分割数据的存储引擎。它会在数据库子目录里为每个数据表创建一个.CSV文件。这是一种普通文本文件,每个数据行占用一个文本行。CSV存储引擎不支持索引。
BlackHole :黑洞引擎,写入的任何数据都会消失,一般用于记录binlog做复制的中继
另外,Mysql的存储引擎接口定义良好。有兴趣的开发者通过阅读文档编写自己的存储引擎。
使用悲观锁 悲观锁本质是当前只有一个线程执行操作,结束了唤醒其他线程进行处理。
也可以缓存队列中锁定主键。
乐观锁是设定每次修改都不会冲突,只在提交的时候去检查,悲观锁设定每次修改都会冲突,持有排他锁。
行级锁分为共享锁和排他锁两种 共享锁又称读锁 排他锁又称写锁
产生死锁的原因主要是:
(2) 进程运行推进的顺序不合适。
(3)资源分配不当等。
如果系统资源充足,进程的资源请求都能够得到满足,死锁出现的可能性就很低,否则就会因争夺有限的资源而陷入死锁。其次,进程运行推进顺序与速度不同,也可能产生死锁。
产生死锁的四个必要条件:
(1) 互斥条件:一个资源每次只能被一个进程使用。
(2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
(3) 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
(4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。
这里提供两个解决数据库死锁的方法:
索引是通过复杂的算法,提高数据查询性能的手段。从磁盘io到内存io的转变
普通索引,主键,唯一,单列/多列索引建索引的几大原则
3.尽量选择区分度高的列作为索引,区分度的公式是count(distinct col)/count(*),表示字段不重复的比例,比例越大我们扫描的记录数越少,唯一键的区分度是1,而一些状态、性别字段可能在大数据面前区分度就是0,那可能有人会问,这个比例有什么经验值吗?使用场景不同,这个值也很难确定,一般需要join的字段我们都要求是0.1以上,即平均1条扫描10条记录
4.索引列不能参与计算,保持列“干净”,比如from_unixtime(create_time) = ’’就不能使用到索引,原因很简单,b+树中存的都是数据表中的字段值,但进行检索时,需要把所有元素都应用函数才能比较,显然成本太大。所以语句应该写成create_time = unix_timestamp(’’);
5.尽量的扩展索引,不要新建索引。比如表中已经有a的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可
“聚簇”就是索引和记录紧密在一起。
非聚簇索引 索引文件和数据文件分开存放,索引文件的叶子页只保存了主键值,要定位记录还要去查找相应的数据块。
每个节点的指针上限为2d而不是2d+1。
内节点不存储data,只存储key;叶子节点不存储指针。
Btree 怎么分裂的,什么时候分裂,为什么是平衡的。
Key 超过1024才分裂B树为甚会分裂? 因为随着数据的增多,一个结点的key满了,为了保持B树的特性,就会产生分裂,就向红黑树和AVL树为了保持树的性质需要进行旋转一样!
A,atomic,原子性,要么都提交,要么都失败,不能一部分成功,一部分失败。
C,consistent,一致性,事物开始及结束后,数据的一致性约束没有被破坏
I,isolation,隔离性,并发事物间相互不影响,互不干扰。
D,durability,持久性,已经提交的事物对数据库所做的更新必须永久保存。即便发生崩溃,也不能被回滚或数据丢失。
避免在where子句中对字段进行is null判断
应尽量避免在where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描。
避免在where 子句中使用or 来连接条件
Like查询(非左开头)
在where子句中对字段进行函数操作
由于复合索引的组合索引,类似多个木板拼接在一起,如果中间断了就无法用了,所以要能用到复合索引,首先开头(第一列)要用上,比如index(a,b) 这种,我们可以select table tname where a=XX 用到第一列索引 如果想用第二列 可以 and b=XX 或者and b like‘TTT%’
mysql中的in语句是把外表和内表作hash 连接,而exists语句是对外表作loop循环,每次loop循环再对内表进行查询。一直大家都认为exists比in语句的效率要高,这种说法其实是不准确的。这个是要区分环境的。
如果查询的两个表大小相当,那么用in和exists差别不大。
如果两个表中一个较小,一个是大表,则子查询表大的用exists,子查询表小的用in:
not in 和not exists如果查询语句使用了not in 那么内外表都进行全表扫描,没有用到索引;而not extsts 的子查询依然能用到表上的索引。所以无论那个表大,用not exists都比not in要快。
2.IN当遇到包含NULL的情况,那么就会返回UNKNOWN。
在分库分表时可能会生成重复主键 利用自增比例达到唯一 自增1 2,3 等
我们公司用activeMQ 因为业务比较简单 只有转码功能,而amq比较简单
如果是分布式的建议用kafka
基本都是对数据进行持久化,多盘存储
集群是保证服务可靠性的一种方式,同时可以通过水平扩展以提升消息吞吐能力。RabbitMQ是用分布式程序设计语言erlang开发的,所以天生就支持集群。接下来,将介绍RabbitMQ分布式消息处理方式、集群模式、节点类型,并动手搭建一个高可用集群环境,最后通过java程序来验证集群的高可用性。
1. 三种分布式消息处理方式
RabbitMQ分布式的消息处理方式有以下三种:
1、Clustering:不支持跨网段,各节点需运行同版本的Erlang和RabbitMQ, 应用于同网段局域网。
字符串(strings):存储整数(比如计数器)和字符串(废话。。),有些公司也用来存储json/pb等序列化数据,并不推荐,浪费内存
哈希表(hashes):存储配置,对象(比如用户、商品),优点是可以存取部分key,对于经常变化的或者部分key要求atom操作的适合
列表(lists):可以用来存最新用户动态,时间轴,优点是有序,确定是元素可重复,不去重
集合(sets):无序,唯一,对于要求严格唯一性的可以使用
有序集合(sorted sets):集合的有序版,很好用,对于排名之类的复杂场景可以考虑
持久化方式:RDB时间点快照 AOF记录服务器执行的所有写操作命令,并在服务器启动时,通过重新执行这些命令来还原数据集。
Redis集群相对单机在功能上存在一些限制, 需要开发人员提前了解,
在使用时做好规避。 限制如下:
1) key批量操作支持有限。 如mset、 mget, 目前只支持具有相同slot值的
行批量操作。 对于映射为不同slot值的key由于执行mget、 mget等操作可
能存在于多个节点上因此不被支持。
2) key事务操作支持有限。 同理只支持多key在同一节点上的事务操
作, 当多个key分布在不同的节点上时无法使用事务功能。
3) key作为数据分区的最小粒度, 因此不能将一个大的键值对象如
sh、 list等映射到不同的节点。
4) 不支持多数据库空间。 单机下的Redis可以支持16个数据库, 集群模
式下只能使用一个数据库空间, 即db0。
5) 复制结构只支持一层, 从节点只能复制主节点, 不支持嵌套树状复
决了Redis分布式方面的需求。 当遇到单机内存、 并发、 流量等瓶颈时, 可
以采用Cluster架构方案达到负载均衡的目的。 之前, Redis分布式方案一般
·客户端分区方案, 优点是分区逻辑可控, 缺点是需要自己处理数据路
由、 高可用、 故障转移等问题。
·代理方案, 优点是简化客户端分布式逻辑和升级维护便利, 缺点是加
重架构部署复杂度和性能损耗。
现在官方为我们提供了专有的集群方案: Redis Cluster, 它非常优雅地
解决了Redis集群方面的问题, 因此理解应用好Redis Cluster将极大地解放我
们使用分布式Redis的工作量, 同时它也是学习分布式存储的绝佳案例。
LRU(近期最少使用算法)TTL(超时算法) 去除ttl最大的键值
集群方式的区别,3采用Cluster,2采用客户端分区方案和代理方案
1) 集群中的每个节点都会单独开辟一个TCP通道, 用于节点之间彼此
通信, 通信端口号在基础端口上加10000。
2) 每个节点在固定周期内通过特定规则选择几个节点发送ping消息。
3) 接收到ping消息的节点用pong消息作为响应。
当缓存使用 持久化使用
并不单一的数据删除机制
基于客户端的分布式系统
变化频繁,具有不稳定性的数据,不需要实时入库, (比如用户在线
门户网站的新闻等,觉得页面静态化仍不能满足要求,可以放入
Memcached默认使用Slab Allocation机制管理内存,其主要思想是按照预先规定的大小,将分配的内存分割成特定长度的块以存储相应长度的key-value数据记录,以完全解决内存碎片问题。
在Redis中,并不是所有的数据都一直存储在内存中的。这是和Memcached相比一个最大的区别。
Redis为单进程单线程模式,采用队列模式将并发访问变为串行访问。Redis本身没有锁的概念,Redis对于多个客户端连接并不存在竞争,但是在Jedis客户端对Redis进行并发访问时会发生连接超时、数据转换错误、阻塞、客户端关闭连接等问题,这些问题均是由于客户端连接混乱造成。对此有2种解决方法:
1.客户端角度,为保证每个客户端间正常有序与Redis进行通信,对连接进行池化,同时对客户端读写Redis操作采用内部锁synchronized。
2.服务器角度,利用setnx实现锁。
MULTI,告诉 Redis 服务器开启一个事务。注意,只是开启,而不是执行
WATCH,监视某一个键值对,它的作用是在事务执行之前如果监视的键值被修改,事务会被取消。
可以利用watch实现cas乐观锁
Raft采用心跳机制触发Leader选举。系统启动后,全部节点初始化为Follower,term为0.节点如果收到了RequestVote或者AppendEntries,就会保持自己的Follower身份。如果一段时间内没收到AppendEntries消息直到选举超时,说明在该节点的超时时间内还没发现Leader,Follower就会转换成Candidate,自己开始竞选Leader。一旦转化为Candidate,该节点立即开始下面几件事情:
1、增加自己的term。
2、启动一个新的定时器。
4、向所有其他节点发送RequestVote,并等待其他节点的回复。
如果在这过程中收到了其他节点发送的AppendEntries,就说明已经有Leader产生,自己就转换成Follower,选举结束。
如果在计时器超时前,节点收到多数节点的同意投票,就转换成Leader。同时向所有其他节点发送AppendEntries,告知自己成为了Leader。
每个节点在一个term内只能投一票,采取先到先得的策略,Candidate前面说到已经投给了自己,Follower会投给第一个收到RequestVote的节点。每个Follower有一个计时器,在计时器超时时仍然没有接受到来自Leader的心跳RPC, 则自己转换为Candidate, 开始请求投票,就是上面的的竞选Leader步骤。
如果多个Candidate发起投票,每个Candidate都没拿到多数的投票(Split Vote),那么就会等到计时器超时后重新成为Candidate,重复前面竞选Leader步骤。
Raft协议的定时器采取随机超时时间,这是选举Leader的关键。每个节点定时器的超时时间随机设置,随机选取配置时间的1倍到2倍之间。由于随机配置,所以各个Follower同时转成Candidate的时间一般不一样,在同一个term内,先转为Candidate的节点会先发起投票,从而获得多数票。多个节点同时转换为Candidate的可能性很小。即使几个Candidate同时发起投票,在该term内有几个节点获得一样高的票数,只是这个term无法选出Leader。由于各个节点定时器的超时时间随机生成,那么最先进入下一个term的节点,将更有机会成为Leader。连续多次发生在一个term内节点获得一样高票数在理论上几率很小,实际上可以认为完全不可能发生。一般1-2个term类,Leader就会被选出来。
Sentinel集群正常运行的时候每个节点epoch相同,当需要故障转移的时候会在集群中选出Leader执行故障转移操作。Sentinel采用了Raft协议实现了Sentinel间选举Leader的算法,不过也不完全跟论文描述的步骤一致。Sentinel集群运行过程中故障转移完成,所有Sentinel又会恢复平等。Leader仅仅是故障转移操作出现的角色。
1、某个Sentinel认定master客观下线的节点后,该Sentinel会先看看自己有没有投过票,如果自己已经投过票给其他Sentinel了,在2倍故障转移的超时时间自己就不会成为Leader。相当于它是一个Follower。
1)更新故障转移状态为start
3)更新自己的超时时间为当前时间随机加上一段时间,随机时间为1s内的随机毫秒数。
6、如果在一个选举时间内,Candidate没有获得超过一半且超过它配置的quorum的票数,自己的这次选举就失败了。
7、如果在一个epoch内,没有一个Candidate获得更多的票数。那么等待超过2倍故障转移的超时时间后,Candidate增加epoch重新投票。
8、如果某个Candidate获得超过一半且超过它配置的quorum的票数,那么它就成为了Leader。
9、与Raft协议不同,Leader并不会把自己成为Leader的消息发给其他Sentinel。其他Sentinel等待Leader从slave选出master后,检测到新的master正常工作后,就会去掉客观下线的标识,从而不需要进入故障转移流程。
RDB 定时快照方式(snapshot): 定时备份,可能会丢失数据
AOF 基于语句追加方式 只追加写操作
AOF 持久化和 RDB 持久化的最主要区别在于,前者记录了数据的变更,而后者是保存了数据本身
ElasticSearch(简称ES)是一个分布式、Restful的搜索及分析服务器,设计用于分布式计算;能够达到实时搜索,稳定,可靠,快速。和Apache Solr一样,它也是基于Lucence的索引服务器,而ElasticSearch对比Solr的优点在于:
轻量级:安装启动方便,下载文件之后一条命令就可以启动。
多索引文件支持:使用不同的index参数就能创建另一个索引文件,Solr中需要另行配置。
分布式:Solr Cloud的配置比较复杂。
倒排索引是实现“单词-文档矩阵”的一种具体存储形式,通过倒排索引,可以根据单词快速获取包含这个单词的文档列表。倒排索引主要由两个部分组成:“单词词典”和“倒排文件”。
在Lucene中一个索引是放在一个文件夹中的。
如上图,同一文件夹中的所有的文件构成一个Lucene索引。
一个索引可以包含多个段,段与段之间是独立的,添加新文档可以生成新的段,不同的段可以合并。
如上图,具有相同前缀文件的属同一个段,图中共三个段 “_0” 和 “_1”和“_2”。
segments.gen和segments_X是段的元数据文件,也即它们保存了段的属性信息。
文档是我们建索引的基本单位,不同的文档是保存在不同的段中的,一个段可以包含多篇文档。
新添加的文档是单独保存在一个新生成的段中,随着段的合并,不同的文档合并到同一个段中。
一篇文档包含不同类型的信息,可以分开索引,比如标题,时间,正文,作者等,都可以保存在不同的域里。
不同域的索引方式可以不同,在真正解析域的存储的时候,我们会详细解读。
词是索引的最小单位,是经过词法分析和语言处理后的字符串。