1.redis特点
- 数据存储:存放在内存,还支持持久化.-存取速度快,并发能力强,数据安全性高
- 支持value类型
- 支持多个语言客户端
- 还支持集群(支持高并发,海量数据)
2.使用场景
- 缓存
- 计数器
- 实时防攻击系统
- 需要设置有效期的应用
- 自动去重应用
- 栈和队列
- 消息订阅系统
3.redis的安装
- 网址
- 官网:https://redis.io/
- 中文网:http://redis.cn/
- redis的windows版的github下载地址:https://github.com/MicrosoftArchive/redis/releases
- 下载
- 去github下载windows版,我这里下载的3.2.100版本
- 安装启动服务端
- 由于redis是一款绿色的,不需要安装,直接启动服务端就好
- 启动命令(在压缩包解压后的目录那里打开黑窗口):redis-server.exe redis.windows.conf
- 客户端安装启动
- redis自带的客户端也是绿色的,无需安装,直接启动即可
- 启动命令1(在压缩包解压后的目录那里打开黑窗口):redis-cli.exe -h 服务端ip -p 服务端redis的端口号
- 启动命令2:直接双击redis-cli.exe文件
4.使用redis-cli.exe操作redis
见操作文档
5.java操作redis
- 选择java客户端jedis
- 创建普通java项目,导入2个jar
- jedis-2.5.2.jar
- commons-pool2-2.2.jar
- jedis连接方式操作redis
package cn.wangningbo.redis.client.jedis;
import org.junit.Test;
import redis.clients.jedis.Jedis;
public class JedisHello {
@Test
public void test() throws Exception {
//连接redis
Jedis jedis = new Jedis("127.0.0.1", 6379);
//设置key-value
jedis.set("test", "easy");
//获取key为test的value
System.out.println(jedis.get("test"));
//关闭连接
jedis.close();
}
}
- 连接池方式操作redis
package cn.wangningbo.redis.client.jedis;
import org.junit.Test;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
public class JedisPoolTest {
@Test
public void test() throws Exception {
//创建配置对象
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
//空闲时的连接数
jedisPoolConfig.setMaxIdle(3);
//最大连接数
jedisPoolConfig.setMaxTotal(10);
//创建连接超时 1秒
jedisPoolConfig.setMaxWaitMillis(1 * 1000);
//获取连接的时候测试连接是否畅通
jedisPoolConfig.setTestOnBorrow(true);
//创建jedis连接池 参数:指定配置,ip,端口
JedisPool jedisPool = new JedisPool(jedisPoolConfig, "127.0.0.1", 6379);
//获取jedis
Jedis jedis = jedisPool.getResource();
//操作数据
jedis.set("name", "二狗");
System.out.println(jedis.get("name"));
//释放连接
jedis.close();
//摧毁连接池
jedisPool.destroy();
}
}
- 封装工具类
package cn.wangningbo.redis.client.jedis;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
public enum JedisUtil {
INSTANCE;
private static JedisPool jedisPool;
static {
//创建配置对象
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
//空闲时的连接数
jedisPoolConfig.setMaxIdle(3);
//最大连接数
jedisPoolConfig.setMaxTotal(10);
//创建连接超时 1秒
jedisPoolConfig.setMaxWaitMillis(1 * 1000);
//获取连接的时候测试连接是否畅通
jedisPoolConfig.setTestOnBorrow(true);
//创建jedis连接池 参数:指定配置,ip,端口
jedisPool = new JedisPool(jedisPoolConfig, "127.0.0.1", 6379);
}
public Jedis getResource() {
return jedisPool.getResource();
}
}
- 工具类操作redis
package cn.wangningbo.redis.client.jedis;
import org.junit.Test;
import redis.clients.jedis.Jedis;
import java.util.List;
public class JedisOprTest {
Jedis jedis = JedisUtil.INSTANCE.getResource();
@Test
public void testKey() throws Exception {
//清空数据
jedis.flushAll();
System.out.println("判断key为name的key是否存在:" + jedis.exists("name"));
System.out.println("判断key为age的key是否存在:" + jedis.exists("age"));
System.out.println("给key为name的key设置value:" + jedis.set("name", "二狗"));
System.out.println("给key为age的key设置value:" + jedis.set("age", "18"));
System.out.println("再次判断key为name的key是否存在:" + jedis.exists("name"));
System.out.println("再次判断key为age的key是否存在:" + jedis.exists("age"));
System.out.println("删除前key为name的value值:" + jedis.get("name"));
System.out.println("删除key为name的value值:" + jedis.del("name"));
System.out.println("删除后key为name的value值:" + jedis.get("name"));
System.out.println("获取key为age的value值:" + jedis.get("age"));
//获取所有的key
jedis.keys("*").forEach(key -> {
System.out.println(key + "-->" + jedis.get(key));
});
//释放连接
jedis.close();
}
@Test
public void testStringBase() throws Exception {
//清空数据
jedis.flushAll();
//add
jedis.set("name", "舔狗");
//get
System.out.println(jedis.get("name"));
//update
jedis.set("name", "二狗");
//get
System.out.println(jedis.get("name"));
//del
jedis.del("name");
//get
System.out.println(jedis.get("name"));
//jedis.append(key,appStr)追加 字符串拼接
jedis.set("age", "12");
System.out.println(jedis.get("age"));
jedis.append("age", "45");
//jedis.get() 获取
System.out.println(jedis.get("age"));
}
@Test
public void testStringBatch() throws Exception {
//清空数据
jedis.flushAll();
//jedis.mset(keyvalue,keyvalue) dd update
jedis.mset("name", "二狗", "age", "19", "sex", "男");
jedis.keys("*").forEach(key -> {
System.out.println(key + "-->" + jedis.get(key));
});
//jedis.mget(key1,key2,key3....) //获取多个key的值返回list
List<String> mget = jedis.mget("name", "sex");
System.out.println(mget);
//jedis.del(key1,key2...) del
jedis.del("name", "sex");
jedis.keys("*").forEach(key -> {
System.out.println(key + "-->" + jedis.get(key));
});
}
@Test
public void testStringAdvanceOpr() throws Exception {
//清空数据
jedis.flushAll();
//设置一个key-value
jedis.set("age", "19");
System.out.println(jedis.get("age"));
//jedis.incr()
jedis.incr("age");
System.out.println(jedis.get("age"));
//jedis.incrBy()
jedis.incrBy("age", 5);
System.out.println(jedis.get("age"));
//jedis.setex(key,time,value) //设置值得时候同时设置过期时间
jedis.setex("name", 10, "奈斯");
System.out.println(jedis.get("name"));
Thread.sleep(12 * 1000);
System.out.println(jedis.get("name"));
System.out.println(jedis.exists("name"));
}
//事务四个特性
// 1 原子性:一个事务中,多个操作是不可分割的,要么都成功,要么都失败.
// 2 一致性:一个事务中,多个操作如果有一个失败了,其他要进行回滚保持数据一致性.
// 3 隔离性:事务是隔离的,多个事务不会相互影响
// 4 连续性(持续性):事务一旦开始,不能中止.
@Test
public void test() throws Exception {
//手动回滚
}
}
6.面试题
- 项目中那些地方用到Redis?
- 缓存、计数器、实时防攻击系统、设置有效期的应用、自动去重应用、栈和队列、消息订阅系统!
- 使用什么来操作Redis?
- 可以操作redis的客户端有很多种。比如:java、php、js、C/C++、C#、python等。
- 我选择了java,java可操作redis的也有很多种。比如:Redisson、jedis等等很多种!
- 我使用过jedis操作redis!
- 你使用过memcached?
- 没有。但是我使用过redis。
- redis与mencached的相同点:都是key-valueNosql,数据存储在内容中,读写效率高,都支持存储过期。
- redis与mencached的不相同点:
- 1)redis支持持久化.可以进行aof及rdb数据持久化到磁盘,从而进行数据备份或数据恢复等操作,较好的防止数据丢失的手段。。
- 2)redis支持存储类型更多
- 3)redis支持pub/sub消息订阅机制,可以用来进行消息订阅与通知。
- 4)支持简单的事务需求,但业界使用场景很少,并不成熟
- 你为什么要使用reids?
- 1)数据存储:存放在内存,还支持持久化.-存取速度快,并发能力强,数据安全性高
- 2)支持value类型
- 3)支持多个语言客户端
- 4)还支持集群(支持高并发,海量数据)
- redis怎么实现栈和队列?
- 队列和堆栈都可以用Redis的list数据类型实现,因为list支持以下操作
- lpush/rpush,从左侧/右侧追加元素
- lpop/rpop,从左侧/右侧弹出元素
- llen,获取队列的长度
- lindex,获取某个位置的元素
- 事务四大特性?
- 1 原子性:一个事务中,多个操作是不可分割的,要么都成功,要么都失败.
- 2 一致性:一个事务中,多个操作如果有一个失败了,其他要进行回滚保持数据一致性.
- 3 隔离性:事务是隔离的,多个事务不会相互影响
- 4 连续性(持续性):事务一旦开始,不能中止.
- 为什么要使用连接池?
- 1)资源重用。由于数据库连接得到重用,避免了频繁创建、释放连接引起的大量性能开销。在减少系统消耗的基础上,另一方面也增进了系统运行环境的平稳性(减少内存碎片以及数据库临时进程/线程的数量)。
- 2)更快的系统响应速度。数据库连接池在初始化过程中,往往已经创建了若干数据库连接置于池中备用。此时连接的初始化工作均已完成。对于业务请求处理而言,直接利用现有可用连接,避免了数据库连接初始化和释放过程的时间开销,从而缩减了系统整体响应时间。
- 3)新的资源分配手段。对于多应用共享同一数据库的系统而言,可在应用层通过数据库连接的配置,实现数据库连接池技术,几年钱也许还是个新鲜话题,对于目前的业务系统而言,如果设计中还没有考虑到连接池的应用,那么…….快在设计文档中加上这部分的内容吧。某一应用最大可用数据库连接数的限制,避免某一应用独占所有数据库资源。
- 4)统一的连接管理,避免数据库连接泄漏。在较为完备的数据库连接池实现中,可根据预先的连接占用超时设定,强制收回被占用连接。从而避免了常规数据库连接操作中可能出现的资源泄漏。一个最小化的数据库连接池实现:
- 说一下redis是怎么存储数据的?
- redis为了考虑效率,保存数据在内容中.并且考虑数据安全性,还做数据持久化,如果满足保存策略,就会把内存的数据保存到数据rdb文件,还来不及保存那部分数据存放到aof更新日志中。在加载时,把两个数据做一个并集。
- redis数据满了时会怎么办?
- 会执行淘汰策略
- 淘汰一些数据,达到redis数据都是有效的。选择合适的淘汰策略进行淘汰。
- 1)volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
- 2)volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
- 3)volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
- 4)allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
- 5)allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
- 6)no-enviction(驱逐):禁止驱逐数据
- redis 确定驱逐某个键值对后,会删除这个数据并,并将这个数据变更消息发布到本地(AOF 持久化)和从机(主从连接)。