// 若依重写的StringUtils下的equals
public static boolean equals(CharSequence cs1, CharSequence cs2) {
if (cs1 == cs2) {
return true;
} else if (cs1 != null && cs2 != null) {
if (cs1.length() != cs2.length()) {
return false;
} else if (cs1 instanceof String && cs2 instanceof String) {
return cs1.equals(cs2);
} else {
int length = cs1.length();
for(int i = 0; i < length; ++i) {
if (cs1.charAt(i) != cs2.charAt(i)) {
return false;
}
}
return true;
}
} else {
return false;
}
}
不属于。
八种基本数据类型:byte short int long char float double boolean。
String str =“i” 会将其分配到常量池中,常量池中没有重复的元素。
如果常量池中存在i,就将 i 的地址直接赋给变量; 如果没有就创建一个再赋给变量。
String str=new String(“i”)会将对象分配到堆中,即使内存一样,还是会重新创建一个新的对象。
String s = new String(a);
创建1个或2个对象。
若常量池中已经存在"a",那么就不会再在常量池中创建对象,⽽是执⾏new操作,在堆中创建⼀个存储"a"的String对象,对象的引⽤
赋值给s,这样就创建了1个对象;
若常量池中没有"a",就会在常量池中创建⼀个对象"a",然后再执⾏new操作,在堆中创建⼀个存储"a"的String对象,对象的引⽤赋值给s,这样就创建了2个对象。
注意: 这⾥的情况其实堆中的String对象的char value[]属性指向常量池中的char> value[],可以通过debug的形式查看两个String对象的> value值的引⽤相同,也就是说,虽然是两个对象,但它们的value值均指向常量池中的同⼀个地址。上述过程中检查常量池是否有相同Unicode的字符串常量时,使⽤的⽅法便是String中的intern()⽅法。
String s ="a"+"b";
这⾥涉及到字符串常量重载“+”的问题,当⼀个字符串由多个字符串常量拼接成⼀个字符串时,它⾃⼰也肯定是字符串常量。字符串常量的“+”号连接Java虚拟机会在程序编译期将其优化为连接后的值。 在编译时已经被合并成“ab”字符串,因此,只会创建1个对象。并没有创建临时字符串对象a和b,这样减轻了垃圾收集器的压⼒。
String s ="a"+new String("b");
创建5个对象。
上述的代码Java虚拟机在编译的时候同样会优化,会创建⼀个StringBuilder来进⾏字符串的拼接,实际效果类似:
String s =new String("b");
new StringBuilder().append("a").append(s).toString()
很显然,常量池中分别有“a”和“b”,堆中对象new String(“b”)和“ab”,还多出了⼀个StringBuilder对象,那就应该是5个对象。
疑问:
为什么会有⼈说,StringBuilder最后toString()之后的“ab”难道不在常量池存⼀份吗?这样不就是6个对象?
解析:
答案是不对的。
StringBuilder的toString()⽅法是如何将拼接的结果转化为字符串的:
@Override
public String toString(){
// Create a copy, don't share the array
return new String(value,0, count);
}
很显然,在toString⽅法中⼜新创建了⼀个String对象,⽽该String对象传递数组的构造⽅法来创建的:
public String(char value[],int offset,int count)
也就是说,String对象的value值直接指向了⼀个已经存在的数组,⽽并没有指向常量池中的字符串。
将对象封装到stringBuilder中,调用reverse方法反转。
String str = "asdfg";
StringBuilder stringBuilder = new StringBuilder(str);
String ret =stringBuilder.reverse().toString();
System.out.println(ret); //gfdsa
Java中Object有一个方法:
public native int hashcode();
Collection是最基本的集合接口,Collection派生了两个子接口list和set,分别定义了两种不同的存储方式。
Collections是一个包装类,它包含各种有关集合操作的静态方法(对集合的搜索、排序、线程安全化等)。
此类不能实例化,就像一个工具类,服务于Collection框架。
1、List简介
实际上有两种List:一种是基本的ArrayList,其优点在于随机访问元素,另一种是LinkedList,它并不是为快速随机访问设计的,而是快速的插入或删除。
ArrayList:由数组实现的List。允许对元素进行快速随机访问,但是向List中间插入与移除元素的速度很慢。
LinkedList :对顺序访问进行了优化,向List中间插入与删除的开销并不大。随机访问则相对较慢。
还具有下列方 法:addFirst(), addLast(), getFirst(), getLast(), removeFirst() 和 removeLast(), 这些方法 (没有在任何接口或基类中定义过)使得LinkedList可以当作堆栈、队列和双向队列使用。
2、Set简介
Set具有与Collection完全一样的接口,因此没有任何额外的功能。实际上Set就是Collection,只是行为不同。这是继承与多态思想的典型应用:表现不同的行为。Set不保存重复的元素(至于如何判断元素相同则较为负责) 。
Set : 存入Set的每个元素都必须是唯一的,因为Set不保存重复元素。加入Set的元素必须定义equals()方法以确保对象的唯一性。Set与Collection有完全一样的接口。Set接口不保证维护元素的次序。
HashSet:为快速查找设计的Set。存入HashSet的对象必须定义hashCode()。
TreeSet: 保存次序的Set, 底层为树结构。使用它可以从Set中提取有序的序列。
3、list与Set区别
(1)List,Set都是继承自Collection接口
(2)List特点:元素有放入顺序,元素可重复 ,Set特点:元素无放入顺序,元素不可重复,重复元素会覆盖掉,(元素虽然无放入顺序,但是元素在set中的位置是有该元素的HashCode决定的,其位置其实是固定的,加入Set 的Object必须定义equals()方法 ,另外list支持for循环,也就是通过下标来遍历,也可以用迭代器,但是set只能用迭代,因为他无序,无法用下标来取得想要的值。)
(3)Set和List对比:
Set:检索元素效率低下,删除和插入效率高,插入和删除不会引起元素位置改变。
List:和数组类似,List可以动态增长,查找元素效率高,插入删除元素效率低,因为会引起其他元素位置改变。
1、静态只能访问静态。
2、非静态既可以访问非静态的,也可以访问静态的
ArrayList是动态数组的数据结构实现,查找和遍历的效率较高;
LinkedList 是双向链表的数据结构,增加和删除的效率较高;
String[] arr = {"zs","ls","ww"};
List<String> list = Arrays.asList(arr);
System.out.println(list);
ArrayList<String> list1 = new ArrayList<String>();
list1.add("张三");
list1.add("李四");
list1.add("王五");
String[] arr1 = list1.toArray(new String[list1.size()]);
System.out.println(arr1);
for(int i = 0; i < arr1.length; i++){
System.out.println(arr1[i]);
}
1、offer()和add()区别:
增加新项时,如果队列满了,add会抛出异常,offer返回false。
2、poll()和remove()区别:
poll()和remove()都是从队列中删除第一个元素,remove抛出异常,poll返回null。
3、peek()和element()区别:
peek()和element()用于查询队列头部元素,为空时element抛出异常,peek返回null。
为了方便的处理集合中的元素,Java中出现了一个对象,该对象提供了一些方法专门处理集合中的元素.例如删除和获取集合中的元素.该对象就叫做迭代器(Iterator)。
List<String> list = new ArrayList<>();
Iterator<String> it = list. iterator();
while(it. hasNext()){
String obj = it. next();
System. out. println(obj);
}
Iterator 接口源码中的方法:
集合(map,set,list…)都是引用类型,所以我们如果用final修饰的话,集合里面的内容还是可以修改的。
我们可以采用Collections包下的unmodifiableMap方法,通过这个方法返回的map,是不可以修改的。他会报 java.lang.UnsupportedOperationException错。
@Test
public void test13(){
Map<String,String> map = new HashMap<>();
map.put("1","a");
map.put("1","a");
map.put("1","a");
Map<String, String> stringStringMap = Collections.unmodifiableMap(map);
stringStringMap.put("1","a");
System.out.println(stringStringMap);
}
同理:Collections包也提供了对list和set集合的方法。
Collections.unmodifiableList(List)
Collections.unmodifiableSet(Set)
ArrayList 不是线程安全的,如果遇到多线程场景,可以通过 Collections 的 synchronizedList 方法将其转换成线程安全的容器后再使用。例如像下面这样:
List<String> synchronizedList = Collections.synchronizedList(list);
synchronizedList.add("aaa");
synchronizedList.add("bbb");
for (int i = 0; i < synchronizedList.size(); i++) {
System.out.println(synchronizedList.get(i));
}
HashSet 是基于 HashMap 实现的,HashSet的值存放于HashMap的key上,HashMap的value统一为present,因此 HashSet 的实现比较简单,相关 HashSet 的操作,基本上都是直接调用底层HashMap 的相关方法来完成,HashSet 不允许重复的值。
private static final Object PRESENT = new Object();
private transient HashMap<E,Object> map;
public HashSet() {
map = new HashMap<>();
}
public Boolean add(E e) {
// 调用HashMap的put方法,PRESENT是一个至始至终都相同的虚值
return map.put(e, PRESENT)==null;
}
hashCode()方法的返回值和equals()方法的关系如下:
x.equals(y)返回true,即两个对象根据equals()方法比较是相等的,那么调用这两个对象中任意一个对象的hashCode()方法都必须产生同样的整数结果。如果x.equals(y)返回false,即两个对象根据equals()方法比较是不相等的,那么x和y的hashCode()方法的返回值有可能相等,也有可能不相等。反之,hashCode()方法的返回值不相等,一定能推出equals()方法的返回值也不相等,而hashCode()方法的返回值相等,equals()方法的返回值则可能相等,也可能不相等。
通过Spring Boot,可以轻松地创建独立的,基于生产级别的Spring的应用程序,您可以“运行”它们。大 多数Spring Boot应用程序需要最少的Spring配置。
快速开发,快速整合,配置简化、内嵌服务容器
启动类上面的注解是@SpringBootApplication,它也是 Spring Boot 的核心注解,主要组合包含了以下 3 个注解:
分类 | 类型名称 | 说明 |
---|---|---|
整数类型 | tinyint | 1个字节(8位二进制) |
smallint | 2个字节(16位二进制) | |
mediumint | 3个字节(24位二进制) | |
smallint | 2个字节(16位二进制) | |
小数类型 | float | 单精度浮点数 |
double | 双精度浮点数 | |
float | 单精度浮点数 | |
日期类型 | year | YYYY 1901~2155 |
time | HH:MM:SS -838:59:59~838:59:59 | |
date | YYYY-MM-DD 1000-01-01~9999-12-3 | |
datetime | YYYY-MM-DD HH:MM:SS 1000-01-01 00:00:00~ 9999-12-31 23:59:59 | |
timestamp | YYYY-MM-DD HH:MM:SS 19700101 00:00:01 UTC~2038-01-19 03:14:07UTC | |
文本、二进制类型 | CHAR(M) | M为0~255之间的整数 |
VARCHAR(M) | M为0~65535之间的整数 | |
TINYBLOB | 允许长度0~255字节 | |
BLOB | 允许长度0~65535字节 | |
MEDIUMBLOB | 允许长度0~167772150字节 | |
LONGBLOB | 允许长度0~4294967295字节 | |
TINYTEXT | 允许长度0~255字节 | |
TEXT | 允许长度0~65535字节 | |
MEDIUMTEXT | 允许长度0~167772150字节 | |
LONGTEXT | 允许长度0~4294967295字节 | |
VARBINARY(M) | 允许长度0~M个字节的变长字节字符串 | |
BINARY(M) | 允许长度0~M个字节的定长字节字符串 |
1、整数类型,包括TINYINT、SMALLINT、MEDIUMINT、INT、BIGINT,分别表示1字节、2字节、3字节、4字节、8字节整数。任何整数类型都可以加上UNSIGNED属性,表示数据是无符号的,即非负整数。
长度:整数类型可以被指定长度,例如:INT(11)表示长度为11的INT类型。长度在大多数场景是没有意义的,它不会限制值的合法范围,只会影响显示字符的个数,而且需要和UNSIGNED ZEROFILL属性配合使用才有意义。
例子,假定类型设定为INT(5),属性为UNSIGNED ZEROFILL,如果用户插入的数据为12的话,那么数据库实际存储数据为00012。
2、实数类型,包括FLOAT、DOUBLE、DECIMAL。
DECIMAL可以用于存储比BIGINT还大的整型,能存储精确的小数。
而FLOAT和DOUBLE是有取值范围的,并支持使用标准的浮点进行近似计算。
计算时FLOAT和DOUBLE相比DECIMAL效率更高一些,DECIMAL你可以理解成是用字符串进行处理。
3、字符串类型,包括VARCHAR、CHAR、TEXT、BLOB
VARCHAR用于存储可变长字符串,它比定长类型更节省空间。
VARCHAR使用额外1或2个字节存储字符串长度。列长度小于255字节时,使用1字节表示,否则使用2字节表示。
VARCHAR存储的内容超出设置的长度时,内容会被截断。
CHAR是定长的,根据定义的字符串长度分配足够的空间。
CHAR会根据需要使用空格进行填充方便比较。
CHAR适合存储很短的字符串,或者所有值都接近同一个长度。
CHAR存储的内容超出设置的长度时,内容同样会被截断。
使用策略:
对于经常变更的数据来说,CHAR比VARCHAR更好,因为CHAR不容易产生碎片。
对于非常短的列,CHAR比VARCHAR在存储空间上更有效率。
使用时要注意只分配需要的空间,更长的列排序时会消耗更多内存。
尽量避免使用TEXT/BLOB类型,查询时会使用临时表,导致严重的性能开销。
4、枚举类型(ENUM),把不重复的数据存储为一个预定义的集合。
有时可以使用ENUM代替常用的字符串类型。
ENUM存储非常紧凑,会把列表值压缩到一个或两个字节。
ENUM在内部存储时,其实存的是整数。
尽量避免使用数字作为ENUM枚举的常量,因为容易混乱。
排序是按照内部存储的整数
5、日期和时间类型,尽量使用timestamp,空间效率高于datetime,
用整数保存时间戳通常不方便处理。
如果需要存储微妙,可以使用bigint存储。
看到这里,这道真题是不是就比较容易回答了。
插入缓冲(insert buffer)
二次写(double write)
自适应哈希索引(ahi)
预读(read ahead)
更通俗的说,索引就相当于目录。为了方便查找书中的内容,通过对内容建立索引形成目录。索引是一个文件,它是要占据物理空间的。
可以大大加快数据的检索速度,这也是创建索引的最主要的原因。
通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能。
时间方面:创建索引和维护索引要耗费时间,具体地,当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,会降低增/改/删的执行效率;
空间方面:索引需要占物理空间。
#{}带引号,${}不带引号。
*(1) ’% ${question}%’ 可能引起SQL注入,不推荐;
*(2)“%”#{question}“%” 注意:因为#{…}解析成sql语句时候,会在变量外侧自动加单引号’ ',所以这里 % 需要使用双引号" ",不能使用单引号 ’ ',不然会查不到任何结果。
${}常用于数据库表名,order by 子句;
一般能用#{}就不要使用${};
CONCAT(’%’,#{question},’%’) 使用CONCAT()函数,推荐
使用bind标签
<select id="listUserByUsername" resultType="com.cuicuicui.pojo.User">
<bind name="pattern" value="'%' + username + '%'" />
select id,sex,age,username,password from person where username LIKE #{pattern}
</select>
<resultMap>,
<parameterMap>,
<sql>:sql片段标签
<include>:引入sql片段
<selectKey>:指定主键的生成策略并且拿回主键的值
比如
<selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
SELECT LAST_INSERT_ID()
</selectKey>
insert into t_user (username,password,create_date) values(#{username},#{password},#{createDate})
</insert>
会将 SELECT LAST_INSERT_ID()的结果放入到传入的model的主键里面
<selectKey keyProperty="id" order="BEFORE" resultType="String">
select uuid()
</selectKey>
insert into t_customer (id,c_name,c_sex,c_ceroNo,c_ceroType,c_age)
values (#{id},#{name},#{sex},#{ceroNo},#{ceroType},#{age})
</insert>
会将生成的uuid放入model的主键中去。
还有几个动态sql的标签:
trim,where,set,foreach,if,choose,when,otherwise,bind
接口的全限定名对应着映射文件的namespace的值,接口的方法可以和xml的全限定名加上标签的id对应。
比如com.jd.dao.UserDao.findAll()对应着命名空间为com.jd.UserDao的id为findAll的sql。
工作原理是JDK动态代理。Mybatis运行时会使用jdk动态代理生成代理对象,拦截接口方法转而执行MappedStatement的sql,然后返回结果。
可以重载,但是对应的xml的MappedStatement的id只能有一个。
比如
List<Student> getAllStu();
List<Student> getAllStu(@Param("id") Integer id);
MappedStatement只能一个
<select id="getAllStu" resultType="com.pojo.Student">
select * from student
<where>
<if test="id!=null">
id=#{id}
</if>
</where>
</select>
支持。association(一对一)和collection(一对多)关联集合对象都支持。
原理是:使用CGLIB创建目标对象的代理对象,当调用目标对象时,进入拦截器方法,这个方法中调用比如a.getB().getName(),拦截器的invoke会发现a.getB()是空值,就会查询关联对象的sql,把B也查出来。
三种基本的执行器:
所有的xml配置信息都封装到Configuration内部。
被解析为ParameterMap对象。
被解析为ResultMap对象,子元素被解析为ResultMapping对象
每个,,,都被解析为MappedStatement对象
标签内的sql被解析为BoundSql对象
1、 创建SqlSessionFactory
2、 通过SqlSessionFactory创建SqlSession
3、 通过sqlsession执行数据库操作
4、 调用session.commit()提交事务
5、 调用session.close()关闭会话
6、如图所示:
foreach标签的属性主要有item,index,collection,open,separator,close。
//推荐使用
<insert id="addEmpsBatch">
INSERT INTO emp(ename,gender,email,did)
VALUES
<foreach collection="emps" item="emp" separator=",">
(#{emp.eName},#{emp.gender},#{emp.email},#{emp.dept.id})
foreach>
insert>
<insert id="insertUser" useGeneratedKeys="true" keyProperty="userId" >
insert into user(
user_name, user_password, create_time)
values(#{userName}, #{userPassword} , #{createTime, jdbcType= TIMESTAMP})
insert>
<insert id="insertUser" >
<selectKey keyColumn="id" resultType="long" keyProperty="userId" order="BEFORE">
SELECT USER_ID.nextval as id from dual
</selectKey>
insert into user(
user_id,user_name, user_password, create_time)
values(#{userId},#{userName}, #{userPassword} , #{createTime, jdbcType= TIMESTAMP})
</insert>
扩展
- 如果Mysql 使用selectKey的方式获取主键,需要注意下面两点:
- order : AFTER
- 获取递增主键值 :SELECT LAST_INSERT_ID()
<select id="getOrder" parameterType="int" resultMap="orderResultMap">
select * from orders where order_id=#{id}
select>
<resultMap type="com.cuicuicui.pojo.Order" id="orderResultMap">
<!–用id属性来映射主键字段–>
<id property="id" column="order_id">
<!–用result属性来映射非主键字段,property为实体类属性名,column为数据库表中的属性–>
<result property ="orderno" column ="order_no"/>
<result property="price" column="order_price" />
reslutMap>
Redis 是一个使用 C 语言写成的,开源的高性能key-value非关系缓存数据库。Redis 是完全开源免费的,遵守 BSD 协议,是一个高性能的 key-value 数据库。
答:Redis 支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及 zsetsorted set:有序集合)。
Memcached 所有的值均是简单的字符串,redis 作为其替代者,支持更为丰富的数据类
Redis 的速度比 Memcached 快很
Redis 可以持久化其数据
答:Redis 是单进程单线程的,redis 利用队列技术将并发访问变为串行访问,消除了传统数据库串行控制的开销。
512M
Redis提供两种持久化机制 RDB 和 AOF 机制:
优点:
缺点:
优点:
缺点:
Master 最好不要写内存快照,如果 Master 写内存快照,save 命令调度 rdbSave函数,会阻塞主线程的工作,当快照比较大时对性能影响是非常大的,会间断性暂停服务
如果数据比较重要,某个 Slave 开启 AOF 备份数据,策略设置为每秒同步一
为了主从复制的速度和连接的稳定性,Master 和 Slave 最好在同一个局域网
尽量避免在压力很大的主库上增加从
主从复制不要用图状结构,用单向链表结构更为稳定,即:Master <- Slave1<- Slave2 <- Slave3…这样的结构方便解决单点故障问题,实现 Slave 对 Master的替换。如果 Master 挂了,可以立刻启用 Slave1 做 Master,其他不变。
volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰;
volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰;
volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰;
allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰;
allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰;
no-enviction(驱逐):禁止驱逐数据;
注意这里的 6 种机制,volatile 和 allkeys 规定了是对已设置过期时间的数据集淘汰数据还是从全部数据集淘汰数据,后面的 lru、ttl 以及 random 是三种不同的淘汰策略,再加上一种 no-enviction 永不回收的策略。
使用策略规则:
(1)如果数据呈现幂律分布,也就是一部分数据访问频率高,一部分数据访问频率低,则使用 allkeys-lru;
(2)如果数据呈现平等分布,也就是所有的数据访问频率都相同,则使用allkeys-random;
maxmemory-policy noeviction #配置策略
maxmemory <bytes> #配置内存大小 阈值
Redis为了达到最快的读写速度,将数据都读到内存中,并通过异步的方式将数据写入磁盘。所以Redis具有快速和数据持久化的特征。如果不将数据放在内存中,磁盘I/O速度会严重影响Redis的性能,在内存越来越便宜的今天,redis会越来越收欢迎,如果设置了最大使用的内存,则数据已有数据数达到内存限值后 ,不能继续插入新值。
Redis可以使用主从同步,从从同步。第一次同步时,主节点做一次bgsave,并同时将后续修改操作记录到内存buffer中,待完成后将rdb文件全量同步到复制节点,复制节点接受完成后将rdb镜像加载到内存中。
有 A,B,C 三个节点的集群,在没有复制模型的情况下,如果节点 B 失败了,那么整个集群就会以为缺少 5501-11000 这个范围的槽而不可用。
设置密码:config set requirepass 123456
授权密码:auth 123456
Redis 集群没有使用一致性 hash,而是引入了哈希槽的概念,Redis 集群有16384 个哈希槽,每个 key 通过 CRC16 校验后对 16384 取模来决定放置哪个槽,集群的每个节点负责一部分 hash 槽。
异步复制
16384
ping命令
事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。
MULTI、EXEC、DISCARD、WATCH
EXPIRE、PERSIST
尽可能使用散列表(hashes),散列表(是说散列表里面存储的数少)使用的内存非常小,所以你应该尽可能的将你的数据模型抽象到一个散列表里面。比如你的 web 系统中有一个用户对象,不要为这个用户的名称,姓氏,邮箱,密码设置单独的 key,而是应该把这个用户的所有信息存储到一张散列表里面。
一个客户端运行了新的命令,添加了新的数据。Redi 检查内存使用情况,如果大于 maxmemory 的限制, 则根据设定好的策略进行回收。一个新的命令被执行。
如果你使用的是 32 位的 Redis 实例,可以好好利用 Hash,list,sorted set,set等集合类型数据,因为通常情况下很多小的 Key-Value 可以用更紧凑的方式存放到一起。
如果达到设置的上限,Redis 的写命令会返回错误信息(但是读命令还可以正常返回。)或者你可以将 Redis 当缓存来使用配置淘汰机制,当 Redis 达到内存上限时会冲刷掉旧的内容。
理论上 Redis 可以处理多达 232 的 keys,并且在实际中进行了测试,每个实例至少存放了 2 亿 5 千万的 keys。我们正在测试一些较大的值。任何 list、set、和 sorted set 都可以放 232 个元素。换句话说,Redis 的存储极限是系统中的可用内存值。
Redis 内存数据集大小上升到一定大小的时候,就会施行数据淘汰策略。
相关知识:Redis 提供 6 种数据淘汰策略:
volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
no-enviction(驱逐):禁止驱逐数据
使用 keys 指令可以扫出指定模式的 key 列表。
对方接着追问:如果这个 redis 正在给线上的业务提供服务,那使用 keys 指令会有什么问题?
这个时候你要回答 redis 关键的一个特性:redis 的单线程的。keys 指令会导致线程阻塞一段时间,线上服务会停顿,直到指令执行完毕,服务才能恢复。这个时候可以使用 scan 指令,scan 指令可以无阻塞的提取出指定模式的 key 列表,但是会有一定的重复概率,在客户端做一次去重就可以了,但是整体所花费的时间会比直接用 keys 指令长。
如果大量的 key 过期时间设置的过于集中,到过期的那个时间点,redis 可能会出现短暂的卡顿现象。一般需要在时间上加一个随机值,使得过期时间分散一些。
一般使用 list 结构作为队列,rpush 生产消息,lpop 消费消息。当 lpop 没有消息的时候,要适当 sleep 一会再重试。如果对方追问可不可以不用 sleep 呢?list 还有个指令叫 blpop,在没有消息的时候,它会阻塞住直到消息到来。如果对方追问能不能生产一次消费多次呢?使用 pub/sub 主题订阅者模式,可以实现1:N 的消息队列。
如果对方追问 pub/sub 有什么缺点?
在消费者下线的情况下,生产的消息会丢失,得使用专业的消息队列如 RabbitMQ等。
如果对方追问 redis 如何实现延时队列?
我估计现在你很想把面试官一棒打死如果你手上有一根棒球棍的话,怎么问的这么详细。但是你很克制,然后神态自若的回答道:使用 sortedset,拿时间戳作为score,消息内容作为 key 调用 zadd 来生产消息,消费者用 zrangebyscore 指令获取 N 秒之前的数据轮询进行处理。到这里,面试官暗地里已经对你竖起了大拇指。但是他不知道的是此刻你却竖起了中指,在椅子背后。
先拿 setnx 来争抢锁,抢到之后,再用 expire 给锁加一个过期时间防止锁忘记了释放。
这时候对方会告诉你说你回答得不错,然后接着问如果在 setnx 之后执行 expire之前进程意外 crash 或者要重启维护了,那会怎么样?这时候你要给予惊讶的反馈:唉,是喔,这个锁就永远得不到释放了。紧接着你需要抓一抓自己得脑袋,故作思考片刻,好像接下来的结果是你主动思考出来的,然后回答:我记得 set 指令有非常复杂的参数,这个应该是可以同时把 setnx 和expire 合成一条指令来用的!对方这时会显露笑容,心里开始默念:摁,这小子还不错。
异步处理 - 相比于传统的串行、并行方式,提高了系统吞吐量。
应用解耦 - 系统间通过消息通信,不用关心其他系统的处理。
流量削锋 - 可以通过消息队列长度控制请求量;可以缓解短时间内的高并发请求。
日志处理 - 解决大量日志传输。
消息通讯 - 消息队列一般都内置了高效的通信机制,因此也可以用在纯的消息通讯。比如实现点对点消息队列,或者聊天室等。
是什么,简介,概述
有什么用,用途,使用场景
怎么用,在实际开发中的应用,注意事项
优缺点
框架原理,工作流程,工作原理
常见面试题
源码分析,核心类,核心方法,设计模式
发布博客,在开发和实践中,博客反馈中持续改进
与同事朋友交流,技术论坛,技术分享中持续丰富知识
集成开发工具(IDE):Eclipse、MyEclipse、Spring Tool Suite(STS)、Intellij IDEA、NetBeans、JBuilder、JCreator
JAVA服务器:tomcat、jboss、websphere、weblogic、resin、jetty、apusic、apache
负载均衡:nginx、lvs
web层框架:Spring MVC、Struts2、Struts1、Google Web Toolkit(GWT)、JQWEB
服务层框架:Spring、EJB
持久层框架:Hibernate、MyBatis、JPA、TopLink
数据库:Oracle、MySql、MSSQL、Redis
项目构建:maven、ant
持续集成:Jenkins
版本控制:SVN、CVS、VSS、GIT
私服:Nexus
消息组件:IBM MQ、RabbitMQ、ActiveMQ、RocketMq
日志框架:Commons Logging、log4j 、slf4j、IOC
缓存框架:memcache、redis、ehcache、jboss cache
RPC框架:Hessian、Dubbo
规则引擎:Drools
工作流:Activiti
批处理:Spring Batch
通用查询框架:Query DSL
JAVA安全框架:shiro、Spring Security
代码静态检查工具:FindBugs、PMD
Linux操作系统:CentOS、Ubuntu、SUSE Linux、
常用工具:PLSQL Developer(Oracle)、Navicat(MySql)、FileZilla(FTP)、Xshell(SSH)、putty(SSH)、SecureCRT(SSH)、jd-gui(反编译)