Redis应用场景

缓存

Redis的性能优于Memcached,且数据结构更多样化,支持多种过期淘汰策略;更重要的是支持持久化,持久化策略丰富。

计数,生成唯一ID

Redis的命令都是原子性的,可以轻松地利用INCR,DECR命令来构建计数器系统。同理,可以用INCR命令,为游戏玩家生成唯一的ID。

示例:

import java.util.Scanner;
import redis.clients.jedis.Jedis;
public class Incr {
    // 访问一次web,计数一次
    public static void accessWeb(Jedis jedis, String url) {
        jedis.incr(url);
    }
    public static void main(String[] args) {
        String host = "127.0.0.1";
        int port = 10011;
        Jedis jedis = new Jedis(host, port);
        String url = "www.ucloud.cn";
        //获取原始的值
        long origin_cnt = Long.parseLong(jedis.get(url));
        //接收终端输⼊入
        Scanner sc = new Scanner(System.in);
        while(true) {
            System.out.println("访问 " + url +" ? [y/n]");
            String ac = sc.nextLine();
            if (ac.equals("y")) {
            accessWeb(jedis,url);
            }else {
                break;
            }
        }
        sc.close();
        //获取现在的值
        long now_cnt = Long.parseLong(jedis.get(url));
        //计算访问www.ucloud.cn的次数。
        System.out.println("你总共访问了 "+ url+ " " + Long.toString(now_cnt - o
        rigin_cnt)+"次.");
        jedis.close();
    }
}

输出

访问 www.ucloud.cn ? [y/n]
y
访问 www.ucloud.cn ? [y/n]
y
访问 www.ucloud.cn ? [y/n]
y
访问 www.ucloud.cn ? [y/n]
y
访问 www.ucloud.cn ? [y/n]
n
你总共访问了 www.ucloud.cn 4次.

排行榜应用,取TOP N操作

游戏服务器中涉及到很多排行信息,取TOP N操作,比如玩家等级排名、金钱排名、战斗力排名等,虽然通过关系数据库也可以实现,但是随着数据量增多,数据库的排序压力就会变大。

这些操作对于Redis来说都很简单,即使你有几百万个用户,每分钟都会有几百万个新的得分。

只需要jedis.zrevrank(key,member);

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ThreadLocalRandom;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Tuple;
public class HelloWorldApp {
    static int TOTAL_SIZE = 10000;
    //获取长度为8,由小写字母组成的随机名称
    public static String randomName(int length) {
        StringBuilder builder = new StringBuilder(length);
        for (int i = 0; i < length; i++) {
            builder.append((char) ThreadLocalRandom.current().nextInt(97,122));//a~z
        }
        return builder.toString();
    }
    public static void main(String[] args)
    {
        //连接信息,从控制台可以获得
        String host = "127.0.0.1";
        int port = 10011;
        Jedis jedis = new Jedis(host, port);
        //排行榜应用,取TOP N操作
        try {
            //Key(键)
            String key = "游戏排行榜!";
            //清除可能的已有数据
            jedis.del(key);
            //模拟生成若干个游戏选手
            List<String> playerList = new ArrayList<String>();
            for (int i = 0; i < TOTAL_SIZE; ++i)
            {
                //随机生成每个选手的名称
                playerList.add(randomName(8));
            }
            System.out.println("输入全部" + TOTAL_SIZE +" 选手 ");
            //记录每个选手的得分
            for (int i = 0; i < playerList.size(); i++)
            {
                //随机生成数字,模拟选手的游戏得分
                int score = (int)(Math.random()*5000);
                String member = playerList.get(i);
                if (i < 10) {
                    System.out.println("选手名称:" + member + ", 选手得分: " + score
                    );
                }
                //将选手的名称和得分,都加到对应key的SortedSet中去
                jedis.zadd(key, score, member);
            }
            System.out.println("更多选手得分......");
            //从对应key的SortedSet中获取已经排好序的选手列表
            Set<Tuple> scoreList = jedis.zrevrangeWithScores(key, 0, -1);
            //输出打印Top100选手排⾏行榜
            System.out.println();
            System.out.println(" "+key);
            System.out.println(" Top 100 选手");
            scoreList = jedis.zrevrangeWithScores(key, 0, 99);
            for (Tuple item : scoreList) {
                System.out.println("选手名称:"+item.getElement()+", 选手得分:"+Doubl
                e.valueOf(item.getScore()).intValue());
            }
            //输出某个选手的排名
            String player = playerList.get(0);
            System.out.println();
            System.out.println(" "+key);
            System.out.println(" 选手"+player+"的排名: "+ jedis.zrevrank(key,player));
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            jedis.quit();
            jedis.close();
        }
    }
}

输出:

输入全部10000 选手
选手名称:hegmsrcs, 选手得分: 1191
选手名称:ocvhhxke, 选手得分: 653
选手名称:ekgdllwj, 选手得分: 694
选手名称:kxwsnnjj, 选手得分: 2051
选手名称:vjflcktn, 选手得分: 2100
选手名称:jtrlmnlk, 选手得分: 4257
选手名称:aatbchgk, 选手得分: 2912
选手名称:phukvvxy, 选手得分: 2044
选手名称:aqqdqnel, 选手得分: 1859
选手名称:hyndvsen, 选手得分: 2381
更多选手得分......
游戏排行榜!
Top 100 选手
选手名称:kqyhxehe, 选手得分:4999
选手名称:datnveli, 选手得分:4998
选手名称:ovxfislm, 选手得分:4997
选手名称:llqnigun, 选手得分:4997
选手名称:ckikmasa, 选手得分:4997
选手名称:wlmdrpnx, 选手得分:4996
选手名称:trslhgga, 选手得分:4996
选手名称:bkbfnutg, 选手得分:4996
选手名称:yqesafda, 选手得分:4995
选手名称:grtjbjrq, 选手得分:4995
更多选手排名......
游戏排行榜!
选手hegmsrcs的排名: 7618

构建队列系统

通过使用list的lpop及lpush命令进行队列的写入和消费,可以轻松实现消息队列;由于它是独立于游戏服务器的,所以多个游戏服务器可以通过它来交换数据、发送事件,通过使用sorted set,甚至可以构建有优先级的队列系统。

import redis.clients.jedis.Jedis;
class ProducerThread extends Thread {
    private Jedis jedis;
    private int port;
    private String host,key;
    ProducerThread(String host, int port, String key) {
        super(host);
        this.key = key;
        this.port = port;
        jedis = new Jedis(host, port);
    }
    protected void finalize( ){
        jedis.close();
    }
    public void run() {
        //System.out.println("ProducerThread is running...");
        for(int i = 0; i < 10; i++) {
            jedis.lpush(key, "job"+ Integer.toString(i));
            System.out.println("ProducerThread push job"+ Integer.toString(i));
        }
    }
}
class ConsumerThread extends Thread {
    private Jedis jedis;
    private int port;
    private String host,key;
    ConsumerThread(String host, int port, String key) {
        super(host);
        this.key = key;
        this.port = port;
        jedis = new Jedis(host, port);
    }
    protected void finalize( ) {
        jedis.close();
    }
    public void run() {
        //构建队列系统
        //System.out.println("ConsumerThread is running...");
        for(int i = 0; i < 10; i++) {
            System.out.println("ConsumerThread pop "+ jedis.lpop(key));
        }
    }
}
public class QueueSystem {
    public static void main(String[] args) {
        String host = "127.0.0.1";
        int port = 10011;
        String key = "jobs";
        ProducerThread pT = new ProducerThread(host,port,key);
        ConsumerThread cT = new ConsumerThread(host,port,key);
        pT.start();
        cT.start();
    }
}

输出:

ConsumerThread pop null
ProducerThread push job0
ConsumerThread pop job0
ProducerThread push job1
ConsumerThread pop job1
ProducerThread push job2
ConsumerThread pop job2
ProducerThread push job3
ConsumerThread pop job3
ProducerThread push job4
ConsumerThread pop job4
ProducerThread push job5
ConsumerThread pop job5
ProducerThread push job6
ConsumerThread pop job6
ProducerThread push job7
ConsumerThread pop job7
ProducerThread push job8
ConsumerThread pop job8
ProducerThread push job9

取最新N条评论

如果要取网站的最新评论

import java.util.List;
import java.util.UUID;
import redis.clients.jedis.Jedis;
public class LastestN {
    static int TOTAL_NUM = 5000;
    static String host = "127.0.0.1";
    static int port = 10011;
    static String key = "comments";
    static Jedis jedis;
    public static void getLastestComments(int start, int num) {
        System.out.println("最新20条评论:");
        List<String> lastest_list = jedis.lrange(key, start, start + num - 1);
        if (lastest_list.size() < num) {
            //get remaining comments from db
        }
        for (int i = 0; i < lastest_list.size(); i ++) {
            System.out.println("CommentID " + Integer.toString(i) + " "+lastest_
            list.get(i));
        }
    }
    public static void setComments() {
        System.out.println(key);
        for (int i = 0; i < TOTAL_NUM; i++) {
            String commentID = UUID.randomUUID().toString();
            jedis.lpush(key, commentID);
            jedis.ltrim(key, 0, 99);//永远最多保存100条评论在redis中。
            if (i < 10) {
                System.out.println("CommentID: " + commentID);
            }
        }
        System.out.println("更多CommentID......");
        System.out.println("");
    }
    public static void main(String[] args) {
        jedis = new Jedis(host, port);
        //添加若干条评论;
        setComments();
        //取最新20条评论
        getLastestComments(0,20);
        jedis.close();
    }
}

输出:

comments
CommentID: b93db3f4-ff88-422a-ad49-bcbf7de5a396
CommentID: 3bf56f8a-5d90-4fa1-a068-7dd7c3993917
CommentID: 83a32ebd-89c4-40a5-bbd8-ace6bf723c57
CommentID: 6003d965-f6cd-4e60-8b12-f9494fcb9bc0
CommentID: a932c934-5dfd-4a5f-90da-5a40da468e78
CommentID: 08ce995b-2ee1-4db9-8e3c-ca5069f87cce
CommentID: ce5b57d5-fc02-4c91-90d5-bbe211073e0b
CommentID: 5314a796-5574-4282-aab5-d8a8fc6e7ade
CommentID: 3018252d-4f9b-40e7-bbbd-e0f3ab8b753d
CommentID: 730bc8f5-d5fc-4d29-adf8-23c2fb632c0b
更多CommentID......
最新20条评论:
CommentID 0 d194a83c-005a-411e-ba36-2b513c3565c1
CommentID 1 c8104907-8912-463a-9d05-7fe0385d13d9
CommentID 2 88b918ac-bf35-4687-a06c-af5e6159a376
CommentID 3 324ff8c1-dfa5-46b3-9463-8a2d6aba3d26
CommentID 4 6b3b76b0-3ce6-4dd4-9ed9-b40618330a44
CommentID 5 7561efe9-96e0-46df-8f7c-e5f6812246c1
CommentID 6 937122ca-a203-4ae9-89d6-454dbf616e1e
CommentID 7 8e0f24fe-152a-4297-afbd-703c51fee50e
CommentID 8 328f4858-6adc-41c7-88c1-a896c5b122a5
CommentID 9 50151b7e-1225-4093-a30b-9d370b44ea25
CommentID 10 dd1bd655-760e-41af-8929-9986dce9a41b
CommentID 11 358fa7d3-4291-4c1b-8b2e-dbee55c60084
CommentID 12 7367b111-9144-428b-af32-685004318c97
CommentID 13 5e06f20a-a5b0-4e85-98c4-9be4ec613d70
CommentID 14 924ac607-8af2-47b7-a08a-37ee851a1693
CommentID 15 add23360-f2d9-4049-b935-97a8823176e4
CommentID 16 15aca269-e4dd-4f2f-9eaa-cf8dc19cc8f3
CommentID 17 45c6f96b-4a8a-4b57-a1f4-f10621911d67
CommentID 18 c0a4fcd1-8df5-4f44-9a04-ba23ce8c8168
CommentID 19 3e06fc46-766a-4e78-a67b-276840f72f62

存储社交关系

譬如将用戶的好友/粉丝/关注,可以存在一个set中,这样求两个人的共同好友的操作,只需要用sinter交集命令即可;也可以进行并集,差集操作。

import java.util.Set;
import redis.clients.jedis.Jedis;
public class CommonFriends {
    public static void main(String[] args) {
        String host = "127.0.0.1";
        int port = 10011;
        Jedis jedis = new Jedis(host, port);
        //my friends
        jedis.sadd("myfriends", "John");
        jedis.sadd("myfriends", "Emliy");
        jedis.sadd("myfriends", "Ben");
        jedis.sadd("myfriends", "Steven");
        System.out.println("my friends are: ");
        Set<String> myList = jedis.smembers("myfriends");
        for (String item:myList) {
            System.out.print(item+" ");
        }
        //your friends
        jedis.sadd("yourfriends", "Mark");
        jedis.sadd("yourfriends", "Tim");
        jedis.sadd("yourfriends", "Willim");
        jedis.sadd("yourfriends", "Ben");
        jedis.sadd("yourfriends", "Steven");
        System.out.println("\n");
        System.out.println("your friends are: ");
        Set<String> yourList = jedis.smembers("yourfriends");
        for (String item:yourList) {
            System.out.print(item+" ");
        }
        //our common friends
        System.out.println("\n");
        System.out.println("our common friends are: ");
        Set<String> commonList = jedis.sinter("myfriends","yourfriends");
        for (String item:commonList) {
            System.out.print(item+" ");
        }
        jedis.close();
    }
}

输出:

my friends are:
Steven Emliy John Ben
your friends are:
Steven Mark Tim Ben Willim
our common friends are:
Steven Ben

构建实时消息系统

Redis的Pub/Sub系统可以构建实时的消息系统,比如很多开发人员用Pub/Sub构建实时聊天系统。

import redis.clients.jedis.*;
import java.util.Date;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.lang3.RandomStringUtils;
class PrintListener extends JedisPubSub{
    @Override
    public void onMessage(String channel, String message) {
        String time = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");
        System.out.println("message receive:" + message + ",channel:" + channel +
        "..." + time);
        //此处我们可以取消订阅
        if(message.equalsIgnoreCase("quit")){
            this.unsubscribe(channel);
        }
    }
}
class PubClient {
    private Jedis jedis;
    public PubClient(String host,int port){
        jedis = new Jedis(host,port);
    }
    public void pub(String channel,String message){
        jedis.publish(channel, message);
    }
    public void close(String channel){
        jedis.publish(channel, "quit");
        jedis.del(channel);//实时消息系统
    }
}
class SubClient {
    private Jedis jedis;//
    public SubClient(String host,int port){
        jedis = new Jedis(host,port);
    }
    public void sub(JedisPubSub listener,String channel){
        jedis.subscribe(listener, channel);
        //此处将会阻塞,在client代码级别为JedisPubSub在处理消息时,将会“独占”链接
        //并且采取了while循环的⽅方式,侦听订阅的消息
    }
}
public class PubSubTest {
    /**
    * @param args
    */
    static String host = "127.0.0.1";
    static int port = 10011;
    public static void main(String[] args) throws Exception{
    PubClient pubClient = new PubClient(host,port);
    final String channel = "pubsub-channel";
    pubClient.pub(channel, "before1");
    pubClient.pub(channel, "before2");
    Thread.sleep(2000);
    //消息订阅者⾮非常特殊,需要独占链接,因此我们需要为它创建新的链接;
    //此外,jedis客户端的实现也保证了“链接独占”的特性,sub⽅方法将⼀一直阻塞,
    //直到调⽤用listener.unsubscribe⽅方法
    Thread subThread = new Thread(new Runnable() {
        @Override
        public void run() {
            try{
                SubClient subClient = new SubClient(host,port);
                System.out.println("----------subscribe operation begin-------");
                JedisPubSub listener = new PrintListener();
                //在API级别,此处为轮询操作,直到unsubscribe调⽤用,才会返回
                subClient.sub(listener, channel);
                System.out.println("----------subscribe operation end-------")
                ;
            }catch(Exception e){
                e.printStackTrace();
            }
        }
    });
    subThread.start();
    int i=0;
    while(i < 10){
        String message = RandomStringUtils.random(6, true, true);//apache-commons
        pubClient.pub(channel, message);
        i++;
        Thread.sleep(1000);
    }
    //被动关闭指示,如果通道中,消息发布者确定通道需要关闭,那么就发送一个“quit”
    //那么在listener.onMessage()中接收到“quit”时,其他订阅client将执行“unsubscribe”操作。
    pubClient.close(channel);
    //此外,你还可以这样取消订阅
    //listener.unsubscribe(channel);
    }
}

输出:

----------subscribe operation begin-------
message receive:erRIEe,channel:pubsub-channel...2016-03-15 15:53:52
message receive:Ovcwiw,channel:pubsub-channel...2016-03-15 15:53:53
message receive:STPWfV,channel:pubsub-channel...2016-03-15 15:53:54
message receive:SR4iIk,channel:pubsub-channel...2016-03-15 15:53:55
message receive:GI3Ege,channel:pubsub-channel...2016-03-15 15:53:56
message receive:0V1JUt,channel:pubsub-channel...2016-03-15 15:53:57
message receive:3iU8BV,channel:pubsub-channel...2016-03-15 15:53:58
message receive:BqeI2x,channel:pubsub-channel...2016-03-15 15:53:59
message receive:D53cHF,channel:pubsub-channel...2016-03-15 15:54:00
message receive:quit,channel:pubsub-channel...2016-03-15 15:54:01
----------subscribe operation end-------

事务处理

用户可以使用MULTI,EXEC,DISCARD,WATCH,UNWATCH指令用来执行原子性的事务操作。

import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;
import java.util.List;
public class JTransaction {
    public static void main(String[] args) {
        String host = "127.0.0.1";
        int port = 10011;
        Jedis jedis1 = new Jedis(host, port);
        Jedis jedis2 = new Jedis(host, port);
        String key = "transaction-key";
        jedis1.set(key, "20");
        //jedis1 watch key
        jedis1.watch(key);//如果在执⾏行事务之前,其他的客户端改变了key,则事务执⾏行失败。
        Transaction tx = jedis1.multi();//开始事务
        tx.get(key);
        tx.get(key);
        tx.get(key);
        //jedis2.incr(key);//如果jedis2改变key,那么jedis1的事务就会失败
        List<Object> result = tx.exec();//执⾏行事务
        if(result == null || result.isEmpty()){
            System.out.println("Transaction error...");
            return;
        }
        for(Object rt : result){
            System.out.println(rt.toString());
        }
    }
}

输出:

20
20
20

基数统计功能

Redis HyperLogLog 实现了基数统计功能,方便统计一组不同元素且数量很大的数据集,且只耗费很小的空间。如统计网站每天访问的独立IP数量;使用PFADD和PFCOUNT,可以轻松实现。

import redis.clients.jedis.Jedis;
import java.util.Random;
public class HyperLog {
    public static void main(String[] args) {
        String host = "127.0.0.1";
        int port = 10011;
        Jedis jedis = new Jedis(host, port);
        String key = "src_ip";
        jedis.del(key);
        //随机生成10000个 ip,代表访问"www.ucloud.cn"的源ip.
        for (int i = 0; i < 10000; i++) {
            int section1 = new Random().nextInt(255);
            int section2 = new Random().nextInt(253);
            String ip = "10.10." + Integer.toString(section1) + "." + Integer.toSt
            ring(section2);
            //System.out.println(ip);
            jedis.pfadd(key, ip);127.0.0.1
        }
    System.out.println("今天访问www.ucloud.cn的独立ip数为:"+jedis.pfcount(key));
    jedis.close();
    }
}

输出:

今天访问www.ucloud.cn的独立ip数为:9218

参考
        https://docs.ucloud.cn/database/uredis/situation
http://www.runoob.com/redis/redis-pub-sub.html 

你可能感兴趣的:(NOSQL)