侧边栏壁纸
博主头像
王一川博主等级

努力成为一个不会前端的全栈工程师

  • 累计撰写 70 篇文章
  • 累计创建 20 个标签
  • 累计收到 39 条评论

目 录CONTENT

文章目录

Redis

王一川
2021-08-03 / 0 评论 / 0 点赞 / 5,510 阅读 / 10,318 字
温馨提示:
本文最后更新于 2021-08-13,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

一、redis 安装

1.1 编译 redis

检验 gcc

gcc -v

安装 gcc

sudo apt-get install gcc g++

下载 redis 安装包 [redis.io] 并解压

tar -zxvf redis-6.2.2.tar.gz

进入解压目录编译源文件

cd cd redis-6.2.2;make

等待编译结束

iShot2021-04-21 15.35.08

安装 redis 到指定目录

make PREFIX=~/redis install

iShot2021-04-21 15.41.18

复制源文件中的 redis.conf 到安装目录

cp ../redis-6.2.2/redis.conf ./

这时候除了安装目录,其他文件都可以删了

1.2 启动 redis

redis 有两种启动方式:前台启动和后台启动

前台启动[不推荐]:运行 redis-server

iShot2021-04-21 15.45.43

启动成功后该 session 将阻塞无法使用

后台启动[推荐]:修改配置文件 redis.conf 将 daemonize no 改为 daemonize yes 后,指定配置文件启动

./redis-server ../redis.conf

1.3 停止 redis

./redis-cli shutdown

ps -ef | grep redis
kill -9 PID

推荐第一种,更加优雅

二、redis 操作

2.1 操作 key

查看所有符合条件的 key,支持正则,返回符合条件的 key,没有返回 (empty array)

keys pattern

判断某些 key 是否存在,返回存在的个数

exists key [key ...]

查看某个 key 的数据类型,返回这个 key 对应 value 的数据类型

type key

删除某个 key,删除返回 1,未删除(key不存在)返回 0

del key

异步删除某个 key,仅将 key 从 keyspace 中删除,真正的数据删除在后续异步操作中

unlink key

给 key 设置过期时间,单位秒,到达指定时间 key 自动删除,设置成功返回 1,key 不存在返回 -1

expire key seconds

查看 key 的过期时间,返回将要过期的秒数,-1表示永不过期,-2表示已过期或不存在

ttl key

切换数据库,redis 默认 16 个数据库 0-15,默认为 0,指定数据库索引即可

select index

查看当前数据库的 key 的个数

dbsize

清空当前数据库

flushdb

清空所有数据库

flushall

2.2 操作 string

  1. string 是 redis 最基础的类型且二进制安全,因此 string 可以包含任意数据,包括视频、图片等;redis 的 string 最多可以是 512M
  2. redis 的 string 是可以修改的字符串,内部结构采用预分配冗余空间的方式减少内存的频繁分配,类型 java 的 ArrayList 扩容,当 string 长度小于 1M 时,每次增加现有空间,当长度大于 1M 时,每次增加 1M

设置一个键值对,多次设置会覆盖原有 key 的 value

set key value

设置一个键值对,当 key 不存在是设置成功返回 1,key 存在设置失败,返回 -1

setnx key value

设置一个键值对,同时指定过期时间,单位秒,多次设置覆盖原有 key 的 value,同时刷新过期时间

setex key seconds value

获取 key 对应的 value,key 不存在返回 (nil)

get key

将给定的 value 追加到原有 key 的 value 的末尾,返回最终 value 的长度,若 key 不存在则新建

append key value

将 key 存储的值自增 1,若 value 不是数字类型则报错,返回自增后的数值

incr key

指定自增步长,功能类似 incr

incrby key increment

将 key 存储的值自减 1,若 value 不是数字类型则报错,返回自减后的数值

decr key

指定自减步长,功能类似 decr

decrby key increment

同时设置一个或多个键值对

mset key value [key value ...]

同时获取一个或多个键值对

mget key [key ...]

同时设置多个不存在的键值对,具有原子性,一个设置失败全部设置失败,成功返回 1,失败返回 0

msetnx key value [key value ...]

获取 key 的返回值,类型 substring,下标从 0 开始,包含开始和结束位置,end = -1 表示全部

getrange key start end

获取旧值同时设置新值,若 key 不存在,返回 (nil) 同时设置新值

getset key value

2.3 操作 list

  1. redis 的 list 单键多值,底层为双向链表,对两端操作性很高,查询效率相对较低
  2. 当 list 元素较少时会使用一块连续的内存存储,即 ziplist
  3. 当 list 元素较多时会将若干个ziplist通过双向指针连接起来形成链表,即 quicklist
  4. redis 的 list 即满足了快速插入删除性能,也不会出现太大的空间冗余

从左边插入一个或多个值,返回当前 key 的元素个数

lpush key element [element ...]

注:插入多值想清楚顺序,如 lpush k1 v1 v2 v3,则获取 k1 是 v3 v2 v1,即依次放到最左边

从右边插入一个或多个值,返回当前 key 的元素个数

rpush key element [element ...]

按照索引获取元素,顺序是从左到右,key 不存在返回 (empty array)

lrange key start stop

注:索引从 0 开始,获取 list 所有值:lrange k1 0 -1

从左边吐出一个或多个值,返回吐出的值,key 不存在返回 (nil)

lpop key [count]

注:默认吐出一个;吐出的值会从原 list 中删除,当 key 全部吐完这个 key 就不存在了

从右边吐出一个或多个值,返回吐出的值,key 不存在返回 (nil)

rpop key [count]

从一个 list 右边吐出一个值,插入到另一个 list 的左边,返回吐出的值,待吐出的 key 不存在返回(nil),命令不会产生效果,待插入的 key 不存在会被创建

rpoplpush source destination

注:源和目标 list 可以是同一个,效果即为把右边的值放到左边

获取 list 长度,若 key 不存在返回 0

llen key

按照索引获取元素(不删除),顺序从左到右,返回索引对应的值,若 key 不存在或索引越界返回(nil)

lindex key index

在 list 中某个值的前面或者后面插入一个新值,返回最终 list 长度,若 key 或者 pivot 不存在返回 -1

linsert key before|after pivot element

注:若 list 有多个 piovt,只会对第一个元素生效,顺序从左到右

从左边开始顺序删除若干个特定值,返回删除的元素个数,若 key 不存在或待删除元素不存在返回 -1

lrem key count element

设置指定下标元素的值,成功返回 ok,key 不存在返回 err no such key,索引越界返回 err index out of range

lset key index elemet

2.4 操作 hash

  1. redis 的 hash 是一个键值对集合,即 value 对应一个 field 和 val 的映射表
  2. 类比 java:key - Map<String,String>,因此 redis 的 hash 适合存储对象
  3. 当 hash 数据量小使用ziplist(压缩列表),数据量大使用hashtable(哈希表)

设置 hash 的值,返回新增的 value 的映射个数,多次执行回覆盖原值返回 0

hset key field value [field value]

获取 hash 的值,若 key 或 field 不存在返回 (nil)

hget key field

获取 hash 的所有值,格式为 field 后面紧跟它的 value

hgetall key

判断指定 key 的 field 是否存在,存在返回 1,反之返回 0

hexists key field

获取 hash 的所有 field

hkeys key

获取 hash 的所有 value

hvals key

给 hash 的 field 对应的 value 自增 n,若 key 或 field 不存在新建并返回 value 值,若 对应的 value 不是数字类型报错

hincrby key field increment

当指定的 field 不存在时设置,存在不设置

hsetnx key field value

2.5 操作 set

  1. redis 的 set 提供了和 list 一样的列表功能,区别于 list 的是 set 元素无序不可重复
  2. redis 的 set 底层是 hash,所有的 value 指向同一个内部值,类比 java 的 HashSet
  3. 底层结构决定 set 添加、删除、查找的复杂度都是O(1)

设置 set 的值,已存在的值会被忽略,返回设置成功的元素个数

sadd key member [member ...]

获取 set 所有值,key 不存在返回(empty array)

smembers key

判断 set 中是否存在某个值。存在返回 1,不存在返回 -1

sismember key member

获取 set 元素个数,key 不存在返回 0

scard key

删除 set 指定元素,返回删除成功的个数

srem key member [member ...]

从 set 中随机吐出(删除)若干个元素,全部吐完 key 自动删除,返回被删除的元素

spop key [count]

注:默认删除一个,可指定数量

从 set 随机取 n 个元素(不删除)

srandmember key [count]

把一个集合的一个值移动到另一个集合中,若 source 的不存在命令不生效,若 destination 不存在会被创建,成功返回 1,反之返回 0

smove source destination member

取若干 set 的交集

sinter key [key ...]

取若干 set 的并集

sunion key [key ...]

取若干 set 的差集

sdiff key [key ...]

2.6 操作 zset

  1. redis 的 zset 提供了和 set 想似的功能,但每个内部成员都维护一个分数 score
  2. zset 的 score 被用来按照从低到高的方式排序集合的成员
  3. zset 的底层结构为 hash,每个 field 对应的 value 存储分数 score
  4. 为了实现根据 socre 的返回获取元素,zset 使用了跳跃表给元素根据 score 排序

设置 zset 的值,返回新增的元素个数,若 value 存在则覆盖原值

zadd key source member [source member ...]

返回 zset 下标在 start 和 stop 之间的元素,下标从 0 开始且为闭区间;元素按照分数从小到大排序

zrange key start stop [withscores]

注:withscores:显示元素的分数 score

返回 zset 下标在 start 和 stop 之间的元素,下标从 0 开始且为闭区间;元素按照分数从大到小排序

zrevrange key start stop [withscores]

返回 zset 分数在 min 和 max 之间的元素,为闭区间;元素按照分数从小到大排序

zrangebyscore key min max [withscores]

注:指定全体技巧:min -> -inf ; max -> +inf,下同

返回 zset 分数在 min 和 max 之间的元素,为闭区间;元素按照分数从大到小排序

zrevrangebyscore key max min [withscores]

统计 zset 指定分数范围内的元素个数

zcount key min max

删除 zset 指定元素,返回删除成功个数

zrem key member [member ...]

返回 zset 指定元素的排名,排名从小到大从 0 开始,指定的元素不存在返回 (nil)

zrank key member

2.7 操作 bitmaps

  1. bitmaps 本身不是一种数据类型,是可以提供位运算的字符串,可以理解为只能存储 0、1 的数组
  2. 其中数据的下标在 bitmaps 中称为偏移量
  3. 合理的使用位运算可以有效的提高内存使用率和开发效率

bitmaps 本身就是一个 string

127.0.0.1:6379> type k1
string

设置 bitmaps 某个偏移量的值

setbit key offset value

偏移量从 0 开始,且 string 最大容量为 512M,因此偏移量最大值为 8*512*1024*1024-1 = 2^32-1,减一是因为 c 语言中字符串末尾需要存储一个分隔符

获取 bitmaps 某个偏移量的值

getbit key offset

获取指定范围内的偏移量中 1 的个数

bitcount key [start end]

不指定范围返回全部 1 的个数

多个 bitmaps 做交、并、差等操作

bitop operator destkey key [key ...]

operator可以取:and(交集)、or(并集)、not(非)、xor(异或),并将结果写入 destkey中

应用场景

用户签到

用户当天签到记 1,未签到记 0(默认就为 0),考虑使用 bitmaps 存储,其中

key :用户id_年份

offset :今天是一年中的第几天

活跃用户

记录所有注册用户每日活跃情况,其中

key:日期

offset:用户 id

每日用户活跃在对该用户的 id 的 offset 设置为 1

对于上述两个情况,可以做很多分析,如:

  1. 利用 bitcount 的返回可以判断该用户是否连续签到
  2. 利用 bitop 的 and 可以判断哪些用户在指定天数活跃

对于上述场景使用 bitmaps 比 string 的比较

  1. 若每日活跃用户很多,使用 bitmaps 存储效率极高,每个用户只需一位保存
  2. 若每日活跃用户较少,使用 bitmaps 不合理,存储大量无意义数据 0,此时使用 set/list 更好

2.8 操作 hyperloglog

  1. hyperloglog 是 redis 用来做基数统计的算法,优点在于:输入的元素数量或体积非常大时,计算基数所需要的空间总是固定的,并且非常小
  2. hyperloglog 只会根据输入元素来计算基数,而不会存储输入元素本身,对于要求元素精度的则不适用

添加指定元素到 hyperloglog 中,执行后若基数发生变化返回 1,否则返回 0

pfadd key element [element ...]

计算若干个 hyperloglog 的基数,返回基数

pfcount key [key ...]

合并若干个 hyperloglog,并将结果写入新的 hyperloglog 中

pfmerge destkey sourcekey [sourcekey ...]

2.9 操作 geospatial

  1. 支持对地图经纬度的操作
  2. 本质是一个 zset

添加地理信息(经度、纬度、城市)

geoadd key longitude latitude member [longitude latitude member ...]

注:南极北极无法添加;有效的经度[-180,180];有效的纬度[-85.05112878,85.05112878];当输入的经纬度超出范围会报错

获取地理信息,返回位置的经纬度坐标

geopos key member [member ...]

获取两个位置的直线距离

geodist key member1 member2 [m|km|ft|mi]

注:m 表示米(默认),km 表示千米;ft 表示英尺;mi 表示英里

给定一个位置,返回该位置附近的位置

georadius key longitude latitude radius m|km|ft|mi

三、redis 配置

3.1 Unit

# 1k => 1000 bytes
# 1kb => 1024 bytes
# 1m => 1000000 bytes
# 1mb => 1024*1024 bytes
# 1g => 1000000000 bytes
# 1gb => 1024*1024*1024 bytes

配置文件 unit 单位对大小写不敏感

3.2 INCLUDES

# include /path/to/local.conf
# include /path/to/other.conf

类似 jsp 中的 include,多实例的情况可以把公用的配置文件提取出来

3.3 MODULES

# loadmodule /path/to/my_module.so
# loadmodule /path/to/other_module.so

需要加载的 redis 模块

3.4 NETWORK

bind 127.0.0.1 -::1

默认情况 bind=127.0.0.1 只接受本机的访问请求,不写表示接受任意 ip 访问

若开启了 protected-mode,接受任意 ip 访问且没有设置密码时,redis 同样只接受本机访问请求

protected-mode yes

本机访问模式,建议设置为 no

port 6379

redis 端口号

tcp-backlog 511

设置 tcp 的backlog,backlog 其实是一个连接队列,backlog 队列总和 = 未完成三次握手队列 + 已经完成三次握手队列。在高并发环境下你需要一个高 backlog 值来避免慢客户端连接问题。

注意 Linux 内核会将这个值减小到 /proc/sys/net/core/somaxconn 的值(128),所以需要确认增大 /proc/sys/net/core/somaxconn 和 /proc/sys/net/ipv4/tcp_max_syn_backlog(128)两个值来达到想要的效果

timeout 0

一个空闲的客户端维持多久会关闭,0 表示永不关闭

tcp-keepalive 300

对客户端的心跳检测,每 n 秒检测一次,0 表示不检测

3.5 GENERAL

daemonize yes

守护进程,后台启动

pidfile /var/run/redis_6379.pid

pid 文件存放位置,每一个实例都会产生一个不同的 pid 文件

loglevel notice

设置日志等级

debug (a lot of information, useful for development/testing)

verbose (many rarely useful info, but not a mess like the debug level)

notice (moderately verbose, what you want in production probably)

warning (only very important / critical messages are logged)

logfile ""

设置日志文件名,可以为空,也可以设置到 /dev/null 丢弃

databases 16

设置 redis 数据库的个数

3.6 SECURITY

# requirepass foobared

设置密码,客户端通过 auth xxx 输入密码连接

3.7 LIMITS

# maxclients 10000

redis 最大连接数,若超过此限制会拒接新的连接并抛出:max number of clients reached

# maxmemory <bytes>
  1. 建议设置,否则内存占满,造成服务器宕机
  2. 设置 redis 可使用的内存,一旦内存达到上限,redis 会根据 maxmemory-policy 指定的规则移除数据
# maxmemory-policy noeviction

设置移除数据的策略

volatile-lru:使用 LRU 算法移除 key,只对设置了过期时间的键;(最近最少使用)

allkeys-lru:在所有集合 key 中,使用 LRU 算法移除 key

volatile-random:在过期集合中移除随机的 key,只对设置了过期时间的键

allkeys-random:在所有集合 key 中,移除随机的 key

volatile-ttl:移除那些 TTL 值最小的 key,即那些最近要过期的 key

noeviction:不进行移除。针对写操作,只是返回错误信息

# maxmemory-samples 5

设置样本数量,LRU 算法和最小TTL 算法都并非是精确的算法,而是估算值,所以你可以设置样本的大小,redis 默认会检查这么多个 key 并选择其中 LRU 的那个。数值越小约不精确,但开销越小

四、redis 客户端

  1. Jedis 是 redis 的 java 实现的客户端,其 API提供了比较全面的 redis 命令支持
  2. Jedis 中的方法调用是比较底层的暴露的 redis 的 API,即 Jedis 中 Java 方法基本和 redis 保持一致
  3. Jedis 使用阻塞的 I/O,其方法调用都是同步的,不支持异步
  4. Jedis 客户端不是线程安全,所以需要使用连接池

4.1 pom 文件

<dependency>
  <groupId>redis.clients</groupId>
  <artifactId>jedis</artifactId>
  <version>3.6.0</version>
</dependency>

4.2 测试用例

import redis.clients.jedis.Jedis;

public class JedisDemo {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("localhost", 6379);

        String ping = jedis.ping();

        System.out.println(ping);

        jedis.close();
    }
}

注:测试失败检查:关闭防火墙或开放 redis 端口;注释掉 bind;关闭 protected-mode

jedis 的 API 和 redis 命令基本一致,通过 Jedis 对象调用即可

五、redis 事务

  1. All the commands in a transaction are serialized and executed sequentially. It can never happen that a request issued by another client is served in the middle of the execution of a Redis transaction. This guarantees that the commands are executed as a single isolated operation.
  2. Redis 事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序的执行。事务在执行过程中,不会被其他客户端发送来的命令请求所打断
  3. Redis 事务的主要作用就是串联多个命令防止别的命令插队

5.1 事务操作

5.1.1 multi、exec、discard

redis 关于事务的命令有:multi、exec、discard

multi 相当于开启事务,multi 之后的所有命令将会进入命令队列中,但不会执行

exec 依次执行命令队列中的所有命令,执行完本次事务结束

discard 相当于回滚,中断事务,命令队列中的命令全部丢弃,事务结束

正常事务

127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k1 1
QUEUED
127.0.0.1:6379(TX)> set k2 2
QUEUED
127.0.0.1:6379(TX)> exec
1) OK
2) OK
127.0.0.1:6379> get k1
"1"
127.0.0.1:6379> get k2
"2"

中断事务

127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k1 1
QUEUED
127.0.0.1:6379(TX)> set k2 2
QUEUED
127.0.0.1:6379(TX)> discard
OK
127.0.0.1:6379> keys *
(empty array)

5.1.2 事务错误处理

redis 事务可以分为两个阶段:组队阶段执行阶段,在这两个阶段出现错误的处理方式是不同的

组队阶段出错则整个命令队列都会被取消;执行阶段出错时出错的命令不会执行,其他命令正常执行不会回滚

演示:组队阶段出错

127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k1 1
QUEUED
127.0.0.1:6379(TX)> set k2
(error) ERR wrong number of arguments for 'set' command
127.0.0.1:6379(TX)> exec
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> keys *
(empty array)

演示:执行阶段出错

127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k1 1
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> incr k1
QUEUED
127.0.0.1:6379(TX)> incr k2
QUEUED
127.0.0.1:6379(TX)> exec
1) OK
2) OK
3) (integer) 2
4) (error) ERR value is not an integer or out of range
127.0.0.1:6379> keys *
1) "k2"
2) "k1"
127.0.0.1:6379> get k1
"2"
127.0.0.1:6379> get k2
"v2"

5.2 事务冲突

工作冲突,尽情期待...

0

评论区