Redis实战总结-简单介绍和常用数据结构及命令

时间:2022-08-06 05:20:54

* 序言

最近花了一些时间将《Redis实战》,和网上关于Redis的一些博客研究了下。针对这段时间的学习做一个总结,内容如下:

  1. Redis介绍
  2. Redis的数据结构及命令
  3. Redis的管道及PUB/SUB机制
  4. Redis配置、复制及持久化
  5. Redis高可用性
  6. Redis内存优化及Lua脚本编程

其中第一篇博客介绍1、2两部分,第二篇博客介绍3部分,第三篇博客介绍4、5两部分,第四篇博客介绍6部分。

1. Redis介绍

Redis是一个开源(遵守BSD协议),基于内存的key-value高性能存储系统,可以用作数据库、速缓存和消息队列代理。它支持字符串(STRING)、哈希表(HASH)、列表(LIST)、集合(SET)、有序集合(ZSET)等数据结构。

Redis 相对于其它 key - value 缓存产品,比如memorycache(分布式缓存),Guavacache(本地缓存)有下面三大特点:

  • Redis支持数据持久化,可以将内存中的数据存储在磁盘中,重启的时候能够还原Redis环境。
  • Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储。
  • Redis支持数据备份,即master-slave(主从)模式的数据备份(不支持主主复制)。

2. Redis的数据结构

Redis可以存储键与5种不同数据结构类型之间的映射,这五种数据结构类型为:STRING,HASH,LIST,SET,ZSET。每种数据结构都有自身的操作命令,当然也有一部分操作命令是对这些数据结构都通用的,比如DEL,RENAME等。

String

在Redis里面,字符串可以存储以下三种类型:

  • 字符串
  • 整数
  • 浮点数

Redis常见字符串命令:

命令 用例及描述
GET GET key——获取给定键的值
SET SET key value——设置给定键的值
DEL DEL key——删除给定的键(适用于所有类型)
INCR INCR key——将键存储的值加上1
DECR INCR key——将键存储的值减去1
INCRBY INCRBY key amount——将键存储的值加上整数amount
DECRBY DECRBY key amount——将键存储的值减去整数amount

HASH

和字符串一样,散列存储的值既可以是字符串又可以是数字值,同样对于数字值可以进行自增或自减。
Redis中常见散列命令:

命令 用例及描述
HSET HSET key field value——在散列里关联起给定的键值对
HGET HGET key field——获取指定散列键的值
HGETALL HGETALL key——获取散列包含的所有键值对
HDEL HDEL key field [field …]——删除散列包含的键
HMSET HMSET key field value [field value …]——同HSET,可以批量操作
HMGET HMGET key field [field …]——同HGET,可以批量操作
HLEN HLEN key——返回散列中包含的键值对数量
HEXISTS HEXISTS key field——判断散列中是否存在键
HKEYS HKEYS key——获取散列中包含的所有键
HVALS HVALS key——获取散列中包含的所有值

LIST

Redis列表(链式存储结构)允许用户从列表额两端推入或者弹出元素,以获取元素,以及执行各种常见的列表操作。
Redis中常见列表命令:

命令 用例及描述
RPUSH RPUSH key value [value …]——将一个值或多个值推入到列表的右端
LPUSH LPUSH key value [value …]——将一个值或多个值推入到列表的右端
LINDEX LINDEX key index——返回列表偏移量为index的元素
LRANGE LRANGE key start stop——返回列表从start偏移量到stop偏移量的所有元素(包括start和stop处的元素)
LPOP LPOP key——移除并返回列表最左边元素
RPOP RPOP key——移除并返回列表最又边元素
LTRIM LTRIM key start stop——对列表裁剪,只保留从start偏移量到stop偏移量的所有元素(包括start和stop处的元素)

Redis列表还存在一些阻塞式弹出推入命令,可以应用在消息传递和任务队列等场景中。

SET

Redis的集合以无序的方式来存储多个各不相同的元素,用户可以很方便的添加元素,移除元素以及判断元素是否存在与集合中。
Redis中常见集合命令:

命令 用例及描述
SADD SADD key member [member …]——将一个或多个元素添加到集合中
SREM SREM key member [member …]——从集合中移除一个或多个元素
SISMEMBER SISMEMBER key member——检查元素member是否存在于集合中
SCARD SCARD key——统计集合中元素数量
SMEMBERS SMEMBERS key——返回集合中包含的所有元素
SMOVE SMOVE source destination member——将集合source中的元素member移动到集合destination中,移植成功返回1

ZSET

有序集合和散列存储着键与值之间映射类似,也存在成员和分值(正常以时间或者值得大小作为分值)的映射,并且提供了分值的处理命令。
Redis中常见有序集合命令:

命令 用例及描述
ZADD ZADD key score member [score member …]——将带有给定分值的成员添加到有序结合中
ZREM ZREM key member [member …]——从有序集合中移除给定的元素
ZCARD ZCARD key——返回有序集合中包含的成员数量
ZINCRBY ZINCRBY key increment member——将member的分值加1
ZCOUNT ZCOUNT key min max——返回分值介于min~max之间的成员变量
ZRANK ZRANK key member——返回member的分值

当然有序集合还有许多比较有用的命令,就不一一列举了。

SORT命令

SORT命令可以实现按照给定的选项,对输入的列表,集合,有序集合,散列进行排序,返回排序后的结果。

命令 用例及描述
SORT sort key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern …]] [ASC

3.Redis其他命令

Redis除了上述数据结构操作命令外,还有发布订阅命令,实现基本事务特性的MULTI命令和EXEC命令,自动过期EXPIRE命令。下面介绍这些命令,其中发布订阅命令发在下篇博客中介绍。

MULTI和EXEC命令

在Redis中,有时可能需要同时处理多个命令,即需要将这些命令一个事物,一次性提交给Redis。Redis为实现这种机制,引进了multi和exec命令。首先需要先执行multi命令,然后在输入我们需要执行的命令,最后在执行exec命令。
Redis实战总结-简单介绍和常用数据结构及命令

EXPIRE命令

在使用Redis存储数据时,有些数据存储一段时间后就不会再使用了,比如不同系统之间的会话密钥(保证两个小时后自动失效),因而Redis引入了一种“带有生存时间”特殊键。如果为一个键设置了生存时间,则到了生存时间,Redis会自动的(应用程序透明,由Redis线程自己负责)将改建删除。
常见的几个命令:

命令 用例及描述
PERSIST PERSIST key——移除键的过期时间
TTL TTL key——查看给定键距离过期还有多少秒
EXPIRE EXPIRE key seconds——设置键的生存时间,单位为秒

4. 在JAVA中实现Redis连接池

在Java中使用Jedis.jar操作Redis,下面给出demo演示在Java中如何构建Redis缓存连接池,并给出散列(MAP)相关命令的代码实现。

新建Maven工程,在pom.xml中添加依赖(maven和log4j)

        <dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.7.3</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>

配置log4j,log4j.properties

#config root logger
log4j.rootLogger = DEBUG,system.out
log4j.appender.system.out=org.apache.log4j.ConsoleAppender
log4j.appender.system.out.layout=org.apache.log4j.PatternLayout
log4j.appender.system.out.layout.ConversionPattern=MINAServer Logger-->%5p{%F:%L}-%m%n

#config this Project.file logger
log4j.logger.thisProject.file=INFO,thisProject.file.out
log4j.appender.thisProject.file.out=org.apache.log4j.DailyRollingFileAppender
log4j.appender.thisProject.file.out.File=logContentFile.log
log4j.appender.thisProject.file.out.layout=org.apache.log4j.PatternLayout

JedisUtil类:

package util;

import org.apache.log4j.Logger;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

import java.util.Map;
import java.util.Set;

/**
* 搭建Redis缓存连接池
* @author guweiyu
*/

public class JedisUtil {

private static Logger logger = Logger.getLogger(JedisUtil.class.getName());

// 创建JedisPoll实例
private static JedisPool jedisPool;

// 设置Jedis连接相关参数
// Redis服务器IP,端口号
private static String REDIS_ADDRESS = "127.0.0.1";
private static Integer REDIS_PORT = 6379;

// 最大连接数, 默认8个
private static Integer MAX_TOTAL = 8;
// 最大空闲连接数, 默认8个
private static Integer MAX_IDLE = 8;
// 获取连接时的最大等待毫秒数(如果设置为阻塞时BlockWhenExhausted),如果超时就抛异常, 小于零:阻塞不确定的时间, 默认-1)
private static Integer MAX_WAIT_MILLIS = 10000;
// Re
private static Integer TIMEOUT = 10000;


/**
* 初始化Redis缓存连接池
*/

public static void init() {
try {
// 构建Redis配置
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(MAX_TOTAL);
config.setMaxIdle(MAX_IDLE);
config.setMaxWaitMillis(MAX_WAIT_MILLIS);

// 创建Redis缓存连接池子
jedisPool = new JedisPool(config, REDIS_ADDRESS, REDIS_PORT, TIMEOUT);
logger.info("Redis连接池初始化成功");
} catch (Exception e) {
logger.error("Redis连接池初始化失败", e);
e.printStackTrace();
}
}

/**
* 关闭redis连接实例
* @param jedis
*/

private static void safeReturn(Jedis jedis) {
if (jedis != null) {
jedis.close();
}
}

/**
* 判断散列中是否存在键
* @param key
* @param field
* @return
*/

public static boolean hexist(String key, String field) {
Jedis jedis = jedisPool.getResource();
try {
return jedis.hexists(key, field);
} finally {
safeReturn(jedis);
}
}

/**
* 在散列里批量关联起给定的键值对,并设置超时时间,单位秒
* @param key
* @param hash
* @param timeout
*/

public static void setHash(String key, Map<String, String> hash, int timeout) {
Jedis jedis = jedisPool.getResource();
try {
jedis.hmset(key, hash);
jedis.expire(key, timeout);
} finally {
safeReturn(jedis);
}
}

/**
* 在散列里批量关联起给定的键值对
* @param key
* @param hash
*/

public static void setHash(String key, Map<String, String> hash) {
Jedis jedis = jedisPool.getResource();
try {
jedis.hmset(key, hash);
} finally {
safeReturn(jedis);
}
}

/**
* 在散列里关联起给定的键值对
* @param key
* @param field
* @param value
*/

public static void addHashValue(String key, String field, String value) {

Jedis jedis = jedisPool.getResource();
try {
jedis.hset(key, field, value);
} finally {
safeReturn(jedis);
}
}

/**
* 获取指定散列键的值
* @param key
* @param field
* @return
*/

public static String getHashValue(String key, String field) {

Jedis jedis = jedisPool.getResource();
try {
return jedis.hget(key, field);
} finally {
safeReturn(jedis);
}
}

/**
* 获取散列包含的所有键值对
* @param key
* @return
*/

public static Map<String, String> hgetAll(String key) {
Jedis jedis = jedisPool.getResource();
try {
return jedis.hgetAll(key);
} finally {
safeReturn(jedis);
}
}

/**
* 删除给定的键(适用于所有类型)
* @param key
*/

public static void del(String key) {
Jedis jedis = jedisPool.getResource();
try {
jedis.del(key);
} finally {
safeReturn(jedis);
}
}
}

主程序测试:

import org.apache.log4j.Logger;
import util.JedisUtil;

import java.util.HashMap;

public class Application {

private static Logger logger = Logger.getLogger(Application.class);

private static String MAP_KEY_PREFIX = "hash_map_prefix_";

private static String TEST1 = "test1";
private static String TEST2 = "test2";
private static String TEST3 = "test3";
private static String TEST4 = "test4";

public static void main (String[] args) throws Exception {

logger.info("Main Application is starting");
JedisUtil.init();

String key1 = new StringBuilder(MAP_KEY_PREFIX).append(TEST1).toString();
HashMap<String, String> test1Map = new HashMap<>();
test1Map.put("user", "guweiyu");
JedisUtil.setHash(key1, test1Map, 5);
logger.info("key["+key1+"]:"+JedisUtil.getHashValue(key1, "user"));

logger.info("key["+key1+"]:"+JedisUtil.hgetAll(key1));

Thread.sleep(6000);
logger.info("key["+key1+"]:"+JedisUtil.hgetAll(key1));

JedisUtil.del(key1);
logger.info("key["+key1+"]:"+JedisUtil.hexist(key1, "user"));

String key2 = new StringBuilder(MAP_KEY_PREFIX).append(TEST2).toString();
HashMap<String, String> test2Map = new HashMap<>();
test2Map.put("user", "hebaobao");
JedisUtil.setHash(key2, test2Map);

logger.info("key["+key2+"]:"+JedisUtil.getHashValue(key2, "user"));

logger.info("key["+key2+"]:"+JedisUtil.hgetAll(key2));

Thread.sleep(6000);
logger.info("key["+key2+"]:"+JedisUtil.hgetAll(key2));

JedisUtil.addHashValue(key2, "mobino", "173");
logger.info("key["+key2+"]:"+JedisUtil.hgetAll(key2));
}
}

程序运行结果:
Redis实战总结-简单介绍和常用数据结构及命令

持之以恒!!!!!