Redis基础
1.简介
REmote DIctionary Server(Redis) 是一个由 Salvatore Sanfilippo 写的 key-value 存储系统,是跨平台的非关系型数据库。
Redis 是一个开源的使用 ANSI C 语言编写、遵守 BSD 协议、支持网络、可基于内存、分布式、可选持久性的键值对(Key-Value)存储数据库,并提供多种语言的 API。
Redis 通常被称为数据结构服务器,因为值(value)可以是字符串(String)、哈希(Hash)、列表(list)、集合(sets)和有序集合(sorted sets)等类型。
2.安装
redis虽然也有windows版本,但主要使用场景是在linux上,使用centos7作为使用平台
官网: Redis
安装redis需要gcc的环境
输入安装环境
yum install centos-release-scl scl-utils-build
yum install -y devtoolset-8-toolchain
scl enable devtoolset-8 bash
测试gcc gcc -v
sing built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/opt/rh/devtoolset-8/root/usr/libexec/gcc/x86_64-redhat-linux/8/lto-wrapper
Target: x86_64-redhat-linux
Configured with: ../configure --enable-bootstrap --enable-languages=c,c++,fortran,lto --prefix=/opt/rh/devtoolset-8/root/usr --mandir=/opt/rh/devtoolset-8/root/usr/share/man --infodir=/opt/rh/devtoolset-8/root/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-shared --enable-threads=posix --enable-checking=release --enable-multilib --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-gcc-major-version-only --with-linker-hash-style=gnu --with-default-libstdcxx-abi=gcc4-compatible --enable-plugin --enable-initfini-array --with-isl=/builddir/build/BUILD/gcc-8.3.1-20190311/obj-x86_64-redhat-linux/isl-install --disable-libmpx --enable-gnu-indirect-function --with-tune=generic --with-arch_32=x86-64 --build=x86_64-redhat-linux
Thread model: posix
gcc version 8.3.1 20190311 (Red Hat 8.3.1-3) (GCC)
解压压缩包 tar -zxvf redis-6.2.1.rar.gz
解压完成进入目录cd redis-6.2.10.tar.gz
在redis目录下再次执行make
命令进行编译
编译完成跳过make test
,在执行make install
命令进行安装
默认安装路径在/ust/local/bin
- redis-benchmark:性能测试工具
- redis-check-aof:修复有问题的aof文件,rdb和aof
- redis-check-dump:修复有问题的dump.rbd文件
- redis-sentinel:redis集群使用
- redis-server:redis服务器启动命令
- redis-cli:客户端,操作入口
redis启动:
-
前台启动
redis-server
(不推荐),当前窗口一旦关闭,redis服务也会关闭7782:C 20 Feb 2023 13:54:56.028 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo 7782:C 20 Feb 2023 13:54:56.028 # Redis version=6.2.10, bits=64, commit=00000000, modified=0, pid=7782, just started 7782:C 20 Feb 2023 13:54:56.028 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf 7782:M 20 Feb 2023 13:54:56.028 * Increased maximum number of open files to 10032 (it was originally set to 1024). 7782:M 20 Feb 2023 13:54:56.028 * monotonic clock: POSIX clock_gettime _._ _.-``__ ''-._ _.-`` `. `_. ''-._ Redis 6.2.10 (00000000/0) 64 bit .-`` .-```. ```\/ _.,_ ''-._ ( ' , .-` | `, ) Running in standalone mode |`-._`-...-` __...-.``-._|'` _.-'| Port: 6379 | `-._ `._ / _.-' | PID: 7782 `-._ `-._ `-./ _.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | https://redis.io `-._ `-._`-.__.-'_.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | `-._ `-._`-.__.-'_.-' _.-' `-._ `-.__.-' _.-' `-._ _.-' `-.__.-'
-
后台启动(推荐)
进入redis的解压目录,将
redis.conf
文件复制到etc在cp redis.conf/etc/redis.conf
[root@znhost etc]# ls | grep redis redis.conf
使用vim修改配置,将
daemonize no
改成yes
进入
/usr/local/bin
,输入redis-server /etc/redis.conf
执行服务输入
ps -ef | grep redis
查看redis进程root 8450 1 0 14:10 ? 00:00:00 redis-server 127.0.0.1:6379 root 8473 3256 0 14:13 pts/0 00:00:00 grep --color=auto redis
3.redis的五大数据类型
1.redis键(key)
keys *
查看当前库所有key
exists key
判断某个key是否存在1表示存在,0表示不存在
type key
查看key的类型
del key
删除指定的key数据
unlink key
根据value选择非阻塞删除
expire key 10
为给定的key设置过期时间
ttl key
查看还有多少秒过期,-1表示永不过期,-2表示已过期
select
命令切换数据库
dbsize
查看当前数据库的key的数量
flushdb
清空当前库
flushall
通杀全部库
2.redis字符串(string)
set key value
添加键值对
get key
查询对应键值
append key value
将给定的value追加到原值的末尾
strlen key
获取key的长度
127.0.0.1:6379> set msg helloworld
OK
127.0.0.1:6379> get msg
"helloworld"
127.0.0.1:6379> append msg xinshijie
(integer) 19
127.0.0.1:6379> get msg
"helloworldxinshijie"
127.0.0.1:6379>
127.0.0.1:6379> strlen msg
(integer) 19
setnx key value
只有key不存在时,设置key的值
127.0.0.1:6379> setnx k1 200
(integer) 0
127.0.0.1:6379> key*
(error) ERR unknown command `key*`, with args beginning with:
127.0.0.1:6379> keys *
1) "msg"
2) "k1"
127.0.0.1:6379> setnx k2 200
(integer) 1
127.0.0.1:6379>
incr key
将key中存储的数字加一,只能对数字值操作,如果为空,新增值为1
decr key
将对可以中存储的数字值减一
incrby/decrby key 步长
自定义存储数值增减
127.0.0.1:6379> incr k2
(integer) 201
127.0.0.1:6379> decr k2
(integer) 200
127.0.0.1:6379> incrby k2 10
(integer) 210
127.0.0.1:6379> decrby k2 5
(integer) 205
127.0.0.1:6379>
mset k1 v1 k2 v2 ...
同时设置一个或多个k-v
mget k1 k2 k3...
同时获取一个或多个v
msetnx k1 v2 k1 v2 k3 v3...
同时设置一个或多个k-v,当且仅当所有给定key都不存在
127.0.0.1:6379> mset k3 v3 k4 v4 k5 v5
OK
127.0.0.1:6379> mget k3 k4 k5
1) "v3"
2) "v4"
3) "v5"
127.0.0.1:6379>
getrange key start end
获取值的范围
setrange key start v
用v覆盖key所存储的字符串值,从start开始索引从0开始
127.0.0.1:6379> getrange msg 0 4
"hello"
127.0.0.1:6379> setrange msg 0 hi
(integer) 19
127.0.0.1:6379> get msg
"hilloworldxinshijie"
setex k 过期时间 v
设置键值的同时,设置过期时间,单位秒
getset k v
以新换旧,替换掉旧值
127.0.0.1:6379> getset msg helloxin
"hilloworldxinshijie"
127.0.0.1:6379> get msg
"helloxin"
127.0.0.1:6379>
3.redis列表(list)
lpush/rpush k v1 v2 v3...
从左边/右边插入一个或多个值
lpop/rpop k [count]
从左边/右边弹出一个值,也可以指定弹出的个数。值在键在,值光键亡
rpoplpush k1 k2
从k1列表右边弹出一个值,插入到k2列表的左边
lrange k start stop
按照索引下标获得元素(从左到右)lrange k 0 -1可以查看所有元素
127.0.0.1:6379> lpush k1 v1 v2 v3 v4 v5
(integer) 5
127.0.0.1:6379> lrange k1 0 -1
1) "v5"
2) "v4"
3) "v3"
4) "v2"
5) "v1"
127.0.0.1:6379> lpop k1 2
1) "v5"
2) "v4"
127.0.0.1:6379> rpush k2 v21 v22 v23 v24
(integer) 4
127.0.0.1:6379> lrange k2 0 -1
1) "v21"
2) "v22"
3) "v23"
4) "v24"
127.0.0.1:6379> rpoplpush k1 k2
"v1"
127.0.0.1:6379> lrange k2 0 -1
1) "v1"
2) "v21"
3) "v22"
4) "v23"
5) "v24"
127.0.0.1:6379> lrange k1 0 -1
1) "v3"
2) "v2"
127.0.0.1:6379>
lindex k index
按照索引获取元素
llen k
获取列表的长度
127.0.0.1:6379> lindex k2 2
"v22"
127.0.0.1:6379> llen k2
(integer) 5
linsert k before/after value newvalue
在value的后面插入newvalue插入值
lrem k n value
从左边删除n个value
lset k index v
将列表key下表为index的值替换成value
127.0.0.1:6379> linsert k2 before v21 v2
(integer) 6
127.0.0.1:6379> lrange k2 0 -1
1) "v1"
2) "v2"
3) "v21"
4) "v22"
5) "v23"
6) "v24"
127.0.0.1:6379> lrem k2 2 v1
(integer) 1
127.0.0.1:6379> lpush k2 v24 v25
(integer) 7
127.0.0.1:6379> lrange k2 0 -1
1) "v25"
2) "v24"
3) "v2"
4) "v21"
5) "v22"
6) "v23"
7) "v24"
127.0.0.1:6379> lrem k2 2 v24
(integer) 2
127.0.0.1:6379>
127.0.0.1:6379> lrange k2 0 -1
1) "v25"
2) "v2"
3) "v21"
4) "v22"
5) "v23"
127.0.0.1:6379> lset k2 1 v223
OK
127.0.0.1:6379> lrange k2 0 -1
1) "v25"
2) "v223"
3) "v21"
4) "v22"
5) "v23"
127.0.0.1:6379>
4.redis集合(set)
sadd k v1 v2 v3....
将一个或多个member元素加入到集合set中,已存在的member元素会被忽略
smembers k
取出该集合的所有值
sismember k v
判断集合k是否含有该v值==(有1,没有0)==
scard k
返回集合的元素个数
srem k v1 v2 v3...
删除集合中的某个元素
spop k [count]
随机从集合中弹出一个值
srandmember k n
随机从该集合中取出n个值,不会从集合中删除
127.0.0.1:6379> sadd k1 v1 v2 v3 v4 v5 v6 v7
(integer) 7
127.0.0.1:6379> smembers k1
1) "v3"
2) "v1"
3) "v4"
4) "v5"
5) "v2"
6) "v7"
7) "v6"
127.0.0.1:6379>
127.0.0.1:6379> sismember k1 v1
(integer) 1
127.0.0.1:6379> scard k1
(integer) 7
127.0.0.1:6379> srem k1 v1 v2
(integer) 2
127.0.0.1:6379> scard k1
(integer) 5
127.0.0.1:6379>
127.0.0.1:6379> spop k1 2
1) "v4"
2) "v5"
127.0.0.1:6379> sismember k1 v1
(integer) 0
127.0.0.1:6379> scard k1
(integer) 3
127.0.0.1:6379> smembers k1
1) "v3"
2) "v7"
3) "v6"
127.0.0.1:6379>
127.0.0.1:6379> srandmember k1 2
1) "v4"
2) "v7"
127.0.0.1:6379>
smove source destination value
把集合一个值从一个集合移动到另一个集合
27.0.0.1:6379> sadd k1 v1 v2 v3
(integer) 3
127.0.0.1:6379> sadd k2 vv1 vv2 vv3
(integer) 3
127.0.0.1:6379> smove k1 k2 v1
(integer) 1
127.0.0.1:6379> smembers k1
1) "v2"
2) "v3"
127.0.0.1:6379> smembers k2
1) "vv3"
2) "vv2"
3) "vv1"
4) "v1"
127.0.0.1:6379>
sinter k1 k2
返回两个集合的交集元素
127.0.0.1:6379> sadd k1 v1
(integer) 1
127.0.0.1:6379> sinter k1 k2
1) "v1"
127.0.0.1:6379>
sunion k1 k2
返回连个集合并集元素
127.0.0.1:6379> sunion k1 k2
1) "v3"
2) "v1"
3) "vv1"
4) "vv2"
5) "v2"
6) "vv3"
127.0.0.1:6379>
sdiff k1 k2
返回两个集合的差集
元素(key1中的, 不包含的key2中的)
5.redis哈希(hash)
hset key field value
给key集合中的 field键赋value
hget k f
从k集合中f取出v
127.0.0.1:6379> clear
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> hset k1 id 1 name xin age 23
(integer) 3
127.0.0.1:6379> hget k1 id
"1"
127.0.0.1:6379> hget k1 name
"xin"
127.0.0.1:6379>
hmset k1 f1 v1 f2 v2 f3 v3
批量设置set的值
127.0.0.1:6379> hmset k2 id 2 name shi age 24
OK
hexists k1 f
查看哈希表key中,给点的field是否存在
127.0.0.1:6379> hexists k1 id
(integer) 1
127.0.0.1:6379>
hkeys k
列出该hash集合中的所有field
127.0.0.1:6379> hkeys k1
1) "id"
2) "name"
3) "age"
hvals k
列出该hash集合的所有value
127.0.0.1:6379> hvals k1
1) "1"
2) "xin"
3) "23"
hincrby k f increment
为哈希表key中的域field的值加上增量,只能时数值
127.0.0.1:6379> hincrby k1 age 10
(integer) 33
127.0.0.1:6379> hincrby k1 name -1
(error) ERR hash value is not an integer
127.0.0.1:6379>
hsetnx k f v
将哈希表key中的域field的值设置为value,当且仅当域field不存储
127.0.0.1:6379> hsetnx k1 brith 00-10-25
(integer) 1
127.0.0.1:6379> hvals k1
1) "1"
2) "xin"
3) "33"
4) "00-10-25"
127.0.0.1:6379>
6.有序集合(zset)
redis有序集合jzset与普通集合set非常相似,是一个没有重复元素的字符串集合
zadd k s1 v1 s2 v2 s3 v3....
将一个或多个member元素及其score值加入到有序有序集合key中
127.0.0.1:6379> zadd k1 100 c 200 d 50 a 150 e
(integer) 4
zrange k start stop [withscores]
返回有序集key中,下标在start和top之间的元素,带withscores
可以让分数一起返回到结果集
127.0.0.1:6379> zrange k1 0 -1
1) "a"
2) "c"
3) "e"
4) "d"
127.0.0.1:6379> zrange k1 0 -1 WITHSCORES
1) "a"
2) "50
3) "c"
4) "100"
5) "e"
6) "150"
7) "d"
8) "200"
zrangebyscore k min max [withscores] [limit offset count]
返回有序集key中,所有score值介于min和max之间的成员
127.0.0.1:6379> zrangebyscore k1 100 200 withscores
1) "c"
2) "100"
3) "e"
4) "150"
5) "d"
6) "200"
127.0.0.1:6379>
zrevrangbyscore key max min [withscores] [limit offset count]
同时,改为从大到小排序
zincrby k increment v
为元素的score加上增量
zrem k v
删除该集合下,指定值的元素
zcount k min max
统计min和max区间元素的个数
zrank k v
返回该值在集合中的排名,从零开始
127.0.0.1:6379> zincrby k1 20 a
"70"
127.0.0.1:6379> zrem k1 a
(integer) 1
127.0.0.1:6379> zrange k1 0 -1 withscores
1) "c"
2) "100"
3) "e"
4) "150"
5) "d"
6) "200"
127.0.0.1:6379> zcount k1 100 200
(integer) 3
127.0.0.1:6379>
127.0.0.1:6379> zrank k1 d
(integer) 2
127.0.0.1:6379>
4.发布和订阅
Redis 发布订阅 (pub/sub) 是一种消息通信模式:发送者 (pub) 发送消息,订阅者 (sub) 接收消息。
Redis 客户端可以订阅任意数量的频道。
客户端一:
127.0.0.1:6379> SUBSCRIBE news1
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "news1"
3) (integer) 1
1) "message"
2) "news1"
3) "hell"
客户端二:
127.0.0.1:6379> PUBLISH news1 hell
(integer) 1
127.0.0.1:6379>
6.新的数据类型
1.Bitmaps
setbit k offset v
设置bitmaps中某个偏移量的值(0或1)
getbit k offset
获取bitmaps中某个偏移量的值
127.0.0.1:6379> setbit k1 1 1
(integer) 0
127.0.0.1:6379> setbit k1 1 3
(error) ERR bit is not an integer or out of range
127.0.0.1:6379> setbit k1 3 1
(integer) 0
127.0.0.1:6379> setbit k1 6 1
(integer) 0
127.0.0.1:6379> setbit k1 8 1
(integer) 0
127.0.0.1:6379> getbit k1 3
(integer) 1
127.0.0.1:6379>
bitcount k
统计字符串被设置成1的bit数
127.0.0.1:6379> bitcount k1
(integer) 4
bitop k
2.HyperLogLog
用于计算基数
pfadd k element [element]
添加指定元素
127.0.0.1:6379> pfadd k1 "java" "mysql" "c++"
(integer) 1
127.0.0.1:6379> pfadd k1 "java"
(integer) 0
pfcount k
统计基数的个数
127.0.0.1:6379> pfcount k1
(integer) 3
pfmerge destkey sourcekey sourcekey
将一个或多个HLL合并后储存在另一个HLL中
127.0.0.1:6379> pfmerge k3 k1 k2
OK
127.0.0.1:6379> pfcount k3
(integer) 6
127.0.0.1:6379>
3.Geospatial
主要存储地理信息
geoadd k longitude latitude member <longitude latitude member
添加一个或多个地理信息
127.0.0.1:6379> geoadd china:city 106.50 29.53 chongqing
(integer) 1
geopos k member
获取坐标信息
127.0.0.1:6379> geopos china:city chongqing
1) 1) "106.49999767541885376"
2) "29.52999957900659211"
127.0.0.1:6379>
geodist k m1 m2
获取两个位置之间的距离
georadius k longitude latitude radius m|km|ft|mi
以给定的经纬度为中心,找出某一半径内的元素
7.Jedis
jedis就是基于java语言的redis客户端,集成了redis的命令操作,提供了连接池管理。 redis-cli是redis官方提供的客户端,可以看作一个shell程序,它可以发送命令对redis进行操作。 对于jedis同理是使用java语言操作redis,双方都遵循redis提供的协议,按照协议开发对应的客户端。
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.2.0</version>
</dependency>
修改redis.conf,把bind:127.0.0.1
注释掉,把protected-mode no
改为yes
在root下,使用systemctl stop firewalld
关闭防火墙
public class JedisDemo {
public static void main(String[] args) {
Jedis jedis = new Jedis("192.168.88.130", 6379);
String ping = jedis.ping();
System.out.println(ping);
}
}
8.redis和springboot整合
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
配置文件application
spring:
redis:
port: 6379
host: 192.168.88.130
编写redis配置类
@Configuration
public class RedisConfig {
@Resource
private RedisConnectionFactory factory;
/**
* 配置redis
* @return
*/
@Bean
public RedisTemplate redisTemplate() {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(factory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
Jackson2JsonRedisSerializer<Object> jsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
redisTemplate.setValueSerializer(jsonRedisSerializer);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
om.setTimeZone(TimeZone.getDefault());
om.configure(MapperFeature.USE_ANNOTATIONS, false);
om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
om.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance ,ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
om.setSerializationInclusion(JsonInclude.Include.NON_NULL);
jsonRedisSerializer.setObjectMapper(om);
return redisTemplate;
}
}
使用
@Autowired
private RedisTemplate redisTemplate;
redisTemplate.opsForValue().set(key, loginUser, 90, TimeUnit.MINUTES);
9.redis的事务操作
redis事务是一个单独的隔离操作,事务中的所有命令都会序列化,按顺序执行,事务在执行的过程中年,不会被其他客服端发送来的命令请求所打断
redis事务的主要作用就是串联多个命令
防止别的命令插队
Redis 事务可以一次执行多个命令, 并且带有以下三个重要的保证:
-
批量操作在发送 EXEC 命令前被放入队列缓存。
-
收到 EXEC 命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行。
-
在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。
一个事务从开始到执行会经历以下三个阶段:
- 开始事务。
- 命令入队。
- 执行事务。
事务的三个特性:
-
单独的隔离操作
事务中的所有命令都会序列化,按顺序的执行,事务在执行的过程中,不会被其他客户端发送来的命令请求所打断
-
没有隔离级别的概念
队列中的命令没有提交之前都不会实际被执行,因为事务提交前任何指令都不会被实际执行
-
不保证原子性
事务中如果有一条命令执行失败,其后的命令仍会被执行,没有回滚
先以 MULTI 开始一个事务, 然后将多个命令入队到事务中, 最后由 EXEC 命令触发事务, 一并执行事务中的所有命令
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k1 v1
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> set k3 v3
QUEUED
127.0.0.1:6379(TX)> exec
1) OK
2) OK
3) OK
DISCARD 取消事务,放弃执行事务块内的所有命令。
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k4 v4
QUEUED
127.0.0.1:6379(TX)> set k5 v5
QUEUED
127.0.0.1:6379(TX)> discard
OK
127.0.0.1:6379> keys*
(error) ERR unknown command `keys*`, with args beginning with:
127.0.0.1:6379> keys *
1) "k2"
2) "k1"
3) "k3"
127.0.0.1:6379>
单个 Redis 命令的执行是原子性的,但 Redis 没有在事务上增加任何维持原子性的机制,所以 Redis 事务的执行并不是原子性的。
事务可以理解为一个打包的批量执行脚本,但批量指令并非原子化的操作,中间某条指令的失败不会导致前面已做指令的回滚,也不会造成后续的指令不做
watch key
watch key [key....]
在执行multi之前,先执行watch key1 [key2…],可以监视一个或多个key,如果事务执行之前这个key或被其他命令所改动,那么事务将被打断
客户端一
127.0.0.1:6379> watch k1
OK
127.0.0.1:6379> incrby k1 10
(integer) 30
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> incrby k1 10
QUEUED
127.0.0.1:6379(TX)> exec
(nil)
127.0.0.1:6379>
客户端二
127.0.0.1:6379> watch k1
OK
127.0.0.1:6379> incrby k1 10
(integer) 20
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> incrby k1 10
QUEUED
127.0.0.1:6379(TX)> exec
(nil)
127.0.0.1:6379>
unwatch
取消监视
27.0.0.1:6379> unwatch
OK
127.0.0.1:6379>