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 - 图1
其中:
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 - 图2

redis-cli的使用

1、状态回复(最简单的回复)
redis> PING
PONG
redis> SET test 123
OK
Redis - 图3
2、错误回复(以error开头,后面跟着错误信息)
Redis - 图4
3、字符串回复(最常用的一种回复,双引号包裹)
Redis - 图5
4、多行字符串回复(查询一个库中所有的键)
Redis - 图6
5、整数回复(INCR递增数字)
Redis - 图7
6、通过SELECT命令更换数据库,例如选择1号数据库
Redis - 图8
Redis - 图9
数据库中不存在test表
FLUSHALL命令会清空所有数据库的数据,flushdb命令会清空当前数据库的数据。
7、判断数据库 1 中的一个test键是否存在
Redis - 图10
键test1存在
Redis - 图11
8、删除键 可以删除多个,返回值是删除的键的个数。
Redis - 图12
9、type 获得键值的数据类型。
string(字符串)、hash(散列类型)、list(列表类型)、set(集合类型)、zset(有序集合类型)、 zset(有序集合类型)。
Redis - 图13
10、赋值和取值 当键不存在时返回空结果
Redis - 图14
11、增加指定的整数
Redis - 图15
Redis - 图16
12、Decr 减少
Redis - 图17
Decrby test 3 减少指定的数量
Redis - 图18
15、向尾部追加值 append,如果键不存在则将该键的值设置为value,即相当于set key value,追加后返回当前字符串长度。
Redis - 图19
16、获取字符串长度,如果键不存在则返回0
Redis - 图20
17、同时设置/获取多个键值
Redis - 图21
18、EXPIRE k2 10设置生存时间为10s ,Redis在实际使用过程中更多的用作缓存,然而缓存的数据一般都是需要设置生存时间的,生存时间到期后数据销毁
Redis - 图22
TTL的返回值:大于0 的数字—>剩余时间,单位为秒;
-1 代表没有生存时间的限制,永久性存储。
-2 代表数据已经被删除了。
Redis - 图23
19、PERSIST k1清除生存时间,注意:重新设置值也会清除生存时间。
Redis - 图24
20、PEXPIRE test 10000生存时间设置单位为:毫秒
Redis - 图25
一个字符串类型键值,最大存储容量为512MB

Redis数据类型之散列类型

Redis - 图26

赋值与取值

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
Redis - 图27
HSET命令不区分插入和更新操作,当执行插入操作时HSET命令返回1,当执行更新操作时返回0.

2、多字段赋值取值
Redis - 图28

3、获取user散列所有内容
Redis - 图29

4、判断字段是否存在
Redis - 图30
age字段存在,name字段不存在
当字段不存在时进行赋值,若字段存在则该命令不执行操作
Redis - 图31

5、增加字段值(字段值为数字时)
Redis - 图32

6、删除字段,可以删除一个或多个,返回值是被删除的字段个数
Redis - 图33

7、只获取字段名或字段值
Redis - 图34

8、获取字段数量
Redis - 图35

Redis配置文件

在windows平台下默认的配置文件是:redis.windows.conf,这里面配置了非常多的信息,一般配置保持默认,在一些特定的场景下可以自定义配置,常用到的配置项如下:

  • port — 服务端口

  • bind – 绑定ip其他ip不能访问(多个ip空格隔开)

  • databases – 数据库数量,默认16个

  • daemonize – 设置为守护进程(Linux平台)

  • maxmemory – 最大的内存大小(1MB、1GB、1m、1g)

  • maxmemory-policy — 达到内存限制后的处理策略(后面详细说明)

修改后配置文件需要重启Redis服务才能生效。

Redis应用举例

  1. 缓存(数据查询、短连接、新闻内容、商品内容等等)。(最多使用)

  2. 分布式集群架构中的session分离。

  3. 聊天室的在线好友列表。

  4. 任务队列。(秒杀、抢购、12306等等)(左进右出、右进左出)

  5. 应用排行榜。

  6. 网站访问统计。

  7. 数据过期处理(可以精确到毫秒)

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的依赖可以看出来:
Redis - 图36

Jedis实例

实例1

  1. package redis;
  2. import redis.clients.jedis.Jedis;
  3. public class RedisMain {
  4. public static void main(String[] args) {
  5. Jedis jedis = new Jedis("127.0.0.1",6379);
  6. String response = jedis.ping();
  7. System.out.println(response);
  8. }
  9. }

运行结果:
Redis - 图37

实例2

  1. package Redis.String;
  2. import redis.clients.jedis.Jedis;
  3. import java.util.List;
  4. public class StringDemo {
  5. public static void main(String[] args){
  6. //创建Jedis客户端
  7. Jedis jedis = new Jedis("127.0.0.1", 6379);
  8. //操作一个字符串
  9. jedis.set("name","luban"); //插入一个名字,叫luban
  10. System.out.println(jedis.get("name")); //读取一个名字
  11. //对String类型的数据可以加减,前提是kv对应的值是数字
  12. jedis.set("age","18");
  13. jedis.incr("age");
  14. System.out.println(jedis.get("age"));
  15. jedis.decr("age");
  16. System.out.println(jedis.get("age"));
  17. //对一次性插入多条数据
  18. jedis.mset("A","火力压制","B","核童手雷","C","无敌是多么寂寞","D","空中支援");
  19. List<String> results = jedis.mget("A", "B", "C");
  20. for (String result:results) {
  21. System.out.println(result);
  22. }
  23. //设置失效时间
  24. jedis.setex("BUFF",10,"让大龙BUFF持续10秒钟");
  25. while(jedis.exists("BUFF")){
  26. System.out.println("大龙BUFF在身,所向披靡");
  27. try {
  28. Thread.sleep(1000);
  29. } catch (InterruptedException e) {
  30. e.printStackTrace();
  31. }
  32. }
  33. System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++");
  34. //对已经存在的key设置过期时间
  35. jedis.set("BUFF","大龙BUFF持续10秒");
  36. jedis.expire("BUFF",10);
  37. while(jedis.exists("BUFF")){
  38. System.out.println("真是个狠人啊!");
  39. try {
  40. Thread.sleep(1000);
  41. } catch (InterruptedException e) {
  42. e.printStackTrace();
  43. }
  44. }
  45. }
  46. }

实例三

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"));
    }
}