Redis是一个速度极快的非关系型数据库(NoSQL),他可以存储key和5种不同类型的value之间的映射。能够将内存中的键值数据持久化到硬盘当中。
redis与集中常用存储的比较 我们知道了Redis是基于内存的数据库,那么当服务器关闭的时候,我们内存中的的数据将何去何从呢?
Redis提供了两种不同形式的数据持久化的方式:
时间点转储(point-in-time dump)转储命令(dump-to-disk)第一种方式即在指定时间段内有指定数量的写操作执行,我们通过设置时间段和操作数来控制其自动转储。第二种方式下我们可以通过两条转储命令的任意一条来执行,它通过将所有修改了数据库的命令记录到一个只可追加的文件中,我们根据情况将只追加写入设置为从不同步、每秒同步一次或者每条命令同步一次。
同时,Redis实现了主从服务器的架构来进行拓展与故障转移。接收复制的从服务器可连接上主服务器,接收主服务器发送的整个服务器的初始副本,在后续主服务器新增写命令时,从服务器都会获取到并执行,从而实现从服务器数据的实时更新,使我们访问任意一台从服务器时都能够接受到最新数据。
下面我们将逐一介绍各个结构
Redis中的字符串在其他的编程语言中String也是常用的数据类型,在这里String拥有一些公共的操作方法如:get、set、del等。 这里以key为hello,值为world的字符串示例,首先启动redis服务端与客户端如下: 上面图中,我们使用redis-cli直接与服务器进行简单的交互,设置了hello world字符串,并进行删除,删除成功的话服务器返回一个整数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>上述示范了对列表的一些基本操作。
Redis的集合集合与列表类似,但集合通过散列表来控制其不能够存储值相同的元素,它提供了一下一些方法:
sadd 将指定元素添加到集合smembers 以序列形式返回集合包含的所有元素sismembers 检查某个元素是否存在与集合中,返回0或1srem 若元素存在于集合中,则移除该元素 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进行的一些基本操作示例。
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。
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实现购物车数据行缓存网页分析等等…1. 字符串 在Redis中,字符串包括字节串、整数、浮点数。 首先介绍一下Redis中字符串的自增自减命令:
INCR 自增1DECR 自减1INCRBY 根据给定数据自增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记录日志 记录当前日志,保留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地址所属城市与国家 服务的发现与配置