Redis简介
Redis官网: http://redis.io/
Redis是一个开源的高性能键值对数据库。它通过提供多种键值数据类型来适应不同场景下的存储需求,并且借助许多高层级的接口使其可以胜任,如缓存、队列系统的不同角色。
源码托管在github https://github.com/antirez/redis
Redis单例模式
Redis的特性
多种数据类型存储
字符串类型
散列类型
列表类型
集合类型
有序集合类型
内存存储与持久化
内存的读写速度远快于硬盘
自身提供了持久化功能(RDB、AOF两种方式)
功能丰富
可用作缓存、队列、消息订阅/发布
支持键的生存时间
按照一定规则删除相应的键
简单稳定
相比SQL而言更加简单
不同语言的客户端丰富
基于C语言开发,代码量只有3万多行
Redis版本说明
Redis的版本规则如下:
次版本号(第一个小数点后的数字)为偶数的版本是稳定版本(2.4、2.6等),奇数为非稳定版本(2.5、2.7),一般推荐在生产环境使用稳定版本。
目前Redis的最新稳定版本是:3.0.5 。
但是,在Windows平台下:
64位系统的最新稳定版本是2.8.9
32位系统的最新稳定版本是2.6
说明:Redis官方是不支持Windows平台的,Windows版本是由微软自己建立的分支,基于官方的Redis源码上进行编译、发布、维护的,所以Windows平台的Redis版本都要略低于官方版本。
下载Redis
Linux版本 2.8.11 :
http://download.redis.io/releases/redis-2.8.11.tar.gz
Windows(64位)版本 2.8.9 :
https://github.com/MSOpenTech/redis/blob/2.8/bin/release/redis-2.8.9.zip?raw=true
Windows(32位)版本 2.6 :
https://github.com/MSOpenTech/redis/blob/2.6/bin/release/redisbin.zip?raw=true
Windows(64位)下安装Redis
压缩包解压后
其中:
Redis Release Notes.docx 文件是关于Redis的描述文件
redis.windows.conf 文件是默认的配置文件
RedisService.docx 安装手册
注册服务:
redis-server —service-install redis.windows.conf —loglevel verbose
卸载服务:
redis-server —service-uninstall
启动Redis:
redis-server —service-start
停止Redis:
redis-server —service-stop
Linux下安装Redis
mkdir -p /root/apps/redis
cd /root/apps/redis
wget http://download.redis.io/releases/redis-2.8.11.tar.gz
tar xzf redis-2.8.11.tar.gz
cd redis-2.8.11
make
make install
修改配置文件,使用redis后台运行:
vi /etc/redis.conf
daemonize yes
启动 redis-server /etc/redis.conf
Redis可执行文件说明

redis-cli的使用
1、状态回复(最简单的回复)
redis> PING
PONG
redis> SET test 123
OK
2、错误回复(以error开头,后面跟着错误信息)
3、字符串回复(最常用的一种回复,双引号包裹)
4、多行字符串回复(查询一个库中所有的键)
5、整数回复(INCR递增数字)
6、通过SELECT命令更换数据库,例如选择1号数据库

数据库中不存在test表
FLUSHALL命令会清空所有数据库的数据,flushdb命令会清空当前数据库的数据。
7、判断数据库 1 中的一个test键是否存在
键test1存在
8、删除键 可以删除多个,返回值是删除的键的个数。
9、type 获得键值的数据类型。
string(字符串)、hash(散列类型)、list(列表类型)、set(集合类型)、zset(有序集合类型)、 zset(有序集合类型)。
10、赋值和取值 当键不存在时返回空结果
11、增加指定的整数

12、Decr 减少
Decrby test 3 减少指定的数量
15、向尾部追加值 append,如果键不存在则将该键的值设置为value,即相当于set key value,追加后返回当前字符串长度。
16、获取字符串长度,如果键不存在则返回0
17、同时设置/获取多个键值
18、EXPIRE k2 10设置生存时间为10s ,Redis在实际使用过程中更多的用作缓存,然而缓存的数据一般都是需要设置生存时间的,生存时间到期后数据销毁
TTL的返回值:大于0 的数字—>剩余时间,单位为秒;
-1 代表没有生存时间的限制,永久性存储。
-2 代表数据已经被删除了。
19、PERSIST k1清除生存时间,注意:重新设置值也会清除生存时间。
20、PEXPIRE test 10000生存时间设置单位为:毫秒

一个字符串类型键值,最大存储容量为512MB
Redis数据类型之散列类型

赋值与取值
HSET key field value
HGET key field
HMSET key field value [field value …]
HMGET key field [field …]
HGETALL key
1、创建hash散列 user 字段 username value zhangsan
HSET命令不区分插入和更新操作,当执行插入操作时HSET命令返回1,当执行更新操作时返回0.
2、多字段赋值取值
3、获取user散列所有内容
4、判断字段是否存在
age字段存在,name字段不存在
当字段不存在时进行赋值,若字段存在则该命令不执行操作
5、增加字段值(字段值为数字时)
6、删除字段,可以删除一个或多个,返回值是被删除的字段个数
7、只获取字段名或字段值
Redis配置文件
在windows平台下默认的配置文件是:redis.windows.conf,这里面配置了非常多的信息,一般配置保持默认,在一些特定的场景下可以自定义配置,常用到的配置项如下:
port — 服务端口
bind – 绑定ip其他ip不能访问(多个ip空格隔开)
databases – 数据库数量,默认16个
daemonize – 设置为守护进程(Linux平台)
maxmemory – 最大的内存大小(1MB、1GB、1m、1g)
maxmemory-policy — 达到内存限制后的处理策略(后面详细说明)
Redis应用举例
缓存(数据查询、短连接、新闻内容、商品内容等等)。(最多使用)
分布式集群架构中的session分离。
聊天室的在线好友列表。
任务队列。(秒杀、抢购、12306等等)(左进右出、右进左出)
应用排行榜。
网站访问统计。
数据过期处理(可以精确到毫秒)
Redis的Java客户端
Redis不仅是使用命令来操作,现在基本上主流的语言都有客户端支持,比如java、C、C#、C++、php、Node.js、Go等。
在官方网站里列一些Java的客户端,有Jedis、Redisson、Jredis、JDBC-Redis、等其中官方推荐使用Jedis和Redisson。
在企业中用的最多的就是Jedis,下面我们就重点学习下Jedis。
Jedis同样也是托管在github上,地址:https://github.com/xetorthio/jedis
Jedis基本上实现了所有的Redis命令,并且还支持连接池、集群等高级的用法,而且使用简单,使得在Java中使用Redis服务将变得非常的简单。
Jedis目前最新的版本是2.5.1。Jedis只有一个依赖,那就是commons-pool。
从Maven的依赖可以看出来:
Jedis实例
实例1
package redis;import redis.clients.jedis.Jedis;public class RedisMain {public static void main(String[] args) {Jedis jedis = new Jedis("127.0.0.1",6379);String response = jedis.ping();System.out.println(response);}}
运行结果:
实例2
package Redis.String;import redis.clients.jedis.Jedis;import java.util.List;public class StringDemo {public static void main(String[] args){//创建Jedis客户端Jedis jedis = new Jedis("127.0.0.1", 6379);//操作一个字符串jedis.set("name","luban"); //插入一个名字,叫lubanSystem.out.println(jedis.get("name")); //读取一个名字//对String类型的数据可以加减,前提是kv对应的值是数字jedis.set("age","18");jedis.incr("age");System.out.println(jedis.get("age"));jedis.decr("age");System.out.println(jedis.get("age"));//对一次性插入多条数据jedis.mset("A","火力压制","B","核童手雷","C","无敌是多么寂寞","D","空中支援");List<String> results = jedis.mget("A", "B", "C");for (String result:results) {System.out.println(result);}//设置失效时间jedis.setex("BUFF",10,"让大龙BUFF持续10秒钟");while(jedis.exists("BUFF")){System.out.println("大龙BUFF在身,所向披靡");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++");//对已经存在的key设置过期时间jedis.set("BUFF","大龙BUFF持续10秒");jedis.expire("BUFF",10);while(jedis.exists("BUFF")){System.out.println("真是个狠人啊!");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}}
实例三
package redis.set;
import redis.clients.jedis.Jedis;
import java.util.Set;
/**
* Describe: 请补充类描述
*/
public class SetMain {
public static void main(String[] args) {
Jedis jedis = new Jedis("127.0.0.1", 6379);
//河南武林人物登记表---杜绝冒名顶替的情况
String[] hreos = new String[]{
"鲁班", "黄忠", "狄仁杰", "李元芳", "香香", "后羿", "伽罗", "虞姬",
"张良", "司马懿", "嬴政", "安琪拉", "貂蝉", "诸葛亮", "高渐离", "小乔",
"亚瑟", "张飞", "程咬金", "项羽", "白起", "廉颇", "苏烈", "牛魔",
"阿珂","兰陵王","孙悟空","韩信","元歌","裴庆虎","娜可露露","李白",
"扁鹊","太乙真人","姜子牙","蔡文姬","鬼谷子","盾山","孙膑","大乔" };
//创建并设置一个set的值
jedis.sadd("SOLO:register", hreos);
//获取一个set中所有的元素
Set<String> hreoSet = jedis.smembers("SOLO:register");
for (String name : hreoSet) {
System.out.print(name + " "); //set集合的特点:无序、无重复元素
}
System.out.println();
//判断一个成员是否属于某条指定的set数据
boolean isComing = jedis.sismember("SOLO:register", "司马懿"); //判断大侠司马懿是否到来
if (!isComing) {
System.out.println("英雄 司马懿尚未登记.");
}
//计算一个set中有多少元素
long totalNum = jedis.scard("SOLO:register");
System.out.println("有" + totalNum + " 位大侠已经登记了!");
System.out.println();
//大侠司马懿没有来,是因为报名参与另外一个会议 三国历史武林大会
String[] hreoArr = new String[]{ "刘备", "关羽", "司马懿", "典韦", "周瑜", "甄姬", "刘禅"};
jedis.sadd("sanguo:register", hreoArr); //国际武林大会登记表
Set<String> xinhreos = jedis.smembers("sanguo:register");
for (String name : xinhreos) {
System.out.print(name + "--- "); //集合的特点:无序、无重复元素
}
System.out.println();
//计算两个Set之间的交集
Set<String> users = jedis.sinter("SOLO:register", "sanguo:register");
for (String name : users) {
System.out.print(name + " ");
}
System.out.println();
//计算两个Set之间的并集
users = jedis.sunion("SOLO:register", "sanguo:register");
for (String name : users) {
System.out.print(name + " ");
}
System.out.println();
System.out.println();
//计算两个集合的差集
users = jedis.sdiff("SOLO:register", "sanguo:register");
for (String name : users) {
System.out.print(name + " ");
}
System.out.println();
System.out.println();
//将两个集合计算出来的差集保存起来,升级为超级Vip
jedis.sdiffstore("ZBhero","SOLO:register", "sanguo:register");
for (String name : jedis.smembers("ZBhero")) {
System.out.print(name + " ");
}
}
}
package redis.set;
import redis.clients.jedis.Jedis;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.Set;
/**
* Describe: 请补充类描述
*/
public class Transform {
public static void main(String[] args) {
Jedis jedis = new Jedis("127.0.0.1", 6379);
//浏览某商品的用户
jedis.sadd("viewUsers", "张良", "司马懿", "嬴政", "安琪拉", "貂蝉", "诸葛亮", "高渐离", "小乔",
"亚瑟", "张飞", "程咬金", "项羽", "白起", "廉颇", "苏烈", "牛魔");
//下单用户
jedis.sadd("orderUsers", "张良", "司马懿", "嬴政", "安琪拉", "貂蝉", "诸葛亮", "高渐离", "小乔");
//支付用户
jedis.sadd("paymentUsers", "张良", "司马懿", "嬴政", "安琪拉");
//浏览过商品的用户,有哪些下单了。
jedis.sinterstore("view2order", "viewUsers", "orderUsers"); //求两个集合的交集
//计算浏览某商品的用户数量 和 既浏览又下单的用户的数量
double viewUserNum = jedis.scard("viewUsers");
double orderUserNum = jedis.scard("view2order");
NumberFormat formatter = new DecimalFormat("0.00");
Double x = new Double(orderUserNum / viewUserNum);
System.out.print("订单" + orderUserNum + "/浏览" + viewUserNum + "转化:" + formatter.format(x) + " 他们是:");
for (String name : jedis.smembers("view2order")) {
System.out.print(name + " ");
}
System.out.println();
//浏览并且下单的用户,最终支付的转化
jedis.sinterstore("order2Payment", "view2order", "paymentUsers"); //求两个集合的交集
double paymentUserNum = jedis.scard("paymentUsers");
x = new Double(paymentUserNum / orderUserNum);
System.out.print("支付" + paymentUserNum + "/订单" + orderUserNum + "转化:" + formatter.format(x) + " 他们是:");
for (String name : jedis.smembers("order2Payment")) {
System.out.print(name + " ");
}
System.out.println();
//浏览并最终支付的用户的转化
x = new Double(paymentUserNum / viewUserNum);
System.out.print("支付" + paymentUserNum + "/浏览" + viewUserNum + "转化:" + formatter.format(x)+" 他们是:");
for (String name : jedis.smembers("order2Payment")) {
System.out.print(name + " ");
}
System.out.println();
}
}
实例四 王者峡谷英雄solo
王者峡谷+英雄
package Redis.String;
import redis.clients.jedis.Jedis;
import java.util.Random;
/**
* 王者峡谷地图
*/
public class KingsCanyon implements Runnable {
private Random random = new Random();
private Jedis jedis;
private String redisCount;
private String kingCanyonName;
public KingsCanyon(String redisCount,String kingCanyonName){
this.redisCount=redisCount;
this.kingCanyonName=kingCanyonName;
}
public void run() {
jedis = new Jedis("127.0.0.1",6379);
String[] heros = new String[]{
"鲁班","后羿","黄忠","孙尚香","虞姬","狄仁杰",
"妲己","甄姬","貂蝉","安其拉","周瑜","张良",
"亚瑟","张飞","程咬金","项羽","白起","廉颇",
};
while(true){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
int p1 = random.nextInt(heros.length);
int p2 = random.nextInt(heros.length);
while (p1 == p2){
p2 = random.nextInt(heros.length);
}
System.out.println("亡者峡谷: "+kingCanyonName+"\t"+"英雄: "+heros[p1]+"VS"+"英雄"+heros[p2]);
jedis.incr(redisCount);
}
}
}
Hero实体类
package Redis.String;
import java.io.Serializable;
public class Hero implements Serializable {
private String name;
private int age;
//alt + INS 快捷键
public Hero() {
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public Hero(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Hero{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
solo
package Redis.String;
import redis.clients.jedis.Jedis;
public class Solo implements Runnable {
private Jedis jedis;
private String redisCount;
public Solo(String redisCount){
this.redisCount = redisCount;
}
public void run() {
jedis = new Jedis("127.0.0.1", 6379);
while(true){
try {
Thread.sleep(1000);
System.out.println("=======当前总共solo次数为:"+jedis.get(redisCount));
} catch (InterruptedException e) {
System.out.println("亡者峡谷场景出错.....");
e.printStackTrace();
}
}
}
}
统计solo次数
package Redis.String;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 调用KingCanyon和Solo线程进行统计各个线路的solo次数
*/
public class SoloPool {
public static void main(String[] args){
//创建一个固定条数的线程池
ExecutorService executorService = Executors.newFixedThreadPool(10);
//下路
executorService.submit(new KingsCanyon("SoloTotal","手起刀落,兵不血刃.下路"));
//上路
executorService.submit(new KingsCanyon("SoloTotal","杀人于无形之中-上路"));
//中路
executorService.submit(new KingsCanyon("SoloTotal","掌握知识,掌握未来.中路"));
//一秒统计一次总共Solo的次数
executorService.submit(new Solo("SoloTotal"));
}
}
