Redis初识

Redis的特性

  • 速度快
  • 持久化
  • 多种数据结构
  • 支持多种编辑语言
  • 功能丰富
  • 简单
  • 主从复制
  • 高可用,分布式

速度快

  • 数据写在内存(主要原因)

  • 使用C语言编写

  • 线程模型为单线程

持久化(断电不丢数据)

Redis所有数据保持在内存中,对数据的更新将异步地保存到磁盘上。

多种数据结构

image-20211220120919227

支持多种编辑语言

Java、PHP、Python、Ruby、Lua、node.js

功能丰富

  • 发布订阅
  • 事务
  • Lua脚本
  • pipeline

简单

  • 不依赖外部库(like libevent)
  • 单线程模型

主从复制

从服务器 → 主服务器

高可用,分布式

Redis-Sentinel(v2.8)支持高可用

Redis-Cluster(v3.0)支持分布式

Redis典型应用场景

  • 缓存系统
  • 计数器
  • 消息队列系统
  • 排行榜
  • 社交网络
  • 实时系统

Redis三种启动方式

  • 最简启动

    redis-server

  • 动态参数启动

    redis-server —port 6380

  • 配置文件启动

    redis-server configPath

三种启动方式比较

  • 生成环境选择配置启动
  • 单机多实例配置文件可以用端口区分开

Redis客户端连接

image-20211220141349732

Redis客户端返回值

image-20211220141504962

image-20211220141537814

Redis常用配置

image-20211220141738949

Redis API的使用和理解

通用命令和数据结构

通用命令

  • keys

  • dbsize

  • exists key
  • del key [key ..]
  • expire key seconds
  • type key

keys

keys *

遍历所有key

1
2
3
4
5
6
7
8
9
10
11
127.0.0.1:6379> set hello world
OK
127.0.0.1:6379> set php good
OK
127.0.0.1:6379> set java best
OK
127.0.0.1:6379> keys *
1) "php"
2) "hello"
3) "key"
4) "java"

keys [pattern]

遍历所有key

1
2
3
4
5
6
7
8
9
10
11
127.0.0.1:6379> mset hello world hehe haha php good phe his
OK
127.0.0.1:6379> keys he*
1) "hello"
2) "hehe"
127.0.0.1:6379> keys he[h-l]*
1) "hello"
2) "hehe"
127.0.0.1:6379> keys ph?
1) "php"
2) "phe"

keys命令一般不在生产环境使用

key* 怎么用

  • 热备从节点
  • scan

dbsize

dbsize

计算key的总数

1
2
3
4
5
6
7
8
9
127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3 k4 v4
OK
127.0.0.1:6379> dbsize
(integer) 4
127.0.0.1:6379> sadd myset a b c d e
(integer) 5
127.0.0.1:6379> dbsize
(integer) 5
127.0.0.1:6379>

exists

exists

检查key是否存在

1
2
3
4
5
6
7
8
127.0.0.1:6379> set a b
OK
127.0.0.1:6379> exists a
(integer) 1
127.0.0.1:6379> del a
(integer) 1
127.0.0.1:6379> exists a
(integer) 0

del

del

删除指定key-value

1
2
3
4
5
6
7
8
127.0.0.1:6379> set a b
OK
127.0.0.1:6379> get a
"b"
127.0.0.1:6379> del a
(integer) 1
127.0.0.1:6379> get a
(nil)

expire、ttl、 persist

expire key seconds

key在seconds秒后过期

ttl key

查看key剩余的过期时间

persist key

去掉key的过期时间

1
2
3
4
5
6
7
8
9
10
11
12
127.0.0.1:6379> set hello world
OK
127.0.0.1:6379> expire hello 20
(integer) 1
127.0.0.1:6379> ttl hello
(integer) 12
127.0.0.1:6379> get hello
"world"
127.0.0.1:6379> ttl hello
(integer) -2 (-2表示key已经不存在了)
127.0.0.1:6379> get hello
(nil)
1
2
3
4
5
6
7
8
9
10
11
12
127.0.0.1:6379> set hello world
OK
127.0.0.1:6379> expire hello 20
(integer) 1
127.0.0.1:6379> ttl hello
(integer) 14
127.0.0.1:6379> persist hello
(integer) 1
127.0.0.1:6379> ttl hello
(integer) -1 (-1表示key存在,且没有过期时间)
127.0.0.1:6379> get hello
"world"

type

type key

返回key的类型

1
2
3
4
5
6
7
8
127.0.0.1:6379> set a b
OK
127.0.0.1:6379> type a
string
127.0.0.1:6379> sadd myset 1 2 3
(integer) 3
127.0.0.1:6379> type myset
set

主要返回类型

  • string
  • hash
  • list
  • set
  • zset
  • none

时间复杂度

命令 时间复杂度
keys O(n)
dbsize O(1)
del O(1)
exists O(1)
expire O(1)
type O(1)

数据结构和内部编码

image-20211220152122372

单线程

  • 一次只能执行一条命令

  • 拒绝长/慢命令(keys, flushall, flushdb, slow lua script, mutil/exec, operate big value(collection))

  • 其实不是单线程(fysnc file descriptor,close file descriptor)

单线程为什么这么快

  • 纯内存(主要原因)
  • 非阻塞IO
  • 避免线程切换和竞态消耗

字符串

字符串键值结构

key value
hello world
counter 1
bits 10111101

字符串类型

场景

  • 缓存
  • 计数器
  • 分布式锁等

get、set、del

get key

获取key对应的value

set key value

设置key-value

del key

删除key-value

1
2
3
4
5
6
7
8
127.0.0.1:6379> set hello world
OK
127.0.0.1:6379> get hello
"world"
127.0.0.1:6379> del hello
(integer) 1
127.0.0.1:6379> get hello
(nil)

incr、decr、 incrby、 decrby

incr key

key自增1,如果key不存在,自增后get(key)=1

decr key

key自减1,如果key不存在,自减后get(key)=-1

incrby key k

key自增k,如果key不存在,自增后get(key)=k

decr key k

key自减k,如果key不存在,自减后get(key)=-k

1
2
3
4
5
6
7
8
9
10
11
12
127.0.0.1:6379> get counter
(nil)
127.0.0.1:6379> incr counter
(integer) 1
127.0.0.1:6379> get counter
"1"
127.0.0.1:6379> incrby counter 99
(integer) 100
127.0.0.1:6379> decr counter
(integer) 99
127.0.0.1:6379> get counter
"99"

实战

记录网站每个用户个人主页的访问量

incr userid:pageview (单线程 : 无竞争)

缓存视频的基本信息(数据源在MySQL中)伪代码

image-20211220154251281

1
2
3
4
5
6
7
8
9
10
public VideoInfo get(long id) {
String redisKey = redisPrefix + id;
VideoInfo videoInfo = redis.get(redisKey);
if (videoInfo == nul) {
videoInfo = mysql.get(id);
if (videoInfo != null) {
//序列化
redis.set(redisKey, serialize(videoInfo);
return videoInfo;
}

分布式id生成器

incr id(原子操作)

set、set、setxx

set key value

不管key是否存在,都设置

setnx key value

key不存在,才设置

set key value XX

key存在,才设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
127.0.0.1:6379> exists php
(integer) 0
127.0.0.1:6379> set php good
OK
127.0.0.1:6379> setnx php bad
(integer) 0
127.0.0.1:6379> set php best xx
OK
127.0.0.1:6379> get php
"best"
127.0.0.1:6379> exists java
(integer) 0
127.0.0.1:6379> setnx java best
(integer) 1
127.0.0.1:6379> set java easy xx
OK
127.0.0.1:6379> get java
"easy"
127.0.0.1:6379> exists lua
(integer) 0
127.0.0.1:6379> set lua hehe xx
(nil)

mget、mset

mget key1 key2 key3…

批量获取key,原子操作

mset key1 value1 key2 value2 key3 value3

批量设置key-value

1
2
3
4
5
6
127.0.0.1:6379> mset hello world java best php good
OK
127.0.0.1:6379> mget hello java php
1) "world"
2) "best"
3) "good"

getset、append、strlen

getset key newvalue

set key newvalue并返回旧的value

append key value

将value追加到旧的value

strlen key

返回字符串的长度(注意中文)

1
2
3
4
5
6
7
8
9
10
127.0.0.1:6379> set hello world
OK
127.0.0.1:6379> getset hello php
"world"
127.0.0.1:6379> append hello java
(integer) 7
127.0.0.1:6379> get hello
"phpjava"
127.0.0.1:6379> strlen hello
(integer) 7

incrbyfloat、getrange、setrange

incrbyfloat key 3.5

加key对应的值3.5

getrange key start end

获取字符串指定下标所有的值

setrange key index value

设置指定下标所有对应的值

1
2
3
4
5
6
7
8
9
10
11
12
127.0.0.1:6379> incr counter
(integer) 1
127.0.0.1:6379> incrbyfloat counter 1.1
"2.10000000000000009"
127.0.0.1:6379> set hello javabest
OK
127.0.0.1:6379> getrange hello 0 2
"jav"
127.0.0.1:6379> setrange hello 4 p
(integer) 8
127.0.0.1:6379> get hello
"javapest"

总结

命令 含义 复杂度
set key value 设置key-value O(1)
get key 获取key-value O(1)
del key 删除key-value O(1)
setxn setxx 根据key是否存在 O(1)
Incr decr 计数 O(1)
mget mset 批量操作key-value O(n)

hash

哈希键值结构

key field value

API

hget、hset、hdel

hget key field

获取hash key对应的field的value

hset key field value

设置hash key对应field的value

hdel key field

删除hash key对应field的value

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
127.0.0.1:6379> hset user:1:info age 23
(integer) 1
127.0.0.1:6379> hget user:1:info age
"23"
127.0.0.1:6379> hset user:1:info name ronaldo
(integer) 1
127.0.0.1:6379> hgetall user:1:info
1) "age"
2) "23"
3) "name"
4) "ronaldo"
127.0.0.1:6379> hdel user:1:info age
(integer) 1
127.0.0.1:6379> hgetall user:1:info
1) "name"
2) "ronaldo"

hexists、hlen

hexists key field

判断hash key是否有field

hlen key

获取hash key field的数量

1
2
3
4
5
6
7
8
9
127.0.0.1:6379> hgetall user:1:info
1) "name"
2) "ronaldo"
3) "age"
4) "23"
127.0.0.1:6379> hexists user:1:info name
(integer) 1
127.0.0.1:6379> hlen user:1:info
(integer) 2

hmget、hmset

hmget key field1 field2…. fieldN

批量获取hash key的一批field对应的值

hmset key field1 value1 field2 value…fieldN valueN

批量设置hash key的一批field value

1
2
3
4
5
6
7
127.0.0.1:6379> hmset user:2:info age 30 name kaka page 50
OK
127.0.0.1:6379> hlen user:2:info
(integer) 3
127.0.0.1:6379> hmget user:2:info age name
1) "30"
2) "kaka"

hgetall、hvals、hkeys

hgetall key

返回hash key对应所有的field和value

hvals key

返回hash key对应所有field的value

hkeys key

返回hash key对应所有field

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
127.0.0.1:6379> hgetall user:2:info
1) "age"
2) "30"
3) "name"
4) "kaka"
5) "page"
6) "50"
127.0.0.1:6379> hvals user:2:info
1) "30"
2) "kaka"
3) "50"
127.0.0.1:6379> hkeys user:2:info
1) "age"
2) "name"
3) "page"

hsetnx、hincrby、 hincrbyfloat

hsetnx key field value

设置hash key对应field的value(如field已经存在,则失败)

hincrby key field intCounter

hash key对应的field的value自增intCounter

hincrbyfloat key field floatCounter

hincrby浮点数版

实战

记录网站每个用户个人主页的访问量

hincrby user:1:info pageview count

缓存视频的基本信息(数据源在mysql中)伪代码

1
2
3
4
5
6
7
8
9
10
11
12
public VideoInfo get(long id) {
String redisKey = redisPrefix + id;
Map < String,String> hashMap = redis.hgetAll(redisKey);
VideoInfo videoInfo = transferMap ToVideo(hashMap);
if (videoInfo == null) {
videoInfo = mysql.get(id);
if (videoInfo != null) {
redis.hmset(redisKey, transferVideo ToMap(videoInfo));
}
}
return videoInfo;
}

总结

命令 复杂度
hget set hdel O(1)
hexists O(1)
hincrby O(1)
hgetall hvals hkeys O(n)
hmget hmset O(n)

List

列表结构

key elements

有序、可重复、左右两边插入弹出

API

rpush、lpush、Linsert

rpush key value1 value2.alueN

从列表右端插入值(1-N个)

lpush key value1 value2.alueN

从列表左端插入值(1-N个)

linsert key before|after value newValue

在list指定的值前|后插入newValue

rpop、lpop、lrem、ltrim

rpop key

从列表右侧弹出一个item

lpop key

从列表左侧弹出一个item

lrem key count value

根据count值,从列表中删除所有value相等的项
(1) count>0 ,从左到右,删除最多count个value相等的项
(2) count<0 ,从右到左,删除最多Math.abs(count)个value相等的项
(3) count=0 , 删除所有value相等的项

Itrim key start end

按照索引范围修剪列表

lrange、lindex、llen

lrange key start end (包含end)

获取列表指定索引范围所有item

lindex key index

获取列表指定索引的item

llen key

获取列表长度

lset

Iset key index newValue

设置列表指定索引值为newValue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
127.0.0.1:6379> rpush mylist a b c
(integer) 3
127.0.0.1:6379> lrange mylist 0 -1
1) "a"
2) "b"
3) "c"
127.0.0.1:6379> lpush mylist 0
(integer) 4
127.0.0.1:6379> lrange mylist 0 -1
1) "0"
2) "a"
3) "b"
4) "c"
127.0.0.1:6379> rpop mylist
"c"
127.0.0.1:6379> lrange mylist 0 -1
1) "0"
2) "a"
3) "b"

补充

blpop、brpop

blpop key timeout

lpop阻塞版本,timeout是阻塞超时时间,timeout=0为永远不阻塞

brpop key timeout

rpop阻塞版本,timeout是阻塞超时时间,timeout=0为永远不阻塞

集合

集合结构

key values

无序、无重复、集合间操作

API

sadd、srem

sadd key element

向集合key添加element(如果element已经存在,添加失败)

srem key element

将集合key中的element移除掉

scard、sismember、srandmember、smembers

scard user:1:follow = 4 #计算集合大小
sismember user:1:follow it = 1(存在) #判断it是否在集合中
srandmember user:1:follow count= his #从集合中随机挑count个元素
spop user:1:follow = sports #从集合中随机弹出一个元素
smembers user:1:follow = music his sports it #获取集合所有元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
127.0.0.1:6379> sadd user:1:follow it news his sports
(integer) 4
127.0.0.1:6379> smembers user:1:follow
1) "it"
2) "his"
3) "sports"
4) "news"
127.0.0.1:6379> spop user:1:follow
"it"
127.0.0.1:6379> smembers user:1:follow
1) "his"
2) "sports"
3) "news"
127.0.0.1:6379> scard user:1:follow
(integer) 3
127.0.0.1:6379> sismember user:1:follow entertainment
(integer) 0

sdiff、sinter、sunion

sdiff user:1:follow user:2:follow = music his #差集
sinter user:1:follow user:2:follow = it sports #交集
sunion user:1:follow user:2:follow = it music his sports news ent #并集
sdiff|sinter|suion + store destkey .. #将差集、交集、并集结果保存在destkey中

有序集合

有序集合结构

key score value

集合 有序集合
无重复元素 无重复元素
无序 有序
element element+score
列表 有序集合
可以重复元素 无重复元素
有序 有序
element element+score

API

zadd

zadd key score element(可以是多对)

添加score和element

zrem

zrem key element(可以是多个)

删除元素

zscore

zscore key element

返回元素的分数

zincrby

zincrby key increScore element

增加或减少元素的分数

zcard

zcard key

返回元素的总个数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
127.0.0.1:6379> zadd player:rank 100 ronaldo 900 messi 800 c-ronaldo 600 kaka
(integer) 4
127.0.0.1:6379> zscore player:rank kaka
"600"
127.0.0.1:6379> zcard player:rank
(integer) 4
127.0.0.1:6379> zrank player:rank ronaldo
(integer) 0
127.0.0.1:6379> zrem player:rank messi
(integer) 1
127.0.0.1:6379> zrange player:rank 0 -1 withscores
1) "ronaldo"
2) "100"
3) "kaka"
4) "600"
5) "c-ronaldo"
6) "800"

zrange、zrangebyscore、zcount

zrange key start end [WITHSCORES]

返回指定索引范围内的升序元素[分值]

zrangebyscore key minScore maxScore[WITHSCORES]

返回指定分数范围内的升序元素[分值]

zcount key minScore maxScore

返回有序集合内在指定分数范围内的个数

zremrangebyrank、zremrangebyscore

zremrangebyrank key start end

删除指定排名内的升序元素

zremrangebyscore key minScore maxScore

删除指定分数内的升序元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
127.0.0.1:6379> zadd player:rank 1000 ronaldo 900 messi 800 c-ronaldo 600 kaka
(integer) 4
127.0.0.1:6379> zrange player:rank 0 -1
1) "kaka"
2) "c-ronaldo"
3) "messi"
4) "ronaldo"
127.0.0.1:6379> zcount player:rank 700 901
(integer) 2
127.0.0.1:6379> zrangebyscore player:rank 700 901
1) "c-ronaldo"
2) "messi"
127.0.0.1:6379> zremrangebyrank player:rank 0 1
(integer) 2
127.0.0.1:6379> zrange player:rank 0 -1 withscores
1) "messi"
2) "900"
3) "ronaldo"
4) "1000"

总结

操作类型 命令
基本操作 zadd zrem zcard
zincrby zscore
范围操作 zrange
zrangebyscore
zcount
zremrangebyrank
集合操作 zunionstore
zinterstore

Jedis

Maven依赖

1
2
3
4
5
6
7
<dependency>
<groupId> redis.clients </groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
<type>jar</type>
<scope>compile</scope>
</dependency>

Jedis直连

  1. 生成一个Jedis对象, 这个对象负责和指定Redis节点进行通信

    Jedis jedis = new Jedis(“127.0.0.1”, 6379);

  2. jedis执行set操作

    jedis. set(“hello”, “world”);

  3. jedis执行get操作,value= “world”

    String value = jedis.get(“hello”);

Jedis(String host, int port, int connectionTimeout, int so Timeout)

  • host : Redis节点的所在机器的IP
  • port : Redis节点的端口
  • connectionTimeout : 客户端连接超时
  • soTimeou : 客户端读写超时