当前位置:网站首页 > Java基础 > 正文

jdk中的java基础



目录

  • 1、java基础
  • 1.1、hashmap原理?扩容
  • 1.2、arraylist原理?扩容
  • 1.3、jdk1.8新特性?
  • 1.4、在java8中,java.time包下主要包含下面几个主要的类:
  • 1.5、completablefuture
  • 1.6、Integer用==进行值比较,什么时候相等,什么时候不等?
  • 1.7 ==和equals()的区别?
  • 1.8、java IO流中有哪些?转换流有哪些?
  • 1.9、java NIO
  • 1.10 hashmap和hashset的关系和区别
  • 1.11 传统单例模式双重检查锁存在的问题
  • 2、数据库
  • 2.1、mysql索引优化
  • 2.2、创建索引的依据?
  • 2.3、mysql执行计划,explain各项参数代表什么意思?
  • 2.4、什么时候不会用到索引?
  • 2.5、mysql存储引擎
  • 2.6、如何修改大表的表结构(增加一列)
  • 2.7、数据库隔离级别
  • 2.7、为什么要使用索引
  • 2.8、联合索引什么时候回用到,什么时候不会用到?
  • 2.9、mysql的limit用法?
  • 2.10、mysql的select 、where、orderby、having的 执行顺序?having的用法?
  • 2.9、mysql的存储引擎的区别?
  • 2.10 mysql表中删除重复的数据,保留一条
  • 3、java多线程
  • 3.1、java并发包java.util.concurrent及其子包都包括什么?
  • 3.2、synconsized和volatile关键字区别?
  • 3.3、实现线程池的方式?
  • 3.4、公平锁与非公平锁
  • 3.5、为什么不适用Excutors来创建线程池
  • 3.6、ReentraneLock & AQS
  • 3.7、手写线程死锁
  • 4、jvm、java内存模型
  • 4.1、jvm内存模型?
  • 4.2、jvm调优具体调的那些参数?
  • 5、java框架(spring boot,sprint cloud)
  • 5.1、mybatis一级缓存,二级缓存
  • 5.2、mybatis ${}和#{}的区别?
  • 5.3、mybatis中like模糊查询中#和$的使用
  • 5.3、sprint cloud 的常用组件?
  • springcloud相关面试题
  • 6、中间件
  • 6.1、Redis面试
  • 6.2、redis实现分布式锁原理,使用redis实现分布式锁有什么问题?
  • 6.3、zookeeper如何实现公平锁
  • 6.4、如何访问 redis 中的海量数据?避免事故产生
  • 6.5、rabbitmq如何实现消息的不丢失?
  • 6.6、dubbo原理
  • 6.7、Redis的几种数据结构及其使用场景
  • 6.8、redis的zset数据结构
  • 6.9、redis动态字符串sds
  • 7、算法与数据结构
  • 7.1、快排算法(手写)
  • 7.2、链表反转
  • 7.3、删除链表最后第n个值
  • 7.4、二分查找
  • 7.5、树,红黑树,b+树jdk中的java基础
  • 7.6、手写一个队列
  • 8、网络
  • 8.1、tcp,udp,http区别
  • 8.2、三次握手,四次挥手

1、java基础

1.1、hashmap原理?扩容

1.2、arraylist原理?扩容

ArrayList的扩容主要发生在向ArrayList集合中添加元素的时候。由add()方法的分析可知添加前必须确保集合的容量能够放下添加的元素。主要经历了以下几个阶段:

第一,在add()方法中调用ensureCapacityInternal(size + 1)方法来确定集合确保添加元素成功的最小集合容量minCapacity的值。参数为size+1,代表的含义是如果集合添加元素成功后,集合中的实际元素个数。换句话说,集合为了确保添加元素成功,那么集合的最小容量minCapacity应该是size+1。在ensureCapacityInternal方法中,首先判断elementData是否为默认的空数组,如果是,minCapacity为minCapacity与集合默认容量大小中的较大值。

第二,调用ensureExplicitCapacity(minCapacity)方法来确定集合为了确保添加元素成功是否需要对现有的元素数组进行扩容。首先将结构性修改计数器加一;然后判断minCapacity与当前元素数组的长度的大小,如果minCapacity比当前元素数组的长度的大小大的时候需要扩容,进入第三阶段。

1.3、jdk1.8新特性?

1.4、在java8中,java.time包下主要包含下面几个主要的类:

Instant:时间戳
Duration:持续时间,时间差
LocalDate:只包含日期,比如:2019-10-20
LocalTime:只包含时间,比如:23:12:10
LocalDateTime:包含日期和时间,比如:2019-10-20 23:14:21
Period:时间段
ZoneOffset:时区偏移量,比如:+8:00
ZonedDateTime:带时区的时间
Clock:时钟,比如获取目前美国纽约的时间

1.5、completablefuture

1.6、Integer用==进行值比较,什么时候相等,什么时候不等?

1.7 ==和equals()的区别?

1, = = 是一个java的运算符,比较的是等号两边的地址值是否相等。equals()是一个object类的方法,具体要看类的实现。
2,基本数据类型在JVM的分布实在虚拟机栈的中的局部变量表里,使用= =和equals()比较基本数据类型是一样的效果。
3,string类重写了equals()方法,比较的是string的值是不是相等,具体实现是通过遍历char数组每个元素对比。

 

1.8、java IO流中有哪些?转换流有哪些?

字节流:OutputStream InputStream 字节缓冲流:BufferedOutputStream BufferedInputStream
字符流:Writer Reader 字符缓冲流:BufferedWriter BufferedReader
转换流:InputStreamReader:字节流转换为字符流
OutputStreamWriter:字符流转换为字节流

1.9、java NIO

java NIO是在jdk1.4加入的,为了解决原来javaIO的阻塞问题,可以理解为New IO 护着NoBlockIO。
Java NIO 由以下几个核心部分组成:
Channels
Buffers
Selectors
新的输入/输出 (NIO) 库是在 JDK 1.4 中引入的。NIO 弥补了原来的 I/O 的不足,它在标准 Java 代码中提供了高速的、面向块的 I/O。通过定义包含数据的类,以及通过以块的形式处理这些数据,NIO 不用使用本机代码就可以利用低级优化,这是原来的 I/O 包所无法做到的。

1.10 hashmap和hashset的关系和区别

关系:
hashset是由hashmap实现的
区别:

HashMap

HashSet

/div>

/h3>

p>


/p>

/h2>

/h3>

p>


/p>

/h3>

p>1、表的主键、外键必须有索引;


2、数据量超过300的表应该有索引;


3、经常与其他表进行连接的表,在连接字段上应该建立索引;


4、经常出现在Where子句中的字段,特别是大表的字段,应该建立索引;


5、索引应该建在选择性高的字段上;


6、索引应该建在小字段上,对于大的文本字段甚至超长字段,不要建索引;


7、复合索引的建立需要进行仔细分析;尽量考虑用单字段索引代替:


A、正确选择复合索引中的主列字段,一般是选择性较好的字段;


B、复合索引的几个字段是否经常同时以AND方式出现在Where子句中?单字段查询是否极少甚至没有?


如果是,则可以建立复合索引;否则考虑单字段索引;


C、如果复合索引中包含的字段经常单独出现在Where子句中,则分解为多个单字段索引;


D、如果复合索引所包含的字段超过3个,那么仔细考虑其必要性,考虑减少复合的字段;


E、如果既有单字段索引,又有这几个字段上的复合索引,一般可以删除复合索引;


8、频繁进行数据操作的表,不要建立太多的索引;


9、删除无用的索引,避免对执行计划造成负面影响;

/p>

/h3>

p>id:选择标识符


select_type:表示查询的类型。


table:输出结果集的表


partitions:匹配的分区


type:表示表的连接类型


possible_keys:表示查询时,可能使用的索引


key:表示实际使用的索引


key_len:索引字段的长度


ref:列与索引的比较


rows:扫描出的行数(估算的行数)


filtered:按表条件过滤的行百分比


Extra:执行情况的描述和说明

/p>

p>

注意

:type标识mysql的访问类型,常见的有ALL、index、range、 ref、eq_ref、const、system、NULL(从左到右,性能从差到好)


ALL:Full Table Scan, MySQL将遍历全表以找到匹配的行


index: Full Index Scan,index与ALL区别为index类型只遍历索引树


range:只检索给定范围的行,使用一个索引来选择行


ref: 表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值


eq_ref: 类似ref,区别就在使用的索引是唯一索引,对于每个索引键值,表中只有一条记录匹配,简单来说,就是多表连接中使用primary key或者 unique key作为关联条件


const、system: 当MySQL对查询某部分进行优化,并转换为一个常量时,使用这些类型访问。如将主键置于where列表中,MySQL就能将该查询转换为一个常量,system是const类型的特例,当查询的表只有一行的情况下,使用system


NULL: MySQL在优化过程中分解语句,执行时甚至不用访问表或索引,例如从一个索引列里选取最小值可以通过单独索引查找完成。

/p>

/h3>

p>索引列上不能使用表达式或函数


用or分割开的条件,如果or左右两个条件中有一个列没有索引,则不会使用索引。


复合索引,如果索引列不是复合索引的第一部分,则不使用索引(即不符合最左前缀)


如果like是以‘%’开始的,则该列上的索引不会被使用。


如果MySQL估计使用索引比全表扫描更慢,则不适用索引,


如果列为字符串,则where条件中必须将字符常量值加引号,否则即使该列上存在索引,也不会被使用

/p>

/h3>

p>


/p>

/h3>

p>在主服务器上建立新表,表结构为修改后的表结构,将旧表数据同步到新表。


在旧表中建立触发器,使旧表更新的数据同步到新表中。


数据同步完成后,在旧表中建立排它锁,重命名新表为旧表,删除旧表。


可使用工具实现上面的操作:pt-online-schema-change

/p>

/h3>

p>Mysql查看当前隔离级别:show variables like ‘%iso%’

/p>

p>隔离级别 隔离级别的值 导致的问题


Read-Uncommitted 0 导致脏读


Read-Committed 1 避免脏读,允许不可重复读和幻读


Repeatable-Read 2 避免脏读,不可重复读,允许幻读


Serializable 3 串行 化读,事务只能一个一个执行,避免了脏读、不可重复读、幻读。执行效率慢,使用时慎重


脏读

:一事务对数据进行了增删改,但未提交,另一事务可以读取到未提交的数据。如果第一个事务这时候回滚了,那么第二个事务就读到了脏数据。


不可重复读

:一个事务中发生了两次读操作,第一次读操作和第二次操作之间,另外一个事务对数据进行了修改,这时候两次读取的数据是不一致的。


幻读

:第一个事务对一定范围的数据进行批量修改,第二个事务在这个范围增加一条数据,这时候第一个事务就会丢失对新增数据的修改。


总结:


隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。


大多数的数据库默认隔离级别为 Read Commited,比如 SqlServer、Oracle


少数数据库默认隔离级别为:Repeatable Read 比如: MySQL InnoDB

/p>

/h3>

p>索引大大减少存储引擎需要扫描的数据量


索引可以帮助我们进项排序避免使用临时表


索引可以把随机io变为顺序io

/p>

/h3>

p>最左匹配原则:b+树的数据项是复合的数据结构,比如(name,age,sex)的时候,b+数是按照从左到右的顺序来建立搜索树的,比如当(张三,20,F)这样的数据来检索的时候,b+树会优先比较name来确定下一步的所搜方向,如果name相同再依次比较age和sex,最后得到检索的数据;但当(20,F)这样的没有name的数据来的时候,b+树就不知道下一步该查哪个节点,因为建立搜索树的时候name就是第一个比较因子,必须要先根据name来搜索才能知道下一步去哪里查询。比如当(张三,F)这样的数据来检索时,b+树可以用name来指定搜索方向,但下一个字段age的缺失,所以只能把名字等于张三的数据都找到,然后再匹配性别是F的数据了, 这个是非常重要的性质,即索引的最左匹配特性。

/p>

/h3>

p>SELECT * FROM table LIMIT 5,10; // 检索记录行 6-15


SELECT * FROM table LIMIT 10; // 检索前10条记录

/p>

/h3>

p>逻辑查询处理阶段简介

/p>

ol>

  • FROM:对FROM子句中的前两个表执行笛卡尔积(Cartesian product)(交叉联接),生成虚拟表VT1
  • ON:对VT1应用ON筛选器。只有那些使<join_condition>为真的行才**入VT2。
  • OUTER(JOIN):如 果指定了OUTER JOIN(相对于CROSS JOIN 或(INNER JOIN),保留表(preserved table:左外部联接把左表标记为保留表,右外 部 联接把右表标记为保留表,完全外部联接把两个表都标记为保留表)中未找到匹配的行将作为外部行添加到 VT2,生成VT3.如果FROM子句包含 两个 以上的表,则对上一个联接生成的结果表和下一个表重复执行步骤1到步骤3,直到处理完所有的表为止。
  • WHERE:对VT3应用WHERE筛选器。只有使<where_condition>为true的行才**入VT4.
  • GROUP BY:按GROUP BY子句中的列列表对VT4中的行分组,生成VT5.
  • CUBE|ROLLUP:把超组(Suppergroups)插入VT5,生成VT6.
  • HAVING:对VT6应用HAVING筛选器。只有使<having_condition>为true的组才会**入VT7.
  • SELECT:处理SELECT列表,产生VT8.
  • DISTINCT:将重复的行从VT8中移除,产生VT9.
  • ORDER BY:将VT9中的行按ORDER BY 子句中的列列表排序,生成游标(VC10).
  • TOP:从VC10的开始处选择指定数量或比例的行,生成表VT11,并返回调用者。
  • /ol>

    /h3>

    p>主要比较InnoDB和MYISAM


    InnoDB支持事务,MyISAM不支持

    ,对于InnoDB每一条SQL语言都默认封装成事务,自动提交,这样会影响速度,所以最好把多条SQL语言放在begin和commit之间,组成一个事务;


    InnoDB是聚集索引

    ,数据文件是和索引绑在一起的,必须要有主键,通过主键索引效率很高。但是辅助索引需要两次查询,先查询到主键,然后再通过主键查询到数据。因此,主键不应该过大,因为主键太大,其他索引也都会很大。

    MyISAM是非聚集索引

    ,数据文件是分离的,索引保存的是数据文件的指针。主键索引和辅助索引是独立的。


    InnoDB不保存表的具体行数

    ,执行select count(*) from table时需要全表扫描。而

    MyISAM用一个变量保存了整个表的行数

    ,执行上述语句时只需要读出该变量即可,速度很快;


    Innodb不支持全文索引,而MyISAM支持全文索引

    ,查询效率上MyISAM要高;

    /p>

    /h3>

    div>

     

    /div>

    /h2>

    /h3>

    p>提供了比synchronized更加高级的各种同步结构,包括CountDownLatch、CyclicBarrier、Semaphore等,可以实现更加丰富的多线程操作。比如利用Semaphore作为资源控制器,限制同时进行工作的线程数量。


    各种线程安全的容器,比如最常见的ConcurrentHashMap、有序的ConcurrentSkipListMap,或者通过类似快照机制,实现线程安全的动态数组CopyOnWriteArrayList等。


    各种并发队列实现,如各种BlockingQueue实现,比较典型的ArrayBlockingQueue、SynchorousQueue或针对特定场景的PriorityBlockingQueue等。


    强大的Executor框架,可以创建各种不同类型的线程池,调度任务运行等。绝大部分情况下,不再需要自己从头实现线程池和任务调度器。

    /p>

    /h3>

    p>volatile本质是在告诉jvm当前变量在寄存器中的值是不确定的,需要从主存中读取,synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住.


    volatile仅能使用在变量级别,synchronized则可以使用在变量,方法.


    volatile仅能实现变量的修改可见性,但不具备原子特性,而synchronized则可以保证变量的修改可见性和原子性.


    volatile不会造成线程的阻塞,而synchronized可能会造成线程的阻塞.


    volatile标记的变量不会被编译器优化,而synchronized标记的变量可以被编译器优化.

    /p>

    /h3>

    p>阿里的 Java开发手册,上面有线程池的一个建议:


    线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,


    这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。

    /p>

    /h3>

    p>

    /p>

    /h3>

    p>


    /p>

    /h3>

    p>

    /p>

    /h3>

    div>

     

    /div>

    /h2>

    /h3>

    /h3>

    p>|Xms| 初始堆大小,也可称最小堆大小 |


    |-Xmx | 最大堆大小 |


    |-Xmn|堆中新生代的大小 |


    |-Xss | 每个线程的堆栈大小|


    |-XX:PermSize| 设置持久代(perm gen)初始值 |


    |-XX:MaxPermSize | 设置持久代最大值 |

    /p>

    /h2>

    /h3>

    p>1)一级缓存 Mybatis的一级缓存是指SQLSession,一级缓存的作用域是SQlSession, Mabits默认开启一级缓存。 在同一个SqlSession中,执行相同的SQL查询时;第一次会去查询数据库,并写在缓存中,第二次会直接从缓存中取。 当执行SQL时候两次查询中间发生了增删改的操作,则SQLSession的缓存会被清空。 每次查询会先去缓存中找,如果找不到,再去数据库查询,然后把结果写到缓存中。 Mybatis的内部缓存使用一个HashMap,key为hashcode+statementId+sql语句。Value为查询出来的结果集映射成的java对象。 SqlSession执行insert、update、delete等操作commit后会清空该SQLSession缓存。


    2)二级缓存 二级缓存是mapper级别的,Mybatis默认是没有开启二级缓存的。 第一次调用mapper下的SQL去查询用户的信息,查询到的信息会存放代该mapper对应的二级缓存区域。 第二次调用namespace下的mapper映射文件中,相同的sql去查询用户信息,会去对应的二级缓存内取结果。 如果调用相同namespace下的mapepr映射文件中增删改sql,并执行了commit操作,此时会清空该缓存

    /p>

    /h3>

    p>#{}的执行结果是给参数添加‘’,


    美元符{}的执行结果是直接将参数替换,存在sql注入的风险。


    能用#{}的地方就不要用${}

    /p>

    /h3>

    p>如果在like语句中想使用#{},通过字符串拼接或者concat拼接的方式实现


    1、表达式: name like"%"#{name}"%"


    2、name like concat(concat(’%’,#{username}),’%’)


    concat拼接可以这么写name like CONCAT(’%’,’${name}’,’%’)


    /p>

    /h3>

    p>Spring Cloud Eureka(服务治理)


    Spring Cloud Ribbon(客户端负载均衡)


    Spring Cloud Hystrix(服务容错保护)


    Spring Cloud Feign(声明式服务调用)


    Spring Cloud Zuul(API网关服务)


    Spring Cloud Config(分布式配置中心)

    /p>

    /h3>

    p>


    /p>

    /h2>

    /h3>

    p>


    /p>

    /h3>

    p>


    /p>

    /h3>

    p>

    获取锁

    :首先在zookeeper当中创建一个持久节点ParentLock。当第一个客户端client1想要获取锁时,需要在ParentLock这个节点下面创建一个临时顺序节点Lock1,之后client1查号ParentLock下面的所有临时顺序节点并排序,判断自己所创建的加点lock1是不是顺序最靠前的一个,如果是,则获取锁成功。这时候另一个客户端client2来获取锁,则在ParentLock下在创建一个临时顺序节点lock2,client2查找ParentLock下所有的临时顺序节点并排序,判断自己创建的节点lock2是不是顺序节点里最靠前的一个,发现lock2不是最小的,于是cline2向排序比他靠前的节点lock1注册Watch,用于监听lock1节点是否存在,这意味着clinet2抢锁失败,进入等待状态。这时候又有一个客户端client3前来获取锁,同样在ParentLock下创建临时顺序节点lock3,查找所有顺序节点并排序,判断自己创建的节点lock3是不是最靠前的一个,同样发现lock3不是最小的,于是client3向排序仅比他靠前的节点lock2注册watch,用于监听lock2节点是否存在,同样意味着clinet3抢锁失败,进入等待状态。这样一来,clinet1得到了锁,client2监听了client1,clinet3监听了clinet2,恰好行程一个等待队列。


    释放锁

    :释放锁可以分为两种情况


    当client1任务完成时,会显式的调用少出节点Lock1的指令来释放锁


    当任务执行过程中,客户端奔溃,则client1会断开与zookeeper的连接,根据临时节点的特新,Lock1节点也会随之自动删除。由于client2一直监听这lock1的状态,当lock1节点删除后,client2会立刻受到通知,这时候client2在查询ParentLock下的所有节点,确认自己创建的节点lock2是不是最小的节点,如果是,则clinet2获取锁。

    /p>

    /h3>

    p>那我们如何去遍历大数据量呢?这个也是面试经常问的。我们可以采用redis的另一个命令scan。我们看一下scan的特点


    1、复杂度虽然也是 O(n),但是它是通过游标分步进行的,不会阻塞线程


    2、提供 count 参数,不是结果数量,是redis单次遍历字典槽位数量(约等于)


    3、同 keys 一样,它也提供模式匹配功能;


    4、服务器不需要为游标保存状态,游标的唯一状态就是 scan 返回给客户端的游标整数;


    5、返回的结果可能会有重复,需要客户端去重复,这点非常重要;


    6、单次返回的结果是空的并不意味着遍历结束,而要看返回的游标值是否为零。


    一、scan命令格式


    SCAN cursor [MATCH pattern] [COUNT count]


    二、命令解释:scan 游标 MATCH <返回和给定模式相匹配的元素> count 每次迭代所返回的元素数量


    SCAN命令是增量的循环,每次调用只会返回一小部分的元素。所以不会让redis假死SCAN命令返回的是一个游标,从0开始遍历,到0结束遍历


    三、举例


    redis > scan 0match user_token* count 5

    /p>

    ol>

  • “6”
  • /ol>

    ol data-indent="1">

  • “user_token:1000”
  • /ol>

    ol start="2">

  • “user_token:1001”
  • “user_token:1010”
  • “user_token:2300”
  • “user_token:1389”
    从0开始遍历,返回了游标6,又返回了数据,继续scan遍历,就要从6开始
    redis > scan 6match user_token* count 5
  • “10”
  • /ol>

    ol data-indent="1">

  • “user_token:3100”
  • /ol>

    ol start="7">

  • “user_token:1201”
  • “user_token:1410”
  • “user_token:5300”
  • “user_token:3389”
  • /ol>

    /h3>

    p>


    /p>

    /h3>

    ol>

  • client一个线程调用远程接口,生成一个唯一的ID(比如一段随机字符串,UUID等),Dubbo是使用AtomicLong从0开始累计数字的
  • 将打包的方法调用信息(如调用的接口名称,方法名称,参数值列表等),和处理结果的回调对象callback,全部封装在一起,组成一个对象object
  • 向专门存放调用信息的全局ConcurrentHashMap里面put(ID, object)
  • 将ID和打包的方法调用信息封装成一对象connRequest,使用IoSession.write(connRequest)异步发送出去
  • 当前线程再使用callback的get()方法试图获取远程返回的结果,在get()内部,则使用synchronized获取回调对象callback的锁, 再先检测是否已经获取到结果,如果没有,然后调用callback的wait()方法,释放callback上的锁,让当前线程处于等待状态。
  • 服务端接收到请求并处理后,将结果(此结果中包含了前面的ID,即回传)发送给客户端,客户端socket连接上专门监听消息的线程收到消息,分析结果,取到ID,再从前面的ConcurrentHashMap里面get(ID),从而找到callback,将方法调用结果设置到callback对象里。
  • 监听线程接着使用synchronized获取回调对象callback的锁(因为前面调用过wait(),那个线程已释放callback的锁了),再notifyAll(),唤醒前面处于等待状态的线程继续执行(callback的get()方法继续执行就能拿到调用结果了),至此,整个过程结束。
    需要注意的是,这里的callback对象是每次调用产生一个新的,不能共享,否则会有问题;另外ID必需至少保证在一个Socket连接里面是唯一的。
    两个问题
    · 当前线程怎么让它“暂停”,等结果回来后,再向后执行?
    答:先生成一个对象obj,在一个全局map里put(ID,obj)存放起来,再用synchronized获取obj锁,再调用obj.wait()让当前线程处于等待状态,然后另一消息监听线程等到服务端结果来了后,再map.get(ID)找到obj,再用synchronized获取obj锁,再调用obj.notifyAll()唤醒前面处于等待状态的线程。
    · 正如前面所说,Socket通信是一个全双工的方式,如果有多个线程同时进行远程方法调用,这时建立在client server之间的socket连接上会有很多双方发送的消息传递,前后顺序也可能是乱七八糟的,server处理完结果后,将结果消息发送给client,client收到很多消息,怎么知道哪个消息结果是原先哪个线程调用的?
    答:使用一个ID,让其唯一,然后传递给服务端,再服务端又回传回来,这样就知道结果是原先哪个线程的了。
  • /ol>

    /h3>

    p>

    string介绍

    string是以一种纯字符串作为value的形式存在的。也是这几种之中使用最多的数据结构。value可以存储json格式、数值型等。


    string使用场景

    string使用场景一般是存储简单的键值类型。比如用户信息,登录信息,配置信息等。还有一种用得比较多的是string的incr/decr操作,即自减/自增操作。调用它是原子性的,无论调用多少次,都一一计算成功。例如需要增减库存的操作。


    尽管string的value可以存储很大,甚至500多MB的容量。但是在性能上来说,我们尽量存储value的值不要过1MB。


    hash介绍

    hash是一个集合,使用过hash的人都知道,hash的读取性能都一样快。在redis中,hash因为是一个集合,所以有两层。第一层是key:hash集合value,第二层是hashkey:string value。所以判断是否采用hash的时候可以参照有两层key的设计来做参考。并且注意的是,设置过期时间只能在第一层的key上面设置。


    hash用场景

    使用hash,一般是有那种需要两层key的应用场景,也可以是‘删除一个key可以删除所有内容’的场景。例如一个商品有很多规格,规格里面有不同的值。


    如果需要删除商品时,可以一次性删除‘商品id’的key,则商品里面的所有规格也会删除,而不需要找到对应的规格再做处理。如果查找商品id与规格id1的商品时,则通过两个key查找即可。或者查找所有商品的规格,查找商品id即可。


    需要注意的是,经过测试,在性能上来说一般hash里面的第二层key,不要超过200个为佳。尽管hash里面的key-value能达到500多MB的存储容量。


    list介绍

    list是一个集合,而在redis中,list的使用场景多种多样。在redis中,插入list中的值,只需要找到list的key即可,而不需要像hash一样插入两层的key。list是一种有序的、可重复的集合。


    list使用场景

    list可以使用左推、左拉、右推、右拉的方式。所以你可以使用list作为集合存储,比如存储某宝商铺里面的所有商品。


    也可以用作轻量级别的队列来使用。左推左拉、右推右拉。


    需要注意的是尽管redis可以使用推拉的队列模式,但是一定要注意场景。因为redis的队列是一种轻量级别的,没有队列重试、队列重放机制。消费完队列消息在redis代表已经删除了。


    set介绍

    set是一种无序的,不能重复的集合。并且在redis中,只有一个key。


    set使用场景

    如保存一些标签的名字。标签的名字不可以重复,顺序是可以无序的。


    需要注意的是使用set一定不要存储大量的数据。value的值不宜过大,并且集合数量不宜过大。几百个集合的值,value不超过1MB为佳。


    sortset介绍

    sortset在redis中是有序的,并且不能重复。既有list的有序,又有set的不可重复性。


    sortset使用场景

    sortset的使用场景一般是排行榜之类的场景。

    /p>

    /h3>

    p>Set数据结构类似于Set结构,只是ZSet结构中,每个元素都会有一个分值,然后所有元素按照分值的大小进行排列,相当于是一个进行了排序的链表。


    如果ZSet是一个链表,而且内部元素是有序的,在进行元素插入和删除,以及查询的时候,就必须要遍历链表才行,时间复杂度就达到了O(n),这个在以单线程处理的Redis中是不能接受的。所以ZSet采用了一种跳跃表的实现。


    跳跃表因为是一个根据分数权重进行排序的列表,可以再很多场景中进行应用,比如排行榜等等。

    /p>

    /h3>

    p>Redis String类型 没有使用C语言传统的字符串表示,而是自己构建了一种名为简单动态字符串(simple dynamic string,SDS) 的抽象类型,并将SDS用作Redis的默认字符串表示。在Redis里,包含字符串值得键值对在底层都是由DS来实现的。


    SDS还被用作缓冲区(buffer):AOF模块中的AOF缓冲区,以及客户端状态中的输入缓冲区,都是由SDS实现的。


    SDS相比较于c字符串的好处

    取字符长度,SDS直接读取len属性。


    杜绝缓冲区溢出。C字符串拼接时时假定已经为拼接的字符串预留了足够多的内存,如果这个假定不成立,那么就会产生缓冲区溢出。而SDS是这样做的:SDS的API会会先检查SDS的空间是否满足所需的要求,如果不满足,API自动将空间扩展至所需大小。


    减少修改字符串长度时所需的内存重分配次数。


    二进制安全。


    兼容部分C字符串函数。


    SDS空间分配策略:

    通过未使用空间,SDS实现了空间预分配和惰性空间释放两种优化策略。


    空间预分配: 用于优化SDS的字符增长操作:程序不仅会为SDS分配修改所必须要的空间,还会为SDS分配额外的未使用空间。(具体:1. len(SDS)< 1mB时,分配len(free)=len,2.如果len(SDS) >1mb,分配len(free)=1mb)


    惰性空间释放: 用于优化SDS字符缩短操作:当缩短字符串时,程序并不立即使用内存重分配来回收缩短后多出来的字节,而是使用free属性将这些字节的数量记录起来,并等待将来使用。

    /p>

    /h2>

    /h3>

    div>

     

    /div>

    /h3>

    /h3>

    /h3>

    /h3>

    /h3>

    /h2>

    /h3>

    /h3>

    版权声明


    相关文章:

  • 求利用JAVA基础写出谁吃食物2024-10-23 10:26:00
  • java前端入门零基础2024-10-23 10:26:00
  • java学基础班都学2024-10-23 10:26:00
  • Java基础飞机大战2024-10-23 10:26:00
  • 零基础适合学java还是python2024-10-23 10:26:00
  • 高新兴java开发基础工资2024-10-23 10:26:00
  • JAVA一些基础概念2024-10-23 10:26:00
  • java基础教程第5天2024-10-23 10:26:00
  • java基础部分的代码案例2024-10-23 10:26:00
  • java map的基础语句2024-10-23 10:26:00