目录
1 Redis基础
NoSQL(Not Only Structured Query Language),即“不仅仅是SQL”,是一项全新的数据库理念,泛指非关系型的数据库。
而Redis是一款主流的NoSQL产品。
1.1 概念
Redis是用C语言开发的一个开源的高性能键值对(key-value)数据库,数据保存在内存里面。
在官方提供测试数据中,50个并发执行100000个请求,读的速度是110000次/s,写的速度是81000次/s ,
且Redis通过提供多种键值数据类型来适应不同场景下的存储需求,目前为止Redis支持的键值数据类型如下:
- 字符串类型 string
- 散列类型 hash
- 列表类型 list
- 集合类型 set
- 有序集合类型 sortedset
1.2 Redis的应用场景
- 缓存(数据查询、短连接、新闻内容、商品内容等等)
- 任务队列。(秒杀、抢购、12306等等)
- 数据过期处理(可以精确到毫秒, 短信验证码)
- 分布式集群架构中的session分离 session 服务器里面
- 聊天室的在线好友列表
- 应用排行榜
- 网站访问统计
2 Windows版的Redis
2.1 下载
Linux版的Redis从官网下载,Window版的Redis从GitHub下载。
- 官网下载地址:<http://redis.io/download>
- github下载地址:https://github.com/MSOpenTech/redis/tags
2.2 目录结构介绍
目录或文件 | 功能 |
redis-benchmark | 性能测试工具 |
redis-check-aof | AOF文件修复工具 |
redis-cli | 命令行客户端 |
redis-server | redis服务器启动命令 |
redis.windows.conf | redis核心配置文件 |
2.3 安装与启动
- 安装
直接解压压缩包,不需要其他操作。
- 启动方式1
我个人比较推荐的方式是:找到redis服务端双击打开,出现黑窗口,然后打开redis客户端软件,在客户端上进行操作和数据可视化展示。
点击 “redis-server.exe” 文件启动Redis服务端,此时会弹出一个黑窗口,关闭该窗口则Redis也会关闭。这种操作在每次使用Redis的时候都要执行一次。
点击 “redis-cli.exe” 进入Redis客户端,在这个黑窗口中可以输入Redis指令来使用Redis。
- 启动方式2
如果不想每次都手动开启Redis服务端,则需要稍微配置一下:
首先,进入到Redis的解压目录之下,然后选中文件路径,输入“cmd”,进入到当前路径下的命令行界面:
输入如下指令安装Redis服务:
redis-server.exe --service-install redis.windows.conf --loglevel verbose
在“计算机管理”中找到刚才已经安装的Redis服务,右键点击启动,完成!以后就不用每次都手动开启Redis服务端了。
与之相反的卸载Redis服务的指令如下:
redis-server.exe --service-uninstall
2.4 客户端工具
双击客户端工具的安装包:redis-desktop-manager-0.9.3.817.exe,一直“下一步”完成安装。
3 Redis的数据类型
- key
是String类型的,不要超过1024个字节
key的常用的写法: 项目名_子模块_key名称; 例如:jd_user_uname
- value
value支持5种数据类型:
- 字符串(String)
- 哈希(hash,类似map)
- 字符串列表(list,类似LinkedList)
- 字符串集合(set,类似hashset)
- 有序的字符串集合(sorted-set或者叫zset,类似有序的set)
- Redis自增指令
百度“Redis命令中心”可查看更多Redis命令
INCR varName #varName初始值为0,INCR表示自增1,即此时值为1
DECR varName #自减1
INCRBY varName x #自增x,类似于+=
DECRBY varName x #自减x,类似于-=
4 Redis中的Hash
Redis中hash是一个“键值对集合”。
Redis中的hash是一个string类型的field和value(看作map)的映射表,hash特别适合用于存储对象。
- 插入数据指令
hset user1 name zhangsan #创建一个名字为user1,值为name-zhangsan的hash数据类型,值本身就是一个键值对
hset user1 age 18 #创建一个名字为user1,值为age-18的hash数据类型(集合中只有一个键值对)
hmset user2 name lisi age 20 #创建一个名字为user2,值为键值对的集合:name-lisi、age-20
- 查询数据指令
hget user1 name #获取user1变量的name属性
hget user1 age #获取user1变量的age属性
hgetall user1 #获取user1变量的全部数据
hlen user1 #获取user1变量的长度,以“键值对”为单位
hdel user1 name #删除user1变量的“name”字段
del user1 #删除整个hash变量
5 Redis 中的List
Redis中的List类似链表,有序(按照插入顺序排序),可以重复,适用于做消息队列
你可以添加一个元素到列表的头部(最左边)或者尾部(最右边)
lpush myList a b c d e f #将a-f依次插入到列表的头部(从左侧将数据依次推入List)
rpush myList g #将g插入到列表的尾部
lrange myList 0 5 #从左侧遍历myList,遍历的范围是索引0-5这6个元素
lpop #弹出/删除最左侧的元素
rpop #弹出/删除最右侧的元素
6 Redis 中的Set
Redis中的Set类似java中的hashset
特点:无序+唯一
应用场景: 投票
sadd mySet a bcd ef #向mySet中添加元素a、bcd、ef
srem mySet a #从mySet中移除元素a
smember mySet #返回集合中的所有成员(不保证顺序)
7 Redis 中的通用操作指令
keys * #查询所有的key
exists key #参看key键是否存在,存在则返回1,否则返回0
expire key 秒数 #设置这个key在缓存中的存活时间
ttl key #展示指定key的剩余时间,返回值 -1:永不过期,返回值-2:已过期或者不存在
del key #删除指定key
rename key #新key:重命名(旧名字 新名字)
type key #判断该key的数据类型
ping #测试连接是否连接
8 redis 的持久化
Redis的高性能是由于其将所有数据都存储在内存中,
为了使Redis在重启之后仍能保证数据不丢失,需要将数据从内存中同步到硬盘中,这一过程就是持久化。
Redis支持两种方式的持久化,一种是RDB方式,一种是AOF方式。
可以单独使用其中一种或将二者结合使用。
8.1 RDB 持久化机制
RDB持久化:在指定的时间间隔内,将内存中数据以快照的方式写入到二进制文件中,默认的文件名为dump.rdb。
这种方式是默认已经开启了,不需要配置。
8.2 AOF 持久化机制
AOF持久化机制:将每一个收到的写命令都通过write函数追加到文件中,默认的文件名是appendonly.aof。
这种方式默认是没有开启的,要使用时候需要配置。
- redis.windows.conf 配置文件如下:
关键字 | 持久化时机 | 解释 |
appendfsync | always | 每执行一次更新命令,持久化一次 |
appendfsync | everysec | 每秒钟持久化一次 |
appendfsync | no | 不持久化 |
8.3 比较
RDB持久化机制的优点
- RDB 是一个非常紧凑的文件,它保存了 Redis 在某个时间点上的数据集。 这种文件非常适合用于进行备份。
- RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快(因为其文件要比AOF的小)
- RDB 的性能要比AOF更好
RDB持久化机制的缺点
- RDB 的持久化不够及时,可能会存在数据丢失。
- RDB 持久化时如果文件过大可能会造成服务器的阻塞,停止客户端请求。
AOF持久化机制的优点
- AOF 更加耐久(可每秒或每次操作保存一次)
- AOF 文件有序地保存了对数据库执行的所有写入操作, 这些写入操作以 Redis 协议的格式保存,因此 AOF 文件的内容非常容易被人读懂, 对文件进行分析也很轻松。
- AOF 是增量操作
AOF持久化机制的缺点
- 对于相同的数据集来说,AOF 文件的体积通常要大于 RDB 文件的体积。
- 根据所使用的 fsync 策略,AOF 的速度可能会慢于 RDB。
选择
- 如果非常关心数据, 但仍然可以承受数分钟以内的数据丢失,选择RDB 持久化。
- 如果对数据的完整性要求比较高, 选择AOF
9 Jedis
Jedis就是使用Java语言操作Redis客户端的jar包。
9.1 Jedis使用步骤
- 导入Jedis的jar包
- 创建jedis对象
- 操作redis
- 释放资源
9.2 Jedis入门代码
下面这段代码,是Jedis的最基本使用:创建Jedis对象,分别存数据和取出数据。
package com.lmy.redis;
import redis.clients.jedis.Jedis;
/**
* Java连接Redis内存数据库
* 使用驱动包jedis中的Jedis类就可以操作数据库了
* 不用手动转码,不用抛出异常
* redis命令名就是Jedis的方法名
*/
public class RedisDemo {
public static void main(String[] args) {
setString();
getString();
}
//写JAVA代码,往redis中存一个键值对
public static void setString(){
//创建Jedis对象,传入ip和端口号
Jedis jedis = new Jedis("localhost",6379);
//System.out.println(jedis);
//向数据库中存字符串,redis指令就是java方法名
jedis.set("lmy","happy");
jedis.set("happy","lmy");
jedis.close();
}
//取出Redis数据库中的值
public static void getString(){
Jedis jedis = new Jedis("localhost",6379);
String lmy = jedis.get("happy");
System.out.println(lmy);
jedis.close();
}
}
9.3 加入连接池的Jedis入门代码
jedis连接资源的创建与销毁是很消耗程序性能,所以Jedis为我们提供了Jedis的池化技术
jedisPool在创建时初始化一些连接资源存储到连接池中
使用Jedis连接资源时不需要创建,而是从连接池中获取一个资源进行redis的操作
使用完毕后,不需要销毁该jedis连接资源,而是将该资源归还给连接池,供其他请求使用
下面这段代码,jedis对象不再是直接new出来的,而是从连接池中获取到的,包含如下步骤:
- 创建连接池配置对象
- 创建连接池对象
- 从连接池中取连接
这样的写法,发现如下问题:连接池配置被写死到代码中,所以接下来会用配置文件来优化操作。
package com.lmy.redis;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
/**
* 内存数据库jedis的连接池
* 连接池对象有个方法可以返回Jedis对象资源,用来实际操作redis数据库
*/
public class RedisPoolDemo {
public static void main(String[] args) {
//创建连接池的配置对象
JedisPoolConfig config = new JedisPoolConfig();
//配置
config.setMaxIdle(50); //最大空闲时间
config.setMaxTotal(10); //连接池中的最大连接数
//创建连接池对象
JedisPool jedisPool = new JedisPool(config, "127.0.0.1", 6379); //配置写死到代码中
//连接池对象返回jedis资源
Jedis jedis = jedisPool.getResource();
String value = jedis.get("lmy");
System.out.println(value);
jedis.close();
}
}
9.4 Jedis连接池工具类
- java.util.ResourceBundle类(只能用来读取properties配置文件,适用于参数比较少的配置文件数据读取),专门处理properties配置文件,它的静态方法getBundle返回子类对象,再调用该对象的getString方法返回键的值,这样每次读取properties配置文件时,不再用“流”来读取,操作更加简单
- java.util.UUID随机字符串对象类的静态方法 randomUUID,获取对象后再调用方法toString(可产生一个32位随机字符串,该字符串永不重复,基本无范围)
将上述的Demo写成一个标准的工具类,代码如下:
package com.lmy.utils;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import java.util.ResourceBundle;
/**
* 读取配置文件
* 创建连接池对象
* 从连接池中取连接
*/
public class JedisPoolUtils {
static JedisPool jedisPool; //连接池对象设置为静态属性
static{
ResourceBundle bundle = ResourceBundle.getBundle("redis"); //读取配置文件
String maxTotal = bundle.getString("maxTotal");
String host = bundle.getString("host");
String port = bundle.getString("port");
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(Integer.parseInt(maxTotal));
jedisPool = new JedisPool(config, host, Integer.parseInt(port)); //创建连接池对象
}
//静态方法,返回redis数据库的连接对象jedis
public static Jedis getJedis(){
return jedisPool.getResource(); //从连接池中取连接
}
}
于是每次使用Jedis时就直接调用该工具类产生一个Jedis对象来操作redis数据库,下面的代码时操作redis数据库中常用数据类型的一些操作:
package com.lmy.redis;
import com.lmy.utils.JedisPoolUtils;
import redis.clients.jedis.Jedis;
import java.util.List;
/**
* 测试连接池工具类
*/
public class TestJedisPool {
public static void main(String[] args) {
//getString();
//setList();
//getList();
setHash();
getHash();
}
/**
* 往redis中存入list
* 数据类型list,一个键对应多个值
*/
public static void setList(){
Jedis jedis = JedisPoolUtils.getJedis();
//左侧推入元素
jedis.lpush("myList","a","b","c");
jedis.close();
}
/**
* 获取redis中的list
*/
public static void getList(){
Jedis jedis = JedisPoolUtils.getJedis();
//左侧推入元素
List<String> myList = jedis.lrange("myList", 0, -1); //取出myList中的所有元素
for (String s : myList) {
System.out.println(s);
}
jedis.close();
}
/**
* 存储散列类型hash
*/
public static void setHash(){
Jedis jedis = JedisPoolUtils.getJedis();
jedis.hset("mySet","key","value");
jedis.close();
}
/**
* 取出散列类型hash
*/
public static void getHash(){
Jedis jedis = JedisPoolUtils.getJedis();
String value = jedis.hget("mySet", "key");
System.out.println(value);
jedis.close();
}
public static void getString(){
Jedis jedis = JedisPoolUtils.getJedis();
String happy = jedis.get("happy");
System.out.println(happy);
jedis.close();
}
}