1.什么是Redis
- redis是一个NOSql数据库(非关系型数据库),区别于MySql,SQLServer,Oracle等有行列的关系型数据库。
- 可以使用在高并发,但存储量不大的情况中。
- 数据存储在内存中,也可以存储在磁盘中。
2.Redis使用场景
- 数据缓存
对于需要经常查询的数据,可以使用Redis,放到内存中,加快读取速度。 - 计数器
比如网站访问量,评论数,注册用户数,Redis有incr 和 incrby - 实时防攻击系统
对于短时间发送多次比如登录操作的暴力破解请求,可以使用Redis进行记录,判断 - 设定有效期的应用
比如有些活动,并发量大,而且有有效期,可以Redis设置过期时间,到期自动移除活动
vip,红包,session,到期失效。购物券,自动解锁。 - 排行榜
zset有排序功能,适用于各种排行榜 - 数据去重
使用set,自动去重,如果想要获取一段并发量高的数据并去重,则可使用Redis - 订阅发布
构建实时消息系统 - 队列
使用list构建队列,sorted set构建有顺序的队列。在秒杀这种高并发环境下,可以把名额放到内存队列中。
3.Redis常用命令
1.key
- keys *
- del key
- expire key second
- ttl key
- 数据库16个默认0,切换使用select index
- flushall删除所有数据库数据
- flushdb删除当前数据库数据
2.string
- set key value
- get key value
- mset key value key value
- mget key key
3.list
- lpush key value value
- rpush key value value
- lpop key
- rpop key
- lrange key 0 -1
- lindex key 0
- lrem key count value
- ltrim key start stop
4.set
- sadd key member member
- srem key member
- smembers key
5.zset
- zadd key member member
- zcard key
- zcount key
- zrange key
- zrevrange key
6.hash
- hset user name zhangsan
- hget user name
- hmset user name zhangsan age 11
- hmget user name age
- hgetall
- hkeys
- hvals
- hmset user:1 name zs age 11 sex 1
- set user:1 {“id”:1,“name”:“zs”,“age”:“11”,“sex”:1}
7.事务
- multi
- set name zs
- get name
- exec
- discard
- 事务队列,错误不回滚
8.订阅发布
- SUBSCRIBE channel
- PUBLISH channel message
- 先订阅后发布,区分大小写
9.设置密码
- 配置文件中requirepass,记得启动服务端时指定使用配置文件
- auth password密码认证
4.Java使用Redis-Jedis
- 导入jar

- 使用
package com.qinjie.redis;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
/**
* Jedis连接池
* 用来优化数据库连接,减少开启关闭时间
*/
public class JedisPoolUtil {
private static JedisPool pool = null;
static{
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
//最大空闲
jedisPoolConfig.setMaxIdle(2);
//最大总连接
jedisPoolConfig.setMaxTotal(200);
//初始化连接池
pool = new JedisPool(jedisPoolConfig, "127.0.0.1", 6379, 1000, "nice_qinjie");
}
//获取Jedis对象
public static Jedis getJedis(){
return pool.getResource();
}
}
package com.qinjie.redis;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPubSub;
import redis.clients.jedis.Transaction;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class TestJedis {
private Jedis jedis;
@Before
public void before(){
//通过连接池获取Jedis对象
jedis = JedisPoolUtil.getJedis();
//清空当前数据库数据
jedis.flushDB();
}
@After
public void after(){
jedis.close();
}
/**
* 1.测试key
* @throws Exception
*/
@Test
public void testKey() throws Exception{
//1.keys:所有键==============
/* keys();
//2.del key:删除键=====================
jedis.del("name");
keys();
//3.expire:过期======================
jedis.expire("age",10);
String age = jedis.get("age");
System.out.println("age:"+age);
//4.flushdb:删除当前库数据=========================
jedis.flushDB();
keys();*/
//5.select:选择数据库,总共16个,默认使用下标0的======================
// jedis.select(1);
// jedis.set("name","任盈盈");
// System.out.println("selectRet1:"+jedis.get("name"));
// jedis.select(0);
// jedis.set("name","田伯光");
// System.out.println("selectRet0:"+jedis.get("name"));
// //6.flushall:删除所有数据库数据=====================
// jedis.flushAll();
// System.out.println("selectRet0:"+jedis.get("name"));
// jedis.select(1);
// System.out.println("selectRet1:"+jedis.get("name"));
//7.incr:加一,必须数字
jedis.set("age","1");
jedis.incr("age");
System.out.println(jedis.get("age"));
//8.decr:减一
jedis.decr("age");
System.out.println(jedis.get("age"));
//9.incrBy:按照增长值增长
jedis.incrBy("age",5);
System.out.println(jedis.get("age"));
//10.decrBy:按照减少值减少
jedis.decrBy("age",1);
System.out.println(jedis.get("age"));
}
private void keys(){
Set<String> keys = jedis.keys("*");
keys.forEach(e->{
System.out.println("keyEach:"+e);
});
}
@Test
public void testKeys() throws Exception{
keys();
}
/**
* 2.测试字符串
* @throws Exception
*/
@Test
public void testString() throws Exception{
// 字符串设置
jedis.set("name","令狐冲");
System.out.println(jedis.get("name"));
//字符串设置对象字符串
jedis.set("user:1","{\n" +
" \"name\": \"小小\",\n" +
" \"age\":10\n" +
"}");
System.out.println(jedis.get("user:1"));
//设置多个值
jedis.mset("name","令狐冲","age","10");
List<String> user = jedis.mget("name","age");
user.forEach(e->System.out.println(e));
}
/**
* 3.测试list
* @throws Exception
*/
@Test
public void testList() throws Exception{
//1.lpush:从头插入key的很多值
// jedis.lpush("name","小白","小黑","小新");
// printListRestVal();
// //2.lpop:从头移除key中的第一个值(后进先出,栈)
// String name = jedis.lpop("name");
// System.out.println("lpop的值:"+name);
// printListRestVal();
//3.rpop:从尾移除key的第一个值
// String name = jedis.rpop("name");
// System.out.println("rpop的值:"+name);
// printListRestVal();
// //4.rpush:从尾插入key的很多值,先进先出,队列
// jedis.rpush("name","小白","小黑","小新");
// printListRestVal();
// String name = jedis.lpop("name");
// System.out.println("lpop:"+name);
//5.lindex:根据下标取值
// jedis.rpush("name","小白","小黑","小新");
// printListRestVal();
// String name = jedis.lindex("name", 0);
// System.out.println("lindex的值:"+name);
//6.lrem移除值:count>0,移除从头开始,value值的count个值
jedis.rpush("name","小白","小黑","小新","小白","小白");
printListRestVal();
jedis.lrem("name",2,"小白");
printListRestVal();
}
/**
* 4.测试Set集合:
* @throws Exception
*/
@Test
public void testSet() throws Exception{
//saddd:增加无序不重复数据
jedis.sadd("name","杨过","郭靖","小龙女","杨过");
//smembers:获取key的所有数据
jedis.smembers("name").forEach(e->System.out.println(e));
//srem:移除set中的值
jedis.srem("name","杨过");
jedis.smembers("name").forEach(e->System.out.println(e));
}
/**
* 5.有序set
* @throws Exception
*/
@Test
public void testZSet() throws Exception{
Map map = new HashMap();
map.put("柯南",5d);
map.put("小白",2d);
map.put("小黑",6d);
//zadd:添加有序不重复数据,添加多个,第二个参数是map,且map中的value为double类型;否则添加一个值
jedis.zadd("name",map);
//zcard:总数
Long zcard = jedis.zcard("name");
System.out.println("zcard:"+zcard);
//zcount:一定范围内的计数
Long zcount = jedis.zcount("name", 2, 6);
System.out.println("zcount:"+zcount);
//zrange:获取集合内容,按照score正序排序
jedis.zrange("name",0,-1).forEach(e->System.out.println(e));
//zrevrange:获取元素内容,按照score倒序排序
jedis.zrevrange("name",0,-1).forEach(e->System.out.println(e));
}
/**
* 6.事务
* 一组操作,需要同时成功或者失败。
* 比如Service的方法加上了@Transactional,
* 那么该方法使用的mapper操作数据库增删改,就会出现要么一起成功/一起失败的情况。
* 事务特性:
* 原子性
* 隔离性
* 一致性
* 持久性
* redis.clients.jedis.exceptions.JedisDataException:
* Cannot use Jedis when in Multi. Please use Transaction or reset jedis state.
* @throws Exception
*/
@Test
public void testTransaction() throws Exception{
//开启事务,获取Transaction
Transaction transaction = jedis.multi();
transaction.set("name","小白");
// //失败,事务依然提交了
// transaction.incr("name");
// //事务提交
// List<Object> list = transaction.exec();
// boolean listEmpty = list.isEmpty();// 判断事务是否成功
// if(listEmpty){
// System.out.println("事务执行失败");
// }
// list.forEach(e->System.out.println(e));
// System.out.println(jedis.get("name"));
//不提交
String discard = transaction.discard();
System.out.println(discard);
System.out.println(jedis.get("name"));
}
/**
* 7.订阅测试
* @throws Exception
*/
// @Test
// public void testSubscribe() throws Exception{
// jedis.subscribe(new JedisPubSub( ) {
// @Override
// public void subscribe(String... channels) {
// super.subscribe(channels);
// }
// });
// }
//打印list剩余的值
private void printListRestVal(){
System.out.println("in:printListRestVal===========================");
List<String> name1 = jedis.lrange("name", 0, -1);
name1.forEach(e->System.out.println("list中还有的值:"+e));
}
}
5.相关面试题
- 项目中那些地方用到Redis?
- vip到期,session过期,红包到期等有过期时间的情况
- 对于需要经常查询,并发量大且消耗数据库资源的操作,可以使用Redis,将数据放在内存中,加快读写速度
- 对于比如商品销量排行,客户订单排行等情况,可以使用zset的排序功能实现
- 获取不可重复的数据,可以使用set
- 订阅/发布,构建实时消息系统,比如在客户和商家的聊天功能中,使用Redis
- 队列,使用list构建队列,sorted list构建有序队列。比如在秒杀情况中,使用Redis的队列,处理高并发的情况
- 网站访问量,注册用户数,商品评论数等需要计数的情况,可以使用Redis
- 使用什么来操作Redis?
java中使用Jedis来操作Redis,一般使用连接池来获取Jedis对象,再通过Jedis对象进行数据操作。 - 你使用过memcached?
没有使用过,只是听说过。它和Redis都是非关系型数据库,不是有行列关系的关系型数据库。都是使用key-vlaue进行数据的存储。只是Redis除了将数据存储到内存里,也可持久化到磁盘中。 - 你为什么要使用redis?
- redis可以应对高并发的情况,因为数据存储在内存中,不像(关系型数据库)磁盘中的数据可以很快获取到。
- 可以实现实时消息系统。
- 可以做网站数量统计。
- 可以解决秒杀等高并发情况下的数据问题
- redis怎么实现栈和队列?
- 使用list结构即可实现。
- list的lpush和lpop即可实现后进先出的栈功能
- list的rpush和lpop即可实现先进先出的队列功能
- 事务四大特性?
- 原子性
事务中的操作是最小单位,不可再进行分割。要么一起成功,要么一起失败。 - 隔离性
各个事务间的操作是相互分离的,互不影响的。 - 一致性
结果是一致的。比如转账,不能出现,转账的人减少了,收款的人没收到。 - 持久性
事务是需要提交,持久化到数据库中去的
- 为什么要使用连接池
- 说一下redis是怎么存储数据的?
- redis中的数据是存储到内存中的,可以加快访问速度。为了防止数据在redis死掉后丢失(比如断电),可以将其持久化存储。
- redis提供了两种持久化的策略AOF和RDB
- RDB:默认使用,可以在配置文件中配置在一定时间内有多少次改动,就保存一份数据
- AOF:需要开启,在有数据写操作时,进行记录,在启动时,执行写操作。可以设置有改动则记录,某段时间记录,交给操作系统记录。
- redis数据满了时会怎么办?
- 会启用淘汰策略
- 一种根据有过期时间的键的数据进行淘汰
- 一种根据所有键的数据进行淘汰
- 过期时间的,有最近使用最少,即将过期,随机淘汰
- 所有键的,有最近使用最少,随机