redis概述
Redis介绍
- Redis概率:是用C语言开发的一个开源的高性能键值对(key-value)数据库。
- 特征:
- 数据间没有必然的关联关系
- 内部采用单线程机制进行工作(所有操作都是原子性的)
- 高性能
- 支持持久化,可以进行数据灾难恢复
- 多线程类型支持
类型 | 类型说明 |
字符串类型 | string |
列表类型 | list |
散列类型 | hash |
集合类型 | set |
有序集合类型 | zset/sorted_set |
Redis特点
- Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
- Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储。
- Redis支持数据的备份,即master-slave模式的数据备份。
Redis 优势
- 性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。
- 丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。
- 原子 – Redis的所有操作都是原子性的,同时Redis还支持对几个操作全并后的原子性执行。
- 丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性。
Redis下载安装
- 官网:https://redis.io
- 中文网:http://www.redis.net.cn/
- 解压直接可以使用:
- redis.windows.conf:配置文件
- redis-cli.exe:redis的客户端
- redis-server.exe:redis服务器端
https://www.runoob.com/redis/redis-commands.html redis 的 中文 命令文档, 大家 可以 多翻翻 这个文档, 里面 都是中文的, 非常的友好 .
应用场景
- String:主页高频访问信息显示控制,例如微博大V的粉丝数
- hash:存放结构化数据,比如用户信息
- list:各种列表,比如twitter的关注列表、粉丝列表等,最新消息排行、每篇文章的评论等也可以用Redis的list结构来实现。
- set:可以存储一些集合性的数据,比如在微博应用中,可以将一个用户所有的关注人存在一个集合中,将其所有粉丝存在一个集合
数据类型及对应操作
1.数据类型-string
- 存储的数据结构:单个数据,最简单的数据存储类型,也是最常用的数据存储类型
- 存储数据的格式,一个存储空间保存一个数据
- 存储内容:通用使用字符串,如果字符串以整数的形式展示,可以作为数字操作使用
基本操作
1-SET key value
保存一个值,key表示字符串的名称,value表示值
保存复杂的字符串value是使用引号: set msg "hello word"
2-get key
获取一个值
3-MSET key value [key value ...]
保存多组值,[]表示可选值
4-MGET key [key ...]
获取多组值
5-APPEND key value
在原有的字符串上追加一段内容
6-strlen key
获取字符串的长度
7-del key
删除key,对应的value值也就被一起删除了
8-setnx key value
保存一个值,如果有一个key,则不存,没有就存
扩展操作
1- 设置数值数据增长指定范围的值
incr key //从1开始
incrby key increment //指定一个数,增长多少
incrbyfloat key increment//指定一个数增长多少,可以增长一个小数
2- 设置数值数据减少指定范围的值
decr key
decrby key increment
3- 设置数据具有指定的生命周期
setex key seconds value //指定多少秒这个数据失效(验证码)
psetex key millisenconds value
2.数据类型-hash
- 新的存储需求:对一系列的数据进行编组,方便管理,典型应用存储对象信息
- 需要的存储结构:一个存储空间保存多个键值对数据
- hash类型:底层使用哈希表结构实现数据存储
- hash存储结构的优化
- 如果field数量较少,存储结构优化为类数组结构
- 如果field数量较多,存储结构使用hashMap结构
基本操作
1 存值/修改
语法:hset hash名称 field value; //存一组值
hset user:123 name tom
语法:hmset hash名称 field value field value field value ...; //存多组值
hmset user:123 age 20 address wh
2 获取
hget hash名称 field //获取一组值
hmget hash名称 field field field...//获取多组值
hgetAll hash名称 //获取所有的field以及对应的value值
hlen hash名称 //获取hash中存储的数据的长度
hkeys hash名称 //获取hash中存储的所有的key
hvals hash名称 //获取hash中存储的所有的value
3 删除
hdel hash名称 field //删除field对应的value
扩展操作
1- 设置指定字段的数字数据增加指定范围的值
hincrby key field increment
hincrbyfloat key field increment
2- 每个hash可以存储2的32次方-1个键值对
3- hgetall操作可以获取全部属性,如果内部filed过多,遍历整体数据效率会很低,有可能成为数据访问瓶颈
3.数据类型-list
- 数据存储需求:存储多个数据,并对数据进入存储空间的顺序进行区分
- 需要的存储结构:一个存储空间保存多个数据,并通过数据可以体现进入顺序
- list类型:保存多个数据,底层使用***双向链表***存储结构实现
基础操作
1 lpush list名称 value [value ...]
向集合的头部保存一个值 key表示集合名称
rpush list名称 value [value ...]
向集合的尾部保存一个值 key表示集合名称
2 lrange list名称 start stop lrange list01 0 -1
获取集合中所有的值 正数表示从左往右数,0开始,负数表示从右往左数,-1开始,-1表示最后一个值
3 lpop list名称
弹出集合头部元素//取出来,集合里面就没有数据了
rpop list名称
弹出集合头部元素
4 lrem list名称 count value
移除指定count个数的value值
5 lindex list名称 index
根据索引获取值
扩展操作
1- 移除指定数据
lrem key count value
2- 规定时间内获取并移除数据
blpop key1 [key2] timeout
brpop key1 [key2] timeout
brpoplpush source destination timeout
4.数据类型-set
- 新的存储需要:存储大量的数据,在查询方面提供更高的效率
- 需要的数据结构:能够保持大量的数据,高效的内部存储机制,便于查询
- set类型:与hash存储结构完全相同,仅存储键,不存储值(nil),并且值是不允许重复的
基本操作
1 sadd set名称 value [value ...] //存值
2 smembers set名称 //取值
3 sismember set名称 value //是否有该值
4 srem set名称 value //删除该值
5 scard set名称 //获取集合的长度
6 srandmember set名称 [count] //随机取cout个值
补充:给redis设置密码:搜索/requirepass关键字,将注释的密码放开
5.其他操作
5.1 key常用指令
1 删除指定的key : del username; 删除username
2 查看所有的key : keys *
3 查看key对应value的类型: type key
4 获取key是否存在:exists key
5 排序:sort
6.改名:rename key newkey
5.1 DB常用指令
1 切换数据库:select 数据库索引 redis中默认有16个数据库,索引从0-15
2 清空当前数据库中的所有key:flushdb
3 清空所有数据库中的所有key:flushall
Jedis的使用
- Jedis用于java语言连接redis服务,并提供对呀操作API
- 使用步骤
- jar包导入
- 获取连接对象
Jedis jedis = new Jedis("localhost",6379);
- 执行操作
jedis.set("username","zhangsan");
- 关闭连接
/*
目的:将jedis连接池封装到工具类中
这个工具类需要做哪些事?
1 读取配置文件
2 创建jedis连接池对象 连接池对象和配置文件只需要加载一次,可以放到static静态代码块中
3 对外提供一个方法,从连接池中获取连接返回
4 对外提供一个方法,释放资源
*/
public class RedisUtils {
private static JedisPool jedisPool;
static {
//1.读取配置文件
InputStream is = RedisUtils.class.getClassLoader().getResourceAsStream("redis.properties");
Properties prop = new Properties();
try {
prop.load(is);
//获取配置文件的值
String host = prop.getProperty("redis.host");
int port = Integer.parseInt(prop.getProperty("redis.port"));
int maxActive = Integer.parseInt(prop.getProperty("redis.maxActive"));
//2.创建连接池配置对象
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(maxActive);
//3.创建jedis连接池对象
jedisPool = new JedisPool(config,host,port);
} catch (IOException e) {
e.printStackTrace();
}
}
//4.对外提供一个方法,从连接池中获取连接返回
public static Jedis getJedis(){
return jedisPool.getResource();
}
//5.释放资源
public static void close(Jedis jedis){
if (jedis!=null){
jedis.close();
}
}
}
redis持久化
1.什么是持久化
利用永久性存储介质将数据进行保存,在特定的时间将保存的数据进行恢复的工作机制称为持久化用于防止数据的意外丢失,确保数据安全性
2.保存方式
- RDB:将当前数据进行状态进行保存,快照形式,存储数据结果,存储格式简单,关注点在数据
- AOF:将数据的操作过程进行保存,日志形式,存储操作过程,存储格式复杂,关注点在数据的操作过程
RDB: 是通过 快照的形式来 做 持久化存储
AOF: 是通过 记录 操作 的过程来 来做 持久化存储
3.RDB和AOF的区别
持久化方式 | RDB | AOF |
占用存储空间 | 小(数据级:压缩) | 大(指令级:重写) |
存储速度 | 慢 | 快 |
恢复速度 | 快 | 慢 |
数据安全性 | 会丢失数据 | 一句决策决定 |
资源消耗 | 搞/重量级 | 低/轻量级 |
启动优先级 | 低 | 高 |
3.1RDB和AOF两种持久化方案保持数据的区别?
RDB文件中保持的是某一时刻redis中所有的数据。AOF保存的是每次的操作,AOF方式要在配置文件中手动开启,appendonly yes。
3.2RDB和AOF优缺点对比?
具体演示效果
1.redis数据类型string常见操作介绍
redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)
字符串类型是Redis中最为基础的数据存储类型,它在Redis中是二进制安全的,这便意味着该类型可以接受任何格式的数据,如JPEG图像数据或Json对象描述信息等。在Redis中字符串类型的Value最多可以容纳的数据长度是512M。
常见操作
- set key value
设定该Key持有指定的字符串Value,如果该Key已经存在,则覆盖其原有值。
返回值:总是返回"OK"
- get key
获取指定Key的Value。如果与该Key关联的Value不是string类型,Redis将返回错误信息,因为GET命令只能用于获取string Value。
返回值:与该Key相关的Value,如果该Key不存在,则返回nil。
- mset key value [key value …]
该命令原子性的完成参数中所有key/value的设置操作,其具体行为可以看成是多次迭代执行SET命令。
返回值:该命令不会失败,始终返回OK。
- mget key [key …]
返回所有指定Keys的Values,如果其中某个Key不存在,或者其值不为string类型,该Key的Value将返回nil。
返回值:返回一组指定Keys的Values的列表。
- append key value
如果该Key已经存在,APPEND命令将参数Value的数据追加到已存在Value的末尾。如果该Key不存在,APPEND命令将会创建一个新的Key/Value。
返回值:追加后Value的长度。
- setrange key offset value
替换指定Key的部分字符串值。从offset开始,替换的长度为该命令第三个参数value的字符串长度,其中如果offset的值大于该Key的原有值Value的字符串长度,Redis将会在Value的后面补齐(offset - strlen(value))数量的0x00,之后再追加新值。如果该键不存在,该命令会将其原值的长度假设为0,并在其后添补offset个0x00后再追加新值。鉴于字符串Value的最大长度为512M,因此offset的最大值为536870911。最后需要注意的是,如果该命令在执行时致使指定Key的原有值长度增加,这将会导致Redis重新分配足够的内存以容纳替换后的全部字符串,因此就会带来一定的性能折损。
返回值:修改后的字符串Value长度。
注意:从左往右数,从0开始依次递增,不能从右往左数;多个单词需要使用引号引起来。
- getrange key start end
截取字符串。该命令在截取子字符串时,将以闭区间的方式同时包含start(0表示第一个字符)和end所在的字符,如果end值超过Value的字符长度,该命令将只是截取从start开始之后所有的字符数据。
返回值:子字符串;
注意:从左往右数,从0开始依次递增;从右往左数,从-1开始依次递减,-1表示最后一位,例如:
2.redis数据类型hash常见操作介绍
Redis中的Hashes类型可以看成具有String Key和String Value的map容器。所以该类型非常适合于存储值对象的信息。如用户信息:Username、Password和Age等。每一个Hash可以存储4294967295个键值对。
常见操作
- hset key field value
为指定的Key设定Field/Value对,如果Key不存在,该命令将创建新Key以参数中的Field/Value对,如果参数中的Field在该Key中已经存在,则用新值覆盖其原有值。
返回值:1表示新的Field被设置了新值,0表示Field已经存在,用新值覆盖原有值。
- hget key field
返回指定Key中指定Field的关联值。
返回值:返回参数中Field的关联值,如果参数中的Key或Field不存,返回nil。
- hlen key
获取该Key所包含的Field的数量。 返回Key包含的Field数量,如果Key不存在,返回0。
- hmset key field value [field value …]
逐对依次设置参数中给出的Field/Value对。如果其中某个Field已经存在,则用新值覆盖原有值。如果Key不存在,则创建新Key,同时设定参数中的Field/Value。
- hmget key field [field …]
获取和参数中指定Fields关联的一组Values。如果请求的Field不存在,其值返回nil。如果Key不存在,该命令将其视为空Hash,因此返回一组nil。
返回值:返回和请求Fields关联的一组Values,其返回顺序等同于Fields的请求顺序。
- hgetall key
获取该键包含的所有Field/Value。其返回格式为一个Field、一个Value,并以此类推。
返回值:Field/Valu
e的列表。
- hkeys key
返回指定Key的所有Fields名。
返回值:Field的列表。
- hvals key
返回指定Key的所有Values名。
返回值:Value的列表。
- hdel key field [field …]
从指定Key的Hashes Value中删除参数中指定的多个字段,如果不存在的字段将被忽略。
返回值:如果Key不存在,则将其视为空Hashes,并返回0,否则返回实际删除的Field数量。
3.redis数据类型list常见操作介绍
在Redis中,List类型是按照插入顺序排序的字符串链表。和数据结构中的普通链表一样,我们可以在其头部(left)和尾部(right)添加新的元素。在插入时,如果该键并不存在,Redis将为该键创建一个新的链表。与此相反,如果链表中所有的元素均被移除,那么该键也将会被从数据库中删除。List中可以包含的最大元素数量是4294967295。
从元素插入和删除的效率视角来看,如果我们是在链表的两头插入或删除元素,这将会是非常高效的操作,即使链表中已经存储了百万条记录,该操作也可以在常量时间内完成。然而需要说明的是,如果元素插入或删除操作是作用于链表中间,那将会是非常低效的。
常见操作
- lpush key value [value …]
在指定Key所关联的List Value的头部插入参数中给出的所有Values。如果该Key不存在,该命令将在插入之前创建一个与该Key关联的空链表,之后再将数据从链表的头部插入。如果该键的Value不是链表类型,该命令将返回相关的错误信息。
返回值:插入后链表中元素的数量。
- lrange key start stop
该命令的参数start和end都是0-based。即0表示链表头部(leftmost)的第一个元素。其中start的值也可以为负值,-1将表示链表中的最后一个元素,即尾部元素,-2表示倒数第二个并以此类推。该命令在获取元素时,start和end位置上的元素也会被取出。如果start的值大于链表中元素的数量,空链表将会被返回。如果end的值大于元素的数量,该命令则获取从start(包括start)开始,链表中剩余的所有元素。
返回值:返回指定范围内元素的列表。
- lpop key
返回并弹出/移除指定Key关联的链表中的第一个元素,即头部元素。如果该Key不存,返回nil。
返回值:链表头部的元素。
- llen key
返回指定Key关联的链表中元素的数量,如果该Key不存在,则返回0。如果与该Key关联的Value的类型不是链表,则返回相关的错误信息。
返回值:链表中元素的数量。
- lset key index value
设定链表中指定位置的值为新值,其中0表示第一个元素,即头部元素,-1表示尾部元素。如果索引值Index超出了链表中元素的数量范围,该命令将返回相关的错误信息。
(对指定脚标的值进行设置)
- lindex key index
该命令将返回链表中指定位置(index)的元素,index是0-based,表示头部元素,如果index为-1,表示尾部元素。如果与该Key关联的不是链表,该命令将返回相关的错误信息。
返回值:返回请求的元素,如果index超出范围,则返回nil。
(读出指定脚标的值)
- rpush key value [value …]
在指定Key所关联的List Value的尾部插入参数中给出的所有Values。如果该Key不存在,该命令将在插入之前创建一个与该Key关联的空链表,之后再将数据从链表的尾部插入。如果该键的Value不是链表类型,该命令将返回相关的错误信息。
返回值:插入后链表中元素的数量。
- rpop key
返回并弹出指定Key关联的链表中的最后一个元素,即尾部元素,。如果该Key不存,返回nil。
返回值:链表尾部的元素。
4.redis中key通用操作
通用操作
- keys pattern
获取所有匹配pattern参数的Keys。需要说明的是,在我们的正常操作中应该尽量避免对该命令的调用,因为对于大型数据库而言,该命令是非常耗时的,对Redis服务器的性能打击也是比较大的。pattern支持glob-style的通配符格式,如*表示任意一个或多个字符,?表示任意字符,[abc]表示方括号中任意一个字母。 匹配模式的键列表。
- del key [key …]
从数据库删除中参数中指定的keys,如果指定键不存在,则直接忽略。还需要另行指出的是,如果指定的Key关联的数据类型不是String类型,而是List、Set、Hashes和Sorted Set等容器类型,该命令删除每个键的时间复杂度为O(M),其中M表示容器中元素的数量。而对于String类型的Key,其时间复杂度为O(1)。
返回值:实际被删除的Key数量。
- exists key
判断指定键是否存在。
返回值:1表示存在,0表示不存在。
- rename key newkey
为指定的键重新命名,如果参数中的两个Keys的名字相同,或者是源Key不存在,该命令都会返回相关的错误信息。如果newKey已经存在,则直接覆盖。
- type key
获取与参数中指定键关联值的类型,该命令将以字符串的格式返回。
返回值:返回的字符串为string、list、set、hash和zset,如果key不存在返回none。
5.Jedis的使用
5.1.使用jedis操作string类型数据
前提:导入开发jar包
public class StringTest {
/**
* 存值
*/
@Test
public void test1(){
//1 创建连接对象
Jedis jedis=new Jedis();
//2 执行操作
jedis.set("username","王郑浩");
//3 释放连接
jedis.close();
}
/**
* 取值
*/
@Test
public void test2(){
//1 创建连接对象
Jedis jedis=new Jedis();
//2 执行操作
String username = jedis.get("username");
System.out.println("username = " + username);
//3 释放连接
jedis.close();
}
/**
* strlen key 获取字符串的长度
*/
@Test
public void test3(){
//1 创建连接对象
Jedis jedis=new Jedis();
//2 执行操作
Long length = jedis.strlen("username");
System.out.println("length = " + length);
//3 释放连接
jedis.close();
}
/**
* 追加字符串
*/
@Test
public void test4(){
//1 创建连接对象
Jedis jedis=new Jedis();
//2 执行操作
jedis.append("username"," is boy");
//3 释放连接
jedis.close();
}
/**
* 一次性存储多个值
*/
@Test
public void test5(){
//1 创建连接对象
Jedis jedis=new Jedis();
//2 执行操作
jedis.mset("age","18","address","武汉");
//3 释放连接
jedis.close();
}
/**
* 一次性取多个值
*/
@Test
public void test6(){
//1 创建连接对象
Jedis jedis=new Jedis();
//2 执行操作
List<String> list = jedis.mget("username", "age", "address","hobby");
System.out.println("list = " + list);
//3 释放连接
jedis.close();
}
/**
* 给key设置超时时间
*/
@Test
public void test7(){
//1 创建连接对象
Jedis jedis=new Jedis();
//2 执行操作
jedis.setex("hobby",5,"sing");
//3 释放连接
jedis.close();
}
}
5.2 使用Jedis操作hash类型数据
public class HashTest {
/**
* 存值
*/
@Test
public void test1(){
//1 创建连接对象
Jedis jedis=new Jedis();
//2 执行操作
jedis.hset("myhash","username","wangzhenghao");
//3 释放连接
jedis.close();
}
/**
* 取值
*/
@Test
public void test2(){
//1 创建连接对象
Jedis jedis=new Jedis();
//2 执行操作
String username = jedis.hget("myhash", "username");
System.out.println("username = " + username);
//3 释放连接
jedis.close();
}
/**
* 存多组值
*/
@Test
public void test3(){
//1 创建连接对象
Jedis jedis=new Jedis();
Map<String,String> map=new HashMap<>();
map.put("age","20");
map.put("address","黑马");
//2 执行操作
jedis.hmset("myhash",map);
//3 释放连接
jedis.close();
}
/**
* 获取多个值
*/
@Test
public void test4(){
//1 创建连接对象
Jedis jedis=new Jedis();
//2 执行操作
List<String> list = jedis.hmget("myhash", "username", "age", "address");
System.out.println("list = " + list);
//3 释放连接
jedis.close();
}
/**
* 获取多组键值对
*/
@Test
public void test5(){
//1 创建连接对象
Jedis jedis=new Jedis();
//2 执行操作
Map<String, String> map = jedis.hgetAll("myhash");
System.out.println("map = " + map);
//3 释放连接
jedis.close();
}
/**
* 删除key
*/
@Test
public void test6(){
//1 创建连接对象
Jedis jedis=new Jedis();
//2 执行操作
jedis.hdel("myhash","username","address");
//3 释放连接
jedis.close();
}
}
5.3 将JedisPool连接池封装到JedisUtils工具类中
- 配置文件内容
redis.host=172.16.87.160
redis.port=6379
redis.auth=123456
#链接数据库
redis.default.db=0
#客户端超时时间单位是毫秒
redis.timeout=60000
#最大连接数
redis.maxActive=300
#最大空闲数
redis.maxIdle=100
#最小空闲数
redis.minIdle=1
#最大建立连接等待时间
redis.maxWait=1000
#指明是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个
redis.testOnBorrow=true
#当调用return Object方法时,进行有效性检查
redis.testOnReturn=true
- 工具类代码
/*
目的:将jedis连接池封装到工具类中
这个工具类需要做哪些事?
1 读取配置文件
2 创建jedis连接池对象 连接池对象和配置文件只需要加载一次,可以放到static静态代码块中
3 对外提供一个方法,从连接池中获取连接返回
4 对外提供一个方法,释放资源
*/
public class JedisUtils {
private static JedisPool jedisPool;
//连接池对象和配置文件只需要加载一次,可以放到static静态代码块中
static{
//1 读取配置文件
//使用ResourceBundle加载属性文件
ResourceBundle bundle = ResourceBundle.getBundle("jedis");//只能从类路径(src)中读取属性文件,不需要写文件后缀名
//获取值
String host = bundle.getString("redis.host");
int port=Integer.parseInt(bundle.getString("redis.port"));
String password = bundle.getString("redis.auth");
//获取最大连接数和默认数据库
int maxActive = Integer.parseInt(bundle.getString("redis.maxActive"));
//2 创建jedis连接池对象
//创建连接池配置对象
JedisPoolConfig config=new JedisPoolConfig();
config.setMaxTotal(maxActive);
//创建jedis连接池对象
jedisPool=new JedisPool(config,host,port,6000,password);
}
//3 对外提供一个方法,从连接池中获取连接返回
public static Jedis getJedis(){
return jedisPool.getResource();
}
// 4 对外提供一个方法,释放资源
public static void close(Jedis jedis){
if(jedis!=null){
jedis.close();
}
}
}