文章目录
Redis的数据类型
Redis 是一种基于内存的数据库,并且提供一定的持久化功能,它是一种键值数据库,使用 key 作为索引找到当前缓存的数据,并且返回给程序调用者。
当前的 Redis 支持 5 种基础数据类型和 3 种特殊数据类型,它们分别是字符串(String)、哈希结构(hash)、列表(List)、集合(set)、有序集合(zset)和基数(HyperLogLog)、GEO(geospatial)、位图(bitmap)。
Redis 定义的这数据类型是十分有用的,它除了提供简单的存储功能,还能对存储的数据进行一些计算。
比如字符串可以支持浮点数的自增、自减、字符求子串,集合求交集、并集,有序集合进行排序等,所以使用它们有利于对一些不太大的数据集合进行快速计算,简化编程,同时它也比数据库要快得多,所以它们对系统性能的提升十分有意义。
- STRING(字符串):可以是保存字符串、整数和浮点数 ,可以对字符串进行操作
- 比如增加字符或者求子串:
- 如果是整数或者浮点数,可以实现计算,比如自增等
- LIST(列表):它是一个链表,它的每一个节点都包含一个字符串
- Redis 支持从链表的两端插入或者弹出节点,或者通过偏移对它进行裁剪;
- 还可以读取一个或者多个节点,根据条件删除或者查找节点等
- SET(集合):它是一个收集器,但是是无序的,在它里而每一个元素都是一个字符串,而且是独一无二,各不相同的
- 可以新增、读取、删除单个元素:检测一个元素是否在集合中;
- 计算它和其他集合的交集、并集和差集等;
- 随机从集合中读取元素
- HASH(哈希散列表):它类似于 Java 语言中的 Map,是一个键值对应的无序列表
- 可以増、删、査、改单个键值对,也可以获取所有的键值对
- ZSET(有序集合):它是一个有序的集合,可以包含字符 串、整数、浮点数、分值(score),元素 的排序是依据分值的大小来决定的
- 可以增、删、査、改元素,根据分值的范围或者成员 來获取对应的元索
- geospatial(Geo):Redis 在 3.2 推出 Geo 类型,该功能可以推算出地理位置信息,两地之间的距离。
- 规则:两极无法直接添加,一般会下载城市数据,直接通过 Java 程序一次性导入。
- 有效的经度从 -180 度到 180 度。有效的纬度从 -85.05112878 度到 85.05112878 度。当坐标位置超出指定范围时,该命令将会返回一个错误。
- HyperLogLog(基数):它的作用是计算重复的值,以确定存储的数量
- 只提供基数的运算,不提供返回的功能
- bitmap (位图):是通过最小的单位bit来进行0或者1的设置,表示某个元素对应的值或者状态。一个bit的值,或者是0,或者是1;也就是说一个bit能存储的最多信息是2。
- bitmap 常用于统计用户信息比如活跃粉丝和不活跃粉丝、登录和未登录、是否打卡等。
String 数据结构和常用命令
字符串(String)是 Redis 最基本的数据结构,它将以一个键和一个值存储于 Redis 内部,它犹如 Java 的 Map 结构,让 Redis 通过键去找到值。Redis 字符串的数据结构如图 1 所示。
图 1 Redis 字符串数据结构
Redis 会通过 key 去找到对应的字符串,比如通过 key1 找到 value1,又如在 Java 互联网中,假设产品的编号为 0001,只要设置 key 为 product_0001,就可以通过 product_0001 去保存该产品到 Redis 中,也可以通过 product_0001 从 redis 中找到产品信息。
常用命令如下:
命 令 | 说 明 | 备 注 |
---|---|---|
set key value | 设置键值对 | 最常用的写入命令 |
get key | 通过键获取值 | 最常用的读取命令 |
del key | 通过 key,删除键值对 | 删除命令,返冋删除数,注意,它是个通用的命令,换句话说在其他数据结构中,也可以使用它 |
strlen key | 求 key 指向字符串的长度 | 返回长度 |
getset key value | 修改原来 key 的对应值,并将旧值返回 | 如果原来值为空,则返回为空,并设置新值 |
getrange key start end | 获取子串 | 记字符串的长度为 len,把字符串看作一个数组,而 Redis 是以 0 开始计数的,所以 start 和 end 的取值范围 为 0 到 len-1 |
append key value | 将新的字符串 value,加入到原来 key 指向的字符串末 | 返回 key 指向新字符串的长度 |
为了让大家更为明确,在 Redis 提供的客户端进行测试如图 2 所示。
图 2 Redis 操作字符串重用命令
这里我们看到了字符串的常用操作,为了在 Spring 中测试这些命令,首先配置 Spring 关于 Redis 字符串的运行环境,配置 Spring 关于 Redis 字符串的运行环境代码如下所示。
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxIdle" value="50" />
<property name="maxTotal" value="100" />
<property name="maxWaitMillis" value="20000" />
</bean>
<bean id="connectionFactory"
class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="hostName" value="localhost" />
<property name="port" value="6379" />
<property name="poolConfig" ref="poolConfig" />
</bean>
<bean id="jdkSerializationRedisSerializer"
class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" />
<bean id="stringRedisSerializer"
class="org.springframework.data.redis.serializer.StringRedisSerializer" />
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="connectionFactory" />
<property name="keySerializer" ref="stringRedisSerializer" />
<property name="valueSerializer" ref="jdkSerializationRedisSerializer" />
</bean>
注意,这里给 Spring 的 RedisTemplate 的键值序列化器设置为了 String 类型,所以它就是一种字符串的操作。假设把这段 Spring 的配置代码保存为一个独立为文件 applicationContext.xml,使用 Spring 测试 Redis 字符串操作代码如下所示。
package com.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.data.redis.core.RedisTemplate;
import com.pojo.Role;
public class Test {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
"applicationContext.xml");
RedisTemplate redisTemplate = applicationContext
.getBean(RedisTemplate.class);
// 设值
redisTemplate.opsForValue().set("key1", "value1");
redisTemplate.opsForValue().set("key2", "value2");
// 通过key获取值
String value1 = (String) redisTemplate.opsForValue().get("key1");
System.out.println(value1);
// 通过key删除值
redisTemplate.delete("key1");
// 求长度
Long length = redisTemplate.opsForValue().size("key2");
System.out.println(length);
// 设值新值并返回旧值
String oldValue2 = (String) redisTemplate.opsForValue().getAndSet(
"key2", "new_value2");
System.out.println(oldValue2);
// 通过key获取值.
String value2 = (String) redisTemplate.opsForValue().get("key2");
System.out.println(value2);
// 求子串
String rangeValue2 = redisTemplate.opsForValue().get("key2", 0, 3);
System.out.println(rangeValue2);
// 追加字符串到末尾,返回新串长度
int newLen = redisTemplate.opsForValue().append("key2", "_app");
System.out.println(newLen);
String appendValue2 = (String) redisTemplate.opsForValue().get("key2");
System.out.println(appendValue2);
}
}
这是主要的目的只是在 Spring 操作 Redis 键值对,其操作就等同于图 2 所示的命令一样。
在 Spring 中,redisTemplate.opsForValue() 所返回的对象可以操作简单的键值对,可以是字符串,也可以是对象,具体依据你所配置的序列化方案。
由于配置 Spring 关于 Redis 字符串的运行环境代码所配置的是字符串,所以以字符串来操作 Redis,其测试结果如下:
图 3 运行结果
结果和我们看到的命令行的结果一样的,作为开发者要熟悉这些方法。
上面介绍了字符串最常用的命令,但是 Redis 除了这些之外还提供了对整数和浮点型数字的功能。如果字符串是数字(整数或者浮点数),那么 Redis 还能支持简单的运算。不过它的运算能力比较弱,目前版本只能支持简单的加减法运算,如下 。
- incr key :在原字段上加 1 ,只能对整数操作
- incrby key increment : 在原字段上加上整数(increment) , 只能对整数操作
- decr key :在原字段上减 1 ,只能对整数操作
- decrby key decrement : 在原字段上减去整数(decrement), 只能对整数操作
- incrbyfloat keyincrement : 在原字段上加上浮点数(increment), 可以操作浮点数或者整数
对操作浮点数和整数进行了测试,如图 4 所示。
图 4 操作浮点数和整数
在测试过程中,如果开始把 val 设置为浮点数,那么 incr、decr、incrby、decrby 的命令都会失败。Redis 并不支持减法、乘法、除法操作,功能十分有限,这点需要我们注意。
由于 Redis 的功能比较弱,所以经常会在 Java 程序中读取它们,然后通过 Java 进行计算并设置它们的值。
注意,所有关于减法的方法,原有值都必须是整数,否则就会引发异常,
hash 数据结构和常用命令
Redis 中哈希(hash)结构就如同 Java 的 map 一样,一个对象里面有许多键值对,它是特别适合存储对象的,如果内存足够大,那么一个 Redis 的 hash 结构可以存储 2 的 32 次方减 1 个键值对(40 多亿)。
一般而言,不会使用到那么大的一个键值对,所以我们认为 Redis 可以存储很多的键值对。在 Redis 中,hash 是一个 String 类型的 field 和 value 的映射表,因此我们存储的数据实际在 Redis 内存中都是一个个字符串而已。
Redis hash 结构命令,如下。
命 令 | 说 明 | 备 注 |
---|---|---|
hdel key field1[field2…] | 删除 hash 结构中的某个(些)字段 | 可以进行多个字段的删除 |
hexists key field | 判断 hash 结构中是否存在 field 字段 | 存在返回 1,否则返回 0 |
hgetall key | 获取所有 hash 结构中的键值 | 返回键和值 |
hincrby key field increment | 指定给 hash 结构中的某一字段加上一个整数 | 要求该字段也是整数字符串 |
hincrbyfloat key field increment | 指定给 hash 结构中的某一字段加上一个浮点数 | 要求该字段是数字型字符串 |
hkeys key | 返回 hash 中所有的键 | —— |
hlen key | 返回 hash 中键值对的数量 | —— |
hmget key field1[field2…] | 返回 hash 中指定的键的值,可以是多个 | 依次返回值 |
hmset key field1 value1 [field2 field2…] | hash 结构设置多个键值对 | —— |
hset key filed value | 在 hash 结构中设置键值对 | 单个设值 |
hsetnx key field value | 当 hash 结构中不存在对应的键,才设置值 | —— |
hvals key | 获取 hash 结构中所有的值 | —— |
可以看出,在 Redis 中的哈希结构和字符串有着比较明显的不同。
首先,命令都是以 h 开头,代表操作的是 hash 结构。其次,大多数命令多了一个层级 field,这是 hash 结构的一个内部键,也就是说 Redis 需要通过 key 索引到对应的 hash 结构,再通过 field 来确定使用 hash 结构的哪个键值对。
下面通过 Redis 的这些操作命令来展示如何使用它们,如图 2 所示。
图 2 Redis 的 hash 结构命令展示
从图 2 中可以看到,Redis 关于哈希结构的相关命令。这里需要注意的是:
哈希结构的大小,如果哈希结构是个很大的键值对,那么使用它要十分注意,尤其是关于 hkeys、hgetall、hvals 等返回所有哈希结构数据的命令,会造成大量数据的读取。这需要考虑性能和读取数据大小对 JVM 内存的影响。
对于数字的操作命令 hincrby 而言,要求存储的也是整数型的字符串,对于 hincrbyfloat 而言,则要求使用浮点数或者整数,否则命令会失败。
我们用 Spring 来完成图 2 的功能,代码如下所示。
public static void testRedisHash() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
RedisTemplate redisTemplate = applicationcontext.getBean(RedisTemplate.class);
String key = "hash";
Map<String, String> map = new HashMap<String,String>();
map.put("f1", "val1");
map.put("f2", "val2");
// 相当于hmset命令
redisTemplate.opsForHash(