《Redis实战》学习笔记

《Redis实战》学习笔记

初识Redis

Redis是一个速度极快的非关系型数据库(NoSQL),他可以存储key和5种不同类型的value之间的映射。能够将内存中的键值数据持久化到硬盘当中。

redis与集中常用存储的比较
在这里插入图片描述
我们知道了Redis是基于内存的数据库,那么当服务器关闭的时候,我们内存中的的数据将何去何从呢?

Redis提供了两种不同形式的数据持久化的方式:

  • 时间点转储(point-in-time dump)
  • 转储命令(dump-to-disk)

第一种方式即在指定时间段内有指定数量的写操作执行,我们通过设置时间段和操作数来控制其自动转储。第二种方式下我们可以通过两条转储命令的任意一条来执行,它通过将所有修改了数据库的命令记录到一个只可追加的文件中,我们根据情况将只追加写入设置为从不同步、每秒同步一次或者每条命令同步一次。

同时,Redis实现了主从服务器的架构来进行拓展与故障转移。接收复制的从服务器可连接上主服务器,接收主服务器发送的整个服务器的初始副本,在后续主服务器新增写命令时,从服务器都会获取到并执行,从而实现从服务器数据的实时更新,使我们访问任意一台从服务器时都能够接受到最新数据。

Redis的数据结构

  • String 可以是字符串、整数或浮点数
  • List 链表,可从链表两端推入或弹出元素
  • Set 无序集合
  • Hash 包含键值对的无序散列表
  • ZSet 有序集合,按照score来分布

下面我们将逐一介绍各个结构

  1. Redis中的字符串

在其他的编程语言中String也是常用的数据类型,在这里String拥有一些公共的操作方法如:get、set、del等。
这里以key为hello,值为world的字符串示例,首先启动redis服务端与客户端如下:
在这里插入图片描述
上面图中,我们使用redis-cli直接与服务器进行简单的交互,设置了hello world字符串,并进行删除,删除成功的话服务器返回一个整数1。

  1. 列表

Redis中操作列表有以下几种方法:

  • rpush&&lpush 从列表的左端或者右端推入数据元素
  • lpop&&rpop 从列表的左端或右端弹出元素
  • lindex 获取列表指定位置的元素
  • lrange 获取列表指定范围的一组元素
127.0.0.1:6379> rpush listkey item1
(integer) 1
127.0.0.1:6379> rpush listkey item1
(integer) 2
127.0.0.1:6379> rpush listkey item2
(integer) 3
127.0.0.1:6379> LRANGE listkey 0
(error) ERR wrong number of arguments for 'lrange' command
127.0.0.1:6379> LRANGE listkey 0 -1
1) "item1"
2) "item1"
3) "item2"
127.0.0.1:6379> LRANGE listkey 0 -2
1) "item1"
2) "item1"
127.0.0.1:6379> LRANGE listkey 0 -3
1) "item1"
127.0.0.1:6379> LRANGE listkey -3
(error) ERR wrong number of arguments for 'lrange' command
127.0.0.1:6379> LRANGE listkey 0 -1
1) "item1"
2) "item1"
3) "item2"
127.0.0.1:6379> lpop listkey
"item1"
127.0.0.1:6379> LRANGE listkey 0 -1
1) "item1"
2) "item2"
127.0.0.1:6379> LINDEX listkey 0
"item1"
127.0.0.1:6379> LINDEX listkey 1
"item2"
127.0.0.1:6379> LINDEX listkey 2
(nil)
127.0.0.1:6379> 

上述示范了对列表的一些基本操作。

  1. Redis的集合

集合与列表类似,但集合通过散列表来控制其不能够存储值相同的元素,它提供了一下一些方法:

  • sadd 将指定元素添加到集合
  • smembers 以序列形式返回集合包含的所有元素
  • sismembers 检查某个元素是否存在与集合中,返回0或1
  • srem 若元素存在于集合中,则移除该元素
127.0.0.1:6379> sadd setkey item1
(integer) 1
127.0.0.1:6379> sadd setkey item2
(integer) 1
127.0.0.1:6379> sadd setkey item3
(integer) 1
127.0.0.1:6379> sadd setkey item3
(integer) 0
127.0.0.1:6379> SMEMBERS setkey
1) "item3"
2) "item1"
3) "item2"
127.0.0.1:6379> SISMEMBER item1
(error) ERR wrong number of arguments for 'sismember' command
127.0.0.1:6379> SISMEMBER setkey item1
(integer) 1
127.0.0.1:6379> SISMEMBER setkey item4
(integer) 0
127.0.0.1:6379> SREM setkey item1
(integer) 1
127.0.0.1:6379> SMEMBERS setkey
1) "item3"
2) "item2"
127.0.0.1:6379> 

以上为对setkey进行的一些基本操作示例。

  1. Redis的散列

散列表用来存储键值对,这里提供了几种操作该数据类型的方法:

  • HSet 在散列里面关联起给定的键值对
  • HGet 获取指定散列键的值
  • HGETALL 获取散列表中所有键值对
  • HDEL 删除键
127.0.0.1:6379> hset hashkey key1 value1
(integer) 1
127.0.0.1:6379> hset hashkey key1 value2
(integer) 0
127.0.0.1:6379> hset hashkey key2 value2
(integer) 1
127.0.0.1:6379> hset hashkey key3 value2
(integer) 1
127.0.0.1:6379> HGET hashkey key1
"value2"
127.0.0.1:6379> HGET hashkey key2
"value2"
127.0.0.1:6379> HGET hashkey key3
"value2"
127.0.0.1:6379> HGETALL hashkey 
1) "key1"
2) "value2"
3) "key2"
4) "value2"
5) "key3"
6) "value2"
127.0.0.1:6379> HDEL hashkey key1
(integer) 1
127.0.0.1:6379> HGETALL hashkey 
1) "key2"
2) "value2"
3) "key3"
4) "value2"
127.0.0.1:6379> 

以上为对hash数据结构的一些基本操作,注意,当重复设置同一key的不同value时,后设置的value会覆盖前面设置的value,并返回0。

  1. Redis的有序集合

有序集合和散列类似,都用键值对进行存储。在zset中,集合的键被称为member,值被称为score。每个成员都是不同的,分值也必须是浮点数,在zset中,可根据score来对成员元素进行排序。

它也提供了一些基本的操作命令:

  • zadd 将一个带有给定分值的成员添加到集合中
  • zrange 根据元素在有序排列中所给的位置,从有序集合中获取多个元素
  • zrangebyscore 获取有序集合在给定分值范围中的元素
  • zrem 若集合中存在该成员则删除
127.0.0.1:6379> ZADD zsetkey 666 mem1
(integer) 1
127.0.0.1:6379> ZADD zsetkey 667 mem2
(integer) 1
127.0.0.1:6379> ZADD zsetkey 666 mem1
(integer) 0
127.0.0.1:6379> ZADD zsetkey 668 mem1
(integer) 0
127.0.0.1:6379> ZRANGE zset 0 -1
(empty list or set)
127.0.0.1:6379> ZRANGE zsetkey 0 -1
1) "mem2"
2) "mem1"
127.0.0.1:6379> zadd zsetkey 669 mem3
(integer) 1
127.0.0.1:6379> ZRANGE zsetkey 0 -1
1) "mem2"
2) "mem1"
3) "mem3"
127.0.0.1:6379> ZRANGE zsetkey 0 -1 withscore
(error) ERR syntax error
127.0.0.1:6379> ZRANGE zsetkey 0 -1 withscores
1) "mem2"
2) "667"
3) "mem1"
4) "668"
5) "mem3"
6) "669"
127.0.0.1:6379> ZRANGEBYSCORE zsetkey 0 600 withscores
(empty list or set)
127.0.0.1:6379> ZRANGEBYSCORE zsetkey 0 900 withscores
1) "mem2"
2) "667"
3) "mem1"
4) "668"
5) "mem3"
6) "669"
127.0.0.1:6379> ZREM zsetkey mem2
(integer) 1
127.0.0.1:6379> ZRANGE zsetkey 0 -1 withscores
1) "mem1"
2) "668"
3) "mem3"
4) "669"
127.0.0.1:6379> 

以上为操作zset的一些简单案例。通过zadd可修改同一member的分值,此时返回0。

使用Redis解决的实际问题

  • 使用Redis进行登录和cookies缓存
  • 使用Redis实现购物车
  • 数据行缓存
  • 网页分析
  • 等等…

Redis中的命令

1. 字符串
在Redis中,字符串包括字节串、整数、浮点数。
首先介绍一下Redis中字符串的自增自减命令:

  • INCR 自增1
  • DECR 自减1
  • INCRBY 根据给定数据自增
  • DECRBY 根据给定数据自减
  • INCRBYFLOAT 根据给定浮点数自增

示例如下:

127.0.0.1:6379> INCR Integer 
(integer) 7
127.0.0.1:6379> DECR Integer
(integer) 6
127.0.0.1:6379> get Integet
(nil)
127.0.0.1:6379> get Integer
"6"
127.0.0.1:6379> INCRBY Integer 3
(integer) 9
127.0.0.1:6379> DECRBY Integer 3
(integer) 6
127.0.0.1:6379> INCRBYFLOAT Integet 3.3
"3.3"
127.0.0.1:6379> INCRBYFLOAT Integer 3.3
"9.3"
127.0.0.1:6379> 

这里要注意的是,我们可以对一个不存在的键值进行以上操作,redis会将该键的值默认为0,如上的Integet,我们原先没有设置,但却可以直接进行INCRBYFLOAT 操作。

处理字符串子串命令

  • APPEND 在字符串末尾追加字符串
  • GETRANGE 获取字符串子串,由 start、end参数 获取子串范围
  • SETRANGE 从offset参数开始,更新字符串值
  • GETBIT 将字符串当做二进制串,并获取偏移量为offset参数的值
  • SETBIT 将字符串当做二进制串,并设置偏移量为offset参数的值
  • BITCOUNT 获取二进制串里值为1的二进制位数
127.0.0.1:6379> set string "strTest"
OK
127.0.0.1:6379> get string
"strTest"
127.0.0.1:6379> APPEND string one
(integer) 10
127.0.0.1:6379> get string
"strTestone"
127.0.0.1:6379> APPEND string Two
(integer) 13
127.0.0.1:6379> get string
"strTestoneTwo"
127.0.0.1:6379> GETRANGE string 0 3
"strT"
127.0.0.1:6379> get string
"strTestoneTwo"
127.0.0.1:6379> SETRANGE string 7 Three
(integer) 13
127.0.0.1:6379> get string
"strTestThreeo"
127.0.0.1:6379> GETBIT string 3
(integer) 1
127.0.0.1:6379> GETBIT string 4
(integer) 0
127.0.0.1:6379> GETBIT string 5
(integer) 0
127.0.0.1:6379> GETBIT string 2
(integer) 1
127.0.0.1:6379> SETBIT string 2 0
(integer) 1
127.0.0.1:6379> get string
"StrTestThreeo"
127.0.0.1:6379> BITCOUNT string
(integer) 52
127.0.0.1:6379> 

以上为上述几个命令演示。

2. 列表操作命令
在前面我们讲过了一些关于列表操作的基本命令,这里对列表这些命令做更详细的介绍。

  • LTRIM 可根据start、end参数,决定取留列表的某一部分,如:
127.0.0.1:6379> LRANGE list-key 0 -1
1) "first"
2) "last"
3) "new last"
127.0.0.1:6379> LTRIM list-key 1 -1
OK
127.0.0.1:6379> LRANGE list-key 0 -1
1) "last"
2) "new last"
127.0.0.1:6379> 

阻塞式列表的弹出与列表元素转移
在这里插入图片描述
如上以B开头的命令,使其阻塞一段时间后等到条件成立时或时间到时结束程序。
测试如下:

127.0.0.1:6379> LRANGE list-key2 0 -1
1) "first2"
2) "last2"
127.0.0.1:6379> BLPOP list-key2 3
1) "list-key2"
2) "first2"
127.0.0.1:6379> LRANGE list-key2 0 -1
1) "last2"
127.0.0.1:6379> BLPOP list-key2 first2 3
1) "list-key2"
2) "last2"
127.0.0.1:6379> LRANGE list-key2 0 -1
(empty list or set)
127.0.0.1:6379> BLPOP list-key2 first2 3
(nil)
(3.10s)
127.0.0.1:6379> BLPOP list-key2 first2 20
(nil)   
(20.10s)
127.0.0.1:6379> BLPOP list-key2 first2 10
1) "list-key2"
2) "first2"
(2.76s)
127.0.0.1:6379> LRANGE list-key2 0 -1
(empty list or set)
127.0.0.1:6379> LPUSH first2
(error) ERR wrong number of arguments for 'lpush' command
127.0.0.1:6379> LPUSH list-key2 first2
(integer) 1
127.0.0.1:6379> RPOPLPUSH list-key2 list-key
"first2"
127.0.0.1:6379> LRANGE list-key 0 -1
1) "first2"
2) "last"
3) "new last"
127.0.0.1:6379> LRANGE list-key2 0 -1
(empty list or set)
127.0.0.1:6379> BRPOPLPUSH list-key2 list-key 10
"first2"
(1.98s)
127.0.0.1:6379> 

以上在阻塞过程中,我们使用另一个redis客户端对对应数据结构添加值,使其有元素可操作。

集合的命令操作

丢失…

使用Redis构建支持程序

使用Redis记录日志
记录当前日志,保留100条:

package redis;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.Pipeline;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;

/**
 * @Description TODO
 * @Author zhenxing.dong
 * @Date 2019/12/5 21:22
 */
public class Chacter05 {

    public static final String DEBUG = "debug";
    public static final String INFO = "info";
    public static final String WARNING = "warning";
    public static final String ERROR = "error";
    public static final String CRITICAL = "critical";

    public static final SimpleDateFormat TIMESTAMP =
            new SimpleDateFormat("EEE MMM dd HH:00:00 yyyy");
    private static final SimpleDateFormat ISO_FORMAT =
            new SimpleDateFormat("yyyy-MM-dd'T'HH:00:00");
    static{
        ISO_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
    }

    public static void main(String args[]){
        new Chacter05().run();

    }


    public void run(){
        Jedis conn = new Jedis("localhost");

        conn.select(15);

//        conn.del("recent:test:info");

        testLogRecent(conn);
//        conn.lrange("recent:test:info",0,-1);

    }
    public void testLogRecent(Jedis conn) {
        System.out.println("\n----- testLogRecent -----");
        System.out.println("Let's write a few logs to the recent log");
        for (int i = 200; i < 250; i++) {
            logRecent(conn, "test", "this is message " + i);
        }
        List<String> recent = conn.lrange("recent:test:info", 0, -1);
        System.out.println(
                "The current recent message log has this many messages: " +
                        recent.size());
        System.out.println("Those messages include:");
        for (String message : recent){
            System.out.println(message);
        }
        assert recent.size() >= 5;
    }

    public void logRecent(Jedis conn, String name, String message) {
        logRecent(conn, name, message, INFO);
    }

    public void logRecent(Jedis conn, String name, String message, String severity) {
        String destination = "recent:" + name + ':' + severity;
        Pipeline pipe = conn.pipelined();
        pipe.lpush(destination, TIMESTAMP.format(new Date()) + ' ' + message);
        pipe.ltrim(destination, 0, 99);
        pipe.sync();
    }


}

如上述日志的函数logRecent()可见,我们可以通过设置一个list数据结构,将日志消息message推入list中,trim保留100条。

记录常用日志
通过zset有序集合,将日志信息出现的次数作为score,信息作为member存在zset中,可按序取出排列:

 public void testLogCommon(Jedis conn) {
        System.out.println("\n----- testLogCommon -----");
        System.out.println("Let's write some items to the common log");
        for (int count = 1; count < 7; count++) {
            for (int i = 0; i < count; i++) {
                logCommon(conn, "test", "message-" + count);
            }
        }
        Set<Tuple> common = conn.zrevrangeWithScores("common:test:info", 0, -1);
        System.out.println("The current number of common messages is: " + common.size());
        System.out.println("Those common messages are:");
        for (Tuple tuple : common) {
            System.out.println("  " + tuple.getElement() + ", " + tuple.getScore());
        }
        assert common.size() >= 7;
    }
 public void logCommon(Jedis conn, String name, String message) {
        logCommon(conn, name, message, INFO, 5000);
    }

    public void logCommon(
            Jedis conn, String name, String message, String severity, int timeout) {
        String commonDest = "common:" + name + ':' + severity;
        String startKey = commonDest + ":start";
        long end = System.currentTimeMillis() + timeout;
        while (System.currentTimeMillis() < end) {
            conn.watch(startKey);
            String hourStart = ISO_FORMAT.format(new Date());
            String existing = conn.get(startKey);

            Transaction trans = conn.multi();
            if (existing != null && COLLATOR.compare(existing, hourStart) < 0) {
                trans.rename(commonDest, commonDest + ":last");
                trans.rename(startKey, commonDest + ":pstart");
                trans.set(startKey, hourStart);
            }

            trans.zincrby(commonDest, 1, message);

            String recentDest = "recent:" + name + ':' + severity;
            trans.lpush(recentDest, TIMESTAMP.format(new Date()) + ' ' + message);
            trans.ltrim(recentDest, 0, 99);
            List<Object> results = trans.exec();
            // null response indicates that the transaction was aborted due to
            // the watched key changing.
            if (results == null) {
                continue;
            }
            return;
        }
    }

Redis实现计数器并进行数据统计

查询IP地址所属城市与国家
服务的发现与配置

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值