准备12345

1. hashmap实现原理

(1)结构:key-value,数组-链表-红黑树

(2)重要变量:length,扩容因子,链表树化阈值,树链表化阈值,最小树化阈值。

(3)术语:碰撞、命中、散列、2的n次冥

(4)下标取值方法:1. key的hash:(h = key.hashCode()) ^ (h >>> 16) 。这个是原生的hashcode值的高16位于低16位进行异或运算。 2. key的hash与length-1做&运算获得的值就是最散列的下标。

(5)扩容下标获取方法: e.hash & oldCap(旧数组的数组长度,2^n)的值为0,那么扩容之后的位标和原先的一样,即(oldCap -1) &e.hash; e.hash & oldCap(旧数组的数组长度,2^n)的值为0,那么扩容之后的位标和原先的一样,即(2oldCap -1) &e.hash

(6)Put方法:一、1.对key的hashCode()做hash运算,计算index;2.如果没碰撞直接放到bucket;3.如果碰撞了,以链表的形式存在buckets后;4.如果碰撞导致链表过⻓(⼤于等于TREEIFY_THRESHOLD),就把链表转换成红⿊树(JDK1.8中的改动);5.如果节点已经存在就替换old value(保证key的唯一性);6.如果bucket满了(超过load factor*current capacity),就要resize。

                        二、在得到下标值以后,可以开始put值进入到数组+链表中,会有三种情况:1. 数组的位置为空。2. 数组的位置不为空,且面是链表的格式。3.数组的位置不为空,且下面是红黑树的格式。

(7)get方法:对key的hashCode()做hash运算,计算index;如果在bucket的第index个节点则直接命中,则直接返回;如果有冲突,则通过key.equals(k)去查找对应的Entry

(8)containsKey 方法:根据get方法的结果,判断是否为空,判断是否包含该key

(9)String类中的hashCode计算⽅法比较简单的,就是以31为权。

(10)红黑树、链表效率不一样,前者新增慢,后者数量大了之后查找慢。所以链表树化阈值,树链表化阈值一般不一样,空一位防止链表和树之间频繁的转换。

(11)并发问题: ConcurrentHashMap。haspmap的key可以是null,value可以是null。

(12)key值:1. Integer、String这种不可变类当HashMap当key,而且String最为常见。2. 用可变类当Hashmap1的Key,类上添加final修饰符,成员变量私有,通过构造器初始化成员, 在getter方法中,不要直接返回对象本身,而是克隆对象。

(13)哈希算法:将不同长度的输入值,计算成为小于输入长度的固定值,当两个不同的输入值x != y,可能会得到f(x)=f(y)的结果,称为碰撞,其实md5也是hash算法的一种。

2. 线程池参数优化\JVM调优\NIO跟Netty线程模型\Elasticsearch\MongoDB

(1)ThreadPoolExecutor:7个参数,corePoolSize(核心线程数)、maximumPoolSize(允许存在的最大线程数量)、keepAliveTime(超过corePoolSize之后的“临时线程”的存活时间)、unit(空闲线程允许存活时间的单位)、workQueue(BlockingQueue是一个先进先出的阻塞式队列实现,LinkedBlockingQueue——takeLock和putLock、threadFactory(创建线程的工厂类)、handler(线程池执行拒绝策略,当线数量达到maximumPoolSize大小)。

(2)AQS:AbstractQuenedSynchronizer抽象的队列式同步器。核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并将共享资源设置为锁定状态,如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中。AQS是自旋锁:**在等待唤醒的时候,经常会使用自旋(while(!cas()))的方式,不停地尝试获取锁,直到被其他线程获取成功。

(3)G1垃圾收集器:跨越年轻代和年老代的垃圾收集器。

(4)Evacuation Failure:当没有更多的空闲region被提升到老一代或者复制到幸存空间时,并且由于堆已经达到最大值,堆不能扩展,从而发生Evacuation Failure。解决方法:不要过度加一些jvm参数、增加JVM堆大小(即-Xmx)、减少-XX:InitiatingHeapOccupancyPercent(默认值是45%,减小该值将提前开始marking cycle ,增加到默认值以上)、并发marking cycle慢,使用属性'-XX:ConcGCThreads'增加并发标记线程数的数量、大量“空间耗尽(to-space exhausted)”或“空间溢出(to-space overflow)”GC事件,则增加-XX:G1ReservePercent。

(5)Humongous Allocation:大型对象(Humongous )是大于G1中region大小50%的对象。频繁大型对象分配会导致性能问题。解决方案:加大region的大小,设置-XX:G1HeapRegionSize=n,但是这个参数需要设置为2的幂次方,最小值是1M,做大值是32M;增加JVM堆大小(即-Xmx -Xms)。

(6)System.gc:system.gc其实是做一次full gc、会暂停整个进程、一般情况下我们要禁掉,使用-XX:+DisableExplicitGC、在cms gc下我们通过-XX:+ExplicitGCInvokesConcurrent来做一次稍微高效点的GC(效果比Full GC要好些)、最常见的场景是RMI/NIO下的堆外内存分配等。Netty框架经常会使用DirectByteBuffer来分配堆外内存,在分配之前会显式的调用System.gc(),如果开启了DisableExplicitGC这个参数,会导致System.gc()调用变成一个空调用,没有任何作用,反而会导致Netty框架无法申请到足够的堆外内存,从而产生java.lang.OutOfMemoryError: Direct buffer memory。

(7)DirectByteBuffer:DirectByteBuffer没有finalizer,它的native memory的清理工作是通过sun.misc.Cleaner自动完成的,而sum.misc.Cleaner是一种基于虚引用的回收工具。DirectByteBuffer通过内存映射,使java进程直接访问与文件相关联的虚拟地址空间,减少了文件拷贝带来的开销,提高了文件读取效率。这一块虚拟地址空间并不是分配在jvm堆上,而是分配在native堆上。yong gc不能回收这部分空间,只能通过Full gc顺带进行回收,那是因为Full gc时会触发sun.misc.Cleaner,对DirectByteBuffer对象做清理工作。所以之前的每隔1小时并不是真的想进行Full gc,而是想通过Full gc回收native堆中无用空间。需要定期地对native堆做清理,清理时可以使用Full gc,也可以使用CMS,视QPS情况而定;
(8)CMS垃圾回收器:-XX:+UseConcMarkSweepGC。ExplicitGCInvokesConcurrent和ExplicitGCInvokesConcurrentAndUnloadsClasses这两个参数来保证显式调用System.gc()触发的是一个并发GC周期而不是Full GC。sun.rmi.dgc.server.gcInterval这个参数来修改GC间隔。CMS收集器会花费更多的时间,如果对QPS比较敏感的应用应降低CMS触发的次数。

(9)机器load过高:禁用Full gc显示调用(-XX:+DisableExplicitGC,但是此选项有OOM风险,设置-XX:MaxDirectMemorySize=10m,使OOM来得更早一些)、减少Full gc次数(通过sun.rmi.dgc.server.gcInterval和un.rmi.dgc.client.gcInterval选项调整Full gc的时间间隔)、降低每次Full gc的时间(添加ExplicitGCInvokesConcurrent选项,使用CMS收集器来触发Full gc)、Full gc更少的停机时间(启用-XX:+ExplicitGCInvokesConcurrent或-XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses选项)。

(10)Java内存结构:线程共享(堆、元空间)、线程私有(虚拟机栈、本地方法栈、程序计数器)。前者错误oom,后者错误StackOverflowerror。

(11)JVM关闭:正常关闭--当最后一个非守护线程结束或调用了System.exit或通过其他特定于平台的方式,比如ctrl+c。强制关闭--调用Runtime.halt方法,或在操作系统中直接kill(发送single信号)掉JVM进程。
异常关闭--运行中遇到RuntimeException 异常等。另外,在JVM关闭时做一些扫尾的工作,比如删除临时文件、停止日志服务需要关闭钩子,其本质上是一个线程(也称为hock线程),可以通过Runtime的addshutdownhock (Thread hock)向主jvm注册一个关闭钩子。hock线程在jvm正常关闭时执行,强制关闭不执行。

(12)Netty:传统的HTTP服务器启动一个新线程处理连接(读Socket,得到字节流、解码协议,得到Http请求对象、处理Http请求,得到一个结果,封装成一个HttpResponse对象、编码协议,将结果序列化字节流、写Socket,将字节流发给客户端、循环步骤3);使用Netty你就可以定制编解码协议,实现自己的特定协议的服务器。;Netty是基于Java NIO(NIO代表的一个词汇叫着IO多路复用,由操作系统提供的系统调用,即Linux下的epoll;NIO的全称是NoneBlocking IO,非阻塞IO,区别与BIO)技术封装的一套框架。Netty提供了内置的常用编解码器,提供了一些列生命周期回调接口,可以同时管理多个端口,除了可以处理TCP/UDP Socket,对消息读写大量使用的ByteBuffer进行了优化。

(13)ElasticSearch:实时分布式存储、搜索、分析的引擎。实时、分布式、搜索、分析、评分、倒排索引、分词。分词--Term Dictionary,二分查找;文档ID记录--PostingList,FOR压缩技术;分词前缀--Term Index,FST形式保存;底层 lucene, jar 包,里面包含了封装好的各种建立倒排索引的算法代码

(14)elasticsearch集群:由多个Elasticsearch节点组成,其中一个是Master Node,负责维护索引元数据、负责切换主分片和副本分片身份等工作。最外层的是Index(相当于数据库 表的概念),Index的数据我分发到不同的Node上进行存储,这个操作就叫做分片,数据写入的时候是写到主分片,副本分片会复制主分片的数据,读取的时候主分片和副本分片都可以读。Elasticsearch 写入的流程(把数据先写入内存缓冲区-每隔1s刷新到文件系统缓存区-定时去生成segement,生成translog,每隔5s才会将缓冲区的刷到磁盘中-commit操作,完成一次的持久化-等所有的节点写入成功就返回ack给协调节点,协调节点返回ack给客户端,完成一次的写入);Elasticsearch更新和删除(给对应的doc记录打上.del标识,如果是删除操作就打上delete状态,如果是更新操作就把原来的doc标志为delete,然后重新新写入一条数据-生成一个segement 文件,合并segement文件,会把带有delete状态的doc物理删除掉);Elasticsearch查询(根据ID查询doc-检索内存的Translog文件、检索硬盘的Translog文件、检索硬盘的Segement文件,实时的;根据query(搜索词)去查询匹配的doc-同时去查询内存和硬盘的Segement文件,准实时的)。查询三个阶段——QUERY_AND_FETCH(查询完就返回整个Doc内容)、QUERY_THEN_FETCH(先查询出对应的Doc id ,然后再根据Doc id 匹配去对应的文档)、DFS_QUERY_THEN_FETCH(先算分,再查询)。

3. 数据库 mysql\数据库优化

(1)MySQL 优化:MySQL 的优化一般可以从存储引擎的选择、字段类型的选择、索引的选择、分区分表、主从复制、读写分离、SQL 优化等这里方面入手。

(2)Mysql的MVCC机制:以readview+undo log版本链为实现基础。Innodb存储引擎给每个数据表都添加了三个隐藏字段-DB_TRX_ID(标记更新当前数据记录的transaction id,每处理一个事务,其值自动+1)、DB_ROLL_PTR(回滚指针,记录了最新一次修改该条记录的undo log,回滚的时候就通过这个指针找到undo log回滚)、DB_ROW_ID(当数据表没有指定主键时,数据库会自动以这个列来作为主键,生成聚集索引)。Mysql执行事务的时候,会生成一个ReadView(m_ids:mysql中未提交的事务id集合;min_trx_id:集合中最小的事务id;max_trx_id,mysql下一个要生成的事务id,也就是事务id集合中最大的事务id加1;当前需要执行的事务id),保证事务隔离。Mysql的RR(Repeatable Read )隔离级别即可重复读,读取数据的事务,无论读多少次都是和第一次读取数据获得的值时一样的。RC级别,就是当别人的事务提交后,你就可以读取到别人修改后的值。因此会发生不可重复读问题。当设置为事务级别为RC时,它每次发起数据查询后,都会新开启一个新的ReadView,再根据ReadView中的信息进行数据读取的判断。Mysql的默认隔离级别是RR,避免了脏读、不可重复读。仅在读提交和可重复读两种隔离级别下生效。每行记录字段都保存有  一个最近变更事务Id  一个最新删除的事务Id。事务读数据的原则就是: 读版本号小于等于当前版本的数据(意思就是读不到在当前事务之后修改的数据 避免了不可重复读);读删除事务版本号大于等于当前版本的数据(意思就是如果这条数据在之后的事务里删了,当前事务也不能再读了) 

(3)幻读和不可重复读:不可重复读侧重于update这种操作,同一条数据前后读起来不一样的情况;幻读侧重于insert delete这种操作,前后两次select 数据的数量会发生变化。Mvcc+行锁+间隙锁可解决幻读。

(4)间隙锁:正常等值条件 并且值存在的情况下加的是行锁;如果等值条件 值不存在的情况下加的是间隙锁,或者范围查询,加的也是间隙锁。通过行锁+间隙锁的机制保证了事务A select之后,其他事务相应的insert操作会阻塞。

(5)undolog和readView:undolog(1.历史恢复 通过undolog恢复之前版本的数据   2. 读老版本  根据条件读旧版本的数据),insert undo_log,只针对当前事务,update undo_log,会根据隔离级别不同事务版本的数据可见性不同。readView(快照    存放了当前活跃的一些事务版本号,以及上一个版本的地址.     用来做可见性判断——根据生成时间不同,产生了RC,RR两种可见性)。RC(每条select创建一个新的readview  ,所以导致读提交  读到的都是最新提交的),RR(事务开始的时候创建一个readview, 一直到事务结束都用的这个readview,也就避免了不可重复读)。

(6)当前读和快照读:单条普通的select语句属于快照读,快照读由mvcc+undolog实现;select for update  , insert, update, delete 属于当前读,当前读由行锁+间隙锁实现。

(7)分库分表:垂直分库、垂直分表、水平分表(hash和取模)。分表后保障ID唯一性(设定步长,分布式ID(雪花算法),新增字段做主键)。

(8)mysql主从同步:master提交完事务后,写入binlog;slave连接到master,获取binlog;master创建dump线程,推送binglog到slave;slave启动一个IO线程读取同步过来的master的binlog,记录到relay log中继日志中;slave再开启一个sql线程读取relay log事件并在slave执行,完成同步;slave记录自己的binglog。

(9)复制方式:全同步复制(主库写入binlog后强制同步日志到从库,所有的从库都执行完成后才返回给客户端,但是很显然这个方式的话性能会受到严重影响),半同步复制(和全同步不同的是,半同步复制的逻辑是这样,从库写入日志成功后返回ACK确认给主库,主库收到至少一个从库的确认就认为写操作完成)。

4. springmvc运行流程

(1)SpringMVC工作流程描述:用户发送请求到核心控制器(DispatcherServlet),DispatcherServlet查询HandlerMapping(映射控制器)从而找到处理请求的Controller(处理器),Controller执行业务逻辑处理后,返回一个ModelAndView(模型和视图),DispatcherServlet查询一个或多个ViewResolver(视图解析器)找到ModelAndView对应的视图对象,视图对象负责渲染返回给客户端。

5. springcloud\微服务Spring Cloud

6. springboot\nacos+sentinel\dubbo

7. redis数据类型和持久化\Redis底层原理

(1)redis是一款开源的高性能键值对(KV)的内存数据库,NoSQL类型。支持并发10WQPS;单进程单线程(6.x版本之前),线程安全,采用IO多路复用机制;支持String、hash、list、set、zset的数据类型;支持数据持久化,可将内存的数据保存在磁盘中,重启时加载;支持单机、主从复制、哨兵、集群等;可用作分布式锁;可以作为消息中间件使用,支持发布订阅。

(2)5种数据类型:String(字符串)——二进制安全,可以包含任何数据,比如jpg图片或者序列化的对象 ,一个键最大能存储512MB;Hash(哈希)——string的key和value的映射表,适合存储对象;List(列表)——简单的字符串列表,按照插入顺序排序,双向链表,lpush、rpush、lpop、rpop、lrange等命令,比如关注列表,粉丝列表,消息排行、消息队列;Set(集合)——string类型元素的集合,且不允许重复的成员,集合是通过哈希表实现,添加,删除,查找的复杂度都是O(1),共同好友、Ip访问统计。zset(sorted set:有序集合)——关联了一个double类型权重的参数score,使得集合中的元素能够按照score进行有序排列,Redis sorted set的内部使用HashMap和跳跃表(skipList)来保证数据的存储和有序,HashMap存放成员到score的映射,skipList存放所有成员,排序依据是HashMap里存的score,使用跳跃表的结构可以获得比较高的查找效率,排行榜、带权重消息队列。

(3)Redis持久化:持久化就是把内存的数据写到磁盘中去,防止服务宕机了内存数据丢失,Redis 提供了两种持久化方式:RDB(默认) 和AOF。

RDB(Redis DataBase): 快照形式是直接把内存中的数据保存到一个dump的文件中,定时保存,保存策略。RDB的缺点:如果你需要尽量避免在服务器故障时丢失数据,那么RDB不合适你。
AOF(Append-only file): 把所有的对Redis的服务器进行修改的命令都存到一个文件里,命令的集合。对于相同的数据集来说,AOF的文件体积通常要大于RDB文件的体积。根据所使用的fsync策略,AOF的速度可能会慢于RDB。存储内容是redis通讯协议(RESP-redis客户端和服务端之前使用的一种通讯协议 )格式的命令文本存储。

(4)架构模式:单机版、主从复制、哨兵(没有解决 master 写的压力)、集群(proxy 型)、集群(直连型、数据通过异步复制,不保证数据的强一致性)。
(5)Redis分布式锁:setnx、redisson、redLock。SET key value [EX seconds|PX milliseconds] [NX|XX] [KEEPTTL] --在用setnx的时候,解锁的时候,先获取value判断是否是当前进程加的锁,再去删除。只是get和del并非原子操作。Redisson是java的redis客户端之一,提供了一些api方便操作redis,Redisson普通的锁实现源码主要是RedissonLock这个类,具备 Watchdog的概念。源码中加锁/释放锁操作都是用lua脚本完成的。Redlock是redis官方提出的一种分布式锁的算法,Redisson提供了对 Redlock 算法的支持RedissonRedLock 

(6)KV+DB读写模式:读的时候,先读缓存,缓存没有的话,就读数据库;更新的时候,先更新数据库,然后再删除缓存。

(7)Redis做异步队列:list的rpush和lpop。生产者和消费者模式。发布者和订阅者模式。Redis Blpop 命令移出并获取列表的第一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。基于redis的消息队列没有Ack的保证。

(8)线程模型:Redis 内部使用文件事件处理器 file event handler,这个文件事件处理器是单线程的,所以 Redis 才叫做单线程的模型。它采用 IO 多路复用机制同时监听多个 Socket,根据 Socket 上的事件来选择对应的事件处理器进行处理。多个 Socket 可能会并发产生不同的操作,每个操作对应不同的文件事件,但是 IO 多路复用程序会监听多个 Socket,会将 Socket 产生的事件放入队列中排队,事件分派器每次从队列中取出一个事件,把该事件交给对应的事件处理器进行处理。这也是Redis 单线程却能支撑高并发的原因。

(9)主从复制过程:从节点执行slaveof[masterIP][masterPort],保存主节点信息;从节点中的定时任务发现主节点信息,建立和主节点的socket连接;从节点发送Ping信号,主节点返回Pong,两边能互相通信;连接建立后,主节点将所有数据发送给从节点(数据同步)。主节点把当前的数据同步给从节点后,便完成了复制的建立过程。接下来,主节点就会持续的把写命令发送给从节点,保证主从数据一致性。一旦主节点宕机,从节点晋升为主节点,同时需要修改应用方的主节点地址,还需要命令所有从节点去复制新的主节点,整个过程需要人工干预。

(10)数据同步过程redis2.8之前使用sync[runId][offset]同步命令,redis2.8之后使用psync[runId][offset]命令;主节点发送数据给从节点过程中,主节点还会进行一些写操作,这时候的数据存储在复制缓冲区中。从节点同步主节点数据完成后,主节点将缓冲区的数据继续发送给从节点,用于部分复制。主节点响应写命令时,不但会把命名发送给从节点,还会写入复制积压缓冲区,用于复制命令丢失的数据补救。主节点有三种响应:FULLRESYNC(全量复制)、CONTINUE(部分复制)、ERR(不支持psync,全量复制)。

(11)Redis Sentinel(哨兵):主要功能包括主节点存活检测、主从运行情况检测、自动故障转移、主从切换。Redis Sentinel最小配置是一主一从。

(12)缓存穿透、缓存雪崩、缓存击穿:缓存穿透——访问一个缓存和数据库都不存在的 key,此时会直接打到数据库上,并且查不到数据,没法写缓存,所以下一次同样会打到数据库上,通过接口校验、缓存空值(expire设置短点)、Guava的布隆过滤器解决。缓存击穿——某一个热点 key,在缓存过期的一瞬间,同时有大量的请求打进来,由于此时缓存过期了,所以请求最终都会走到数据库,造成瞬时数据库请求量大、压力骤增,甚至可能打垮数据库,通过加互斥锁(Redis 分布式锁、JVM 锁)、热点数据不过期(使用时需要考虑业务能接受数据不一致的时间)解决缓存雪崩——大量的热点 key 设置了相同的过期时间,导在缓存在同一时刻全部失效,造成瞬时数据库请求量大、压力骤增,引起雪崩,甚至导致数据库被打挂,通过过期时间打散、热点数据不过期、加互斥锁解决、redis集群热点数据均匀分布。

(13)Redis的并发竞争问题:客户端角度,为保证每个客户端间正常有序与Redis进行通信,对连接进行池化,同时对客户端读写Redis操作采用内部锁synchronized。服务器角度,利用setnx实现锁。

(14)Redis缓存使用:一种是直接通过RedisTemplate来使用,另一种是使用spring cache集成Redis(也就是注解的方式)。pom文件需要引入spring-boot-starter-data-redis--在spring boot 2.x以后底层不再使用Jedis,而是换成了Lettuce;commons-pool2:用作redis连接池,如不引入启动会报错;spring-session-data-redis:spring session引入,用作共享session。注解方法包括@CachePut、 @CacheEvict、@Cacheable

(15)Redis为何这么快:Redis完全是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络带宽。数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度是O(1);采用单线程,避免了不必要的上下文切换和竞争条件,不存在多线程导致的CPU切换,各种锁的问题;使用多路复用IO模型,非阻塞IO。

(16)Redis和Memcached的区别:memcache会把数据全部存在内存之中,断电后会挂掉,数据不能超过内存大小。redis有部分数据存在硬盘上,这样能保证数据的持久性。数据支持类型上:memcache对数据类型的支持简单,只支持简单的key-value,,而redis支持五种数据类型;使用底层模型不同:它们之间底层实现方式以及与客户端之间通信的应用协议不一样。redis直接自己构建了VM机制,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求;value的大小:redis可以达到1GB,而memcache只有1MB。

(17)淘汰策略:Volatile/AllKeys-LRU(less recently used)、TTL(time to live)、LFU(least frequency used)、Random、NoEvivtion.

(18)延时双删策略:先淘汰缓存、再写数据库、休眠1秒,再次淘汰缓存。其中第二次删除作为异步的。自己起一个线程,异步删除。这样,写的请求就不用沉睡一段时间后了,再返回。这么做,加大吞吐量。另外删缓存失败,需要提供一个保障的重试机制,即将需要删除的key发送至消息队列,自己消费消息,获得需要删除的key,继续重试删除操作,直到成功;或者启动一个订阅程序去订阅数据库的binlog(订阅binlog程序在mysql中有现成的中间件叫canal,可以完成订阅binlog日志的功能),获得需要操作的数据。在应用程序中,另起一段程序,获得这个订阅程序传来的信息,进行删除缓存操作。

(19)Lettuce 和 Jedis:定位都是Redis的clien,可以直接连接redis server。Jedis在实现上是直接连接的redis server,如果在多线程环境下是非线程安全的,这个时候只有使用连接池,为每个Jedis实例增加物理连接Lettuce的连接是基于Netty的,连接实例(StatefulRedisConnection)可以在多个线程间并发访问,应为StatefulRedisConnection是线程安全的,所以一个连接实例(StatefulRedisConnection)就可以满足多线程环境下的并发访问。

(20)IO多路复用:指的是同一个进(线)程可以处理多个IO数据流;多线程+池模型指的是每个线程处理一个IO流。优势在于,当处理的消耗对比IO几乎可以忽略不计时,可以处理大量的并发IO,而不用消耗太多CPU/内存。优点是IO多路复用+单进(线)程比较省资源、适合处理大量的闲置的IO、IO多路复用+多单进(线)程与线程池方案相比有好处,但是并不会有太大的优势、如果压力很大,什么方案都得跪,这时就得扩容,因为IO多路复用+单进(线)程比较省资源,所以扩容时能省钱。

8. zookeeper/dubbo/eureka/zuul/fegin/

9.nginx

10.高并发架构怎么搭建

11.java面向对象基础/设计模式

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值