Redis 基础知识点总结

关系型数据库 VS 非关系型数据库(NoSQL)

关系型数据库

我们过去使用的 mysql、Oracle 都属于关系型数据库。关系型数据库的特点是数据表之间可以存在联系,表内每列数据也存在关联,同时支持事务、复杂的锁机制,这样可以支持复杂操作,在查询时也可以很快得到与之相关联的数据,但同时这些也成为限制数据库速度的因素,在存储大数据的表中进行查询修改、拓展表时会格外消耗时间。在过去受硬件水平的限制,系统架构往往比较简单,并发量也比较小,但随着硬件水平的提高,系统变得越拉越庞大,需要存储的数据量也越来越大,很多业务都需要在海量数据中快速查找到所需要的数据,此时靠关系型数据库已经无法满足了,所以提出了非关系型数据库的概念。

 

非关系型数据库

非关系型数据库中的数据间没有关联关系,数据结构简单,在数据的增删改查时速度都比较快,缺点是不能支持复杂的操作,比如事务的ACID、复杂的锁机制,也因为数据间没有关联关系所以在查询符合条件的数据会很快。所以我们通常只使用 NoSQL 来存储一些简单、不需要复杂操作的数据,如某个用户的粉丝数,某个博客的点赞数、字数等。

 

四种聚合模型

 

总结

传统的关系型数据库因为内部功能多,数据间存在关联,导致在数据量过大时操作起来效率比较低。非关系型数据库则与之相反从而得到了很好的性能,在日益要求性能的今天起到了很好的作用,但是因为其不能实现复杂功能,所以对于一些需要复杂操作(读写锁、事务、多表联查等)还是使用关系型数据库,而对于一些简单数据,不需要太复杂操作的可以使用非关系型数据库。

 

Redis 

redis 是一个单线程(底层使用IO多路复用模型)分布式数据库,也是一个典型的 NoSQL,它的执行效率非常高,其原因主要有以下几点:

1、是非关系型数据库,数据结构简单,且没有复杂的关联关系。

2、单线程操作,避免了多线程之间切换和竞争,并通过IO多路复用模型来避免传统 BIO 的低效执行。

3、数据存储在内存,读取时直接从内存中读取。

基础知识

1、在安装后相应的执行命令和配置文件默认在 /usr/local/bin/ 目录下

2、redis 默认有 16个数据库,0-15,默认是0号数据库,可以通过 " select 数据库号"  来切换数据库。数据库个数可以在 redis.conf 中配置。

3、redis 是统一密码管理,默认情况下没有开启密码,可以在配置文件 redis.conf 中配置开启

4、默认端口是 6379。

5、启动服务器:redis-server  配置文件全路径。配置文件可以是自定义的配置文件。启动客户端:redis -cli -p  6379。

 

五大基本数据类型及常用方法

String

最基本的数据类型,虽然为 String 类型,但是其 value 可以为 string 也可以为 int 类型。其可以用于实现计数器,也可以用于进行 json 格式的对象存储。

常用方法:

set / get / del / append / strlen :  设值 / 获值 / 删值 / 末尾添加值 / 获取长度

Incr / decr / incrby key n / decrby key n:  自增 / 自减 / 增加 n / 减去 n

getrange  n1  n2 / setrange  n1  n2  val:  截取下标n1,n2之间的值(从0开始,两边都是闭区间) / 设置下标n1,n2区间的值

setex  key  time  val / setnx  key  val:  设值并指定过期时间(单位为秒) / 在 val 不存在或者已过期时设值

mset / mget / msetnx:  批量(进行设置  /  获值 / 非空设值)

 

List

底层是链表结构,方法名开头的 l 表示 left,r 表示 right。可以用于实现消息队列。

常用方法:

lpush / rpush / lrange n1 n2:  左添 / 右添 / 从左开始截取下标n1,n2之间的内容(0开始,两边都是闭区间)

lpop / rpop:  类似于消息队列和栈的出栈操作,分别是 (左出栈 / 右出栈)

lindex:  从左边计算获取指定下标的值

lrem key n  value:  对 key 对应的 list 数据从左边开始删除 n 个 value

ltrim  key  n1  n2:  获取 key 对应的 list 值,截取 n1 到 n2 之间的值再赋值覆盖当前的 list 值。

rpoplpush  list1  list2:  将list1中的右边尾部数据移到 list2 的左边头部 

lset  key  index  value:  左边开始修改指定索引上的值

linsert  key  before/after  val1  val2:  从左开始,获取第一个 val1,在其 (左 / 右) 插入 val2   

 

Set

和 java 中的 Set 集合一样,唯一无需的结构。可以用来存储好友,然后计算共同好友

常用方法:

sadd / smembers / sismember key val:  添加(可以批量添加) / 显示所有值 / 查看是否存在val

scard key:  获取集合中元素的个数

srem key val:  删除某个元素

scrandmember  key  n:  随机出 n 个元素

spop key:  随机出栈一个元素

smove key1  key2  key1中的某个值val:  将 key1 对应集合中的某个值val 移入 key2 对应的集合中

sdiff  key1  key2...:  获取存在与于key1 对应集合中但不存在后面所有key对应集合中的元素

sinter key1  key2...:  获取存在于 key1 对应的集合中且存在于后面所有key对应集合中的元素

sunion  key1  key2...:  获取存在于 key1 对应的集合中或者存在于后面所有key对应集合中的元素

 

Hash

类似于 java 中的 Map 结构,可以用于存储对象。

常用方法:

hset / hget / hmset / hmget / hgettall  key/ hdel  key  key(hash):  设值 / 获值 / 批量设值 / 批量获取 / 获取 key 对应所有的键值对数据 / 删除 key 对应 hash 结构中的 key(hash) 对应的值。

hlen  key :  获取元素个数

hexists  key  key(hash):  查看 key 对应 hash 结构的 key(hash) 对应的值是否存在

hkeys key / hvals key:  获取 key 对应 hash 结构所有(key 值 / val 值)

hincrby key  key(hash)  val / hincrbyfloat  key  key(hash)  val:  对 key 对应 hash 结构中的 key(hash) 对应的值添加(整数 / 小数) val

hsetnx  key  key(hash)  val:  不存在时赋值

 

ZSet

每个数据关联一个分数,排序时会按分数升序排列,相当与一个有序 Set。可以用于实现各种排行榜。

常用方法:

zadd  key  score  val / zrange  key  n1  n2  withscores:  添加 / 获取所有值

zrangebysorce key score1 score2:  查找 score1 与 score2 之间的数据

zrem key val:  删除某个值

zcard key / zcount key score1  score2 / zrank key value / zscore key value:  获取数据数 / 统计在 score1 与 score2 之间元素的个数 / 获取指定数据所在下标 / 获取指定数据的分数

zrevrant key value:  逆序获取指定值的下标

zrevrangebyscore key score1 score2:  逆序获取 score1 与 score2 之间的数据

 

Key 及其他操作方法

keys  *:  获取所有的key

exists  key:  查看是否存在key,返回1是存在,0不存在

expire key  时间:  为 key 设置过期时间,单位是秒

ttl  key:  查看 key 还有多久过期,-1表示永不过期,-2表示已过期。

type  key:  查看 key 是什么类型

Dbsize:  查看当前数据的 key 数量

Flushdb:  清除当前库中的所有数据

Flushall:  清除所有库中的所有数据

 

配置文件 redis.conf(Linux)

下面列出的各个配置可能存在多出,因为参考了多个版本的配置文件,同时可能存在遗漏,请见谅。修改配置文件的原则是不要动默认的配置文件,应该将默认配置文件复制一份到指定目录,然后去处理,防止修改错误无法恢复。

Units配置大小单位,可以用于自定义一些度量单位,底层单位只支持 bytes,大小写不敏感。

includes:可以来引入其他配置文件

general:

  Daemonize:  是否以守护进程的方式运行,默认为no,也就是服务器窗口关闭后就会关闭服务器,如果需要后台运行可以设置为 yes。

  protected-mode:  保护模式是否开启,默认是 yes。关闭其他任何ip 地址都可以来访问连接,关闭后必须通过 bind 来配置相应的 ip 后,其才能连接。

  Pidfile:  如果 redis 以守护进程的方式运行时,系统就会将这个守护进程的 id 记录下来,记录的位置就是通过 Pidfile 来配置,默认是 /var/run/redis.pid

  Port:  端口端口号

  Tcp-backlog:  设置 tcp 的连接队列长度,其值 = 未完成三次握手队列 + 已完成三次握手队列。默认是 511,如果并发量比较大时可以设置为 2048。

  timeout:  设置最大空闲时间,也就是多久没有操作服务器就会自动断开。默认是0,也就是永不断开。

  Bind:  设置允许访问的 ip 地址,在 protected-mode 为 yes 时使用。

  tcp-keepalive:  服务器会检测客户端是否还在使用,如果一段时间内客户端没有操作,那么 redis 服务器就会释放这条连接,tcp-keepalive 就是设置客户端的最大空闲时间的,默认是0,也就是永不断开,而官方推荐是 60,也就是超过60秒没操作服务器就会回收这一条的连接。

  loglevel:  日志级别。从高到低分别为 warning、notice、verbose、debug。默认是 notice。

  logfile:  日志文件存放位置。

  Syslog-enable:  是否把输出日志保存到日志文件中。默认关闭

  Syslog-ident:  设置日志中的日志标识。

  Syslog-facility:  指定输出 syslog 的设备,可以为 user 或 local0-local7。

  Databases:  设置数据库个数。默认16

  always-show-logo:  是否总是显示 logo,默认 yes。

REPLICATION(主从复制相关):

  一般配置主从复制时,需要自己手动通过 salveof 命令来配置从机,而如果各机器事先就决定好角色,可以直接在配置文件中配置来避免手动配置。

  masterip:  主机的 ip 地址

  masterport:  主机的端口号

  masterpassword:  主机的密码

SNAPAHOTTING快照(RDB相关):

  save :  设置RDB自动备份的时间间隔,save 配置的格式是 "save  时间间隔  次数",默认配置是

    save  900  1

    save  300  10

    save  60  10000

从第一行开始作用分别是在 900s 内执行了一次写操作就会触发一次备份;300s 内执行了 10次写操作就会触发一次备份;60s 内执行了10000次写操作就会触发一次备份。

一般来说使用默认配置就可以,如果想要禁用自动备份可以将其删除或者改成 save ""。

  Stop-writes-on-bgsave-error:  通过bgsave备份出错时,主线程是否继续工作。

  rdbcompression:  是否压缩 rdb文件,需要消耗一些 cpu 资源。压缩会使文件占用空间减小。

  rdbchecknum:  存储快照后是否进行数据校验。如果想要提高执行效率可以关闭。

  dbfilename:  备份的文件名。默认是 dump.rdb。

  dir:  RDB、AOF 备份文件的存储地址。在服务器启动时恢复数据也会在 dir 配置的目录中读取对应的备份文件。默认在执行目录下。可以通过 "config  get  dir" 获取 dir 。

APPEND ONLY MODE追加(AOF相关)

  appendonly:  是否启用AOF。

  appendfilename:  备份文件的文件名,默认为 appendonly.aof。

  appendfsync:  存储策略。共有以下三种。

    always:同步持久化,每次写操作后都会立刻记录到磁盘,性能较差但是数据完整性最好。

    everysec:异步操作,每一秒执行一次记录,可能会有少量数据丢失。

    no:不进行记录。

  No-appendfsync-on-rewrite:  rewrite 时是否执行存储策略(进行存储)。一般使用默认 no 即可,保证数据安全性。

  Auto-aof-rewrite-min-Size:   设置 rewrite 触发的最小基准值。

  Auto-aof-rewrite-percentage:  设置 rewrite 触发的超出百分比基准值。

Security(安全权限相关)

默认情况下,安全权限是关闭的,在客户端连接时不需要输入密码,执行操作也不需要密码,但是如果开启了安全检测,那么所有客户端在执行命令时都需要先执行 "auth 密码" 来验证身份。

可以在配置文件中配置 "requirepass  密码" 来配置密码,也可以在命令行执行 "config  set  requirepass  密码" 来配置,查看密码可以使用 "config  get  requirepass"。如果想要关闭可以直接在命令行执行 "config  set  requirepass "" " 。

limit

  maxclients:  最大连接的客户端数,默认无限制,下同。

  maxmemory:  redis 服务器占用的最大内存

  maxmemory-policy:  redis 数据存储策略。类似与线程池的拒绝策略,就是当存储新数据时内存空间不足执行的操作。策略主要有以下六种。

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

    2)allkeys-lru:使用 LRU 算法移除 key。

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

    4)allkeys-random:移除随机的 key。

    5))volatile-ttl:移除那些 TTL 最小的 key,即那些最先要过期的 key

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

    LRU 算法就是被使用的数据会被移到头部,未使用的就会慢慢向尾部靠近,在移除时从尾部移除。

  maxmemory-samples:  设置样本数。在清除时,因为LRU算法和最小TTL算法都并非是精确的算法,而是估算值,所以我们可以设置一个具体的大小,redis会抽出这个数量的数据并根据算法进行清除。

 

RDB

默认的持久化方式。其本质就是保存当前时刻的数据快照默认生成文件名是 dump.rdb。

默认会在一段时间内自动保存数据,规则就是上面配置文件 RDB 部分 save 的配置。需要注意的是,RDB 的持久化方式分为 save 与 bgsave。

save 是中断当前进程,然后进行持久化操作,等到持久化完成后再继续执行其他操作;

bgsave 是 fork 一个子进程,fork 出来的子进程会拥有当前进程所有的内存数据,然后子进程单独进行持久化操作,不会阻塞当前进程执行。

RDB 持久化触发时机

1、来自配置文件中配置的自动备份,也就是上面说得 save。其实现方式是 bgsave。

2、执行命令 save 或 bgsave。执行 save 就是中断状态来实现的;而 bgsave 则是 fork 子进程来实现,不会中断当前进程执行。

3、执行flushdb、flushall、shutdown 命令后在命令生效前也会先备份一次。其实现方式是 save。

 

RDB 文件恢复

默认情况下只需要将备份文件放在启动目录下然后在启动目录下启动服务器即可。

启动目录指的是启动 redis-server 命令的目录,在备份时会自动备份到该目录下,比如在 /temp/ 启动,那么默认会读取该目录下的 dump.rdb 文件,备份也会在该目录,如果下次启动在 /myredis/ 下,那么也会读取 /myredis/下的 dump.rdb 文件,备份数据也是会在该目录下备份。这个目录也可以自定义,配置参数是 redis.conf 中 dir 参数。

 

如何关闭 RDB 的自动备份

可以在配置文件中将 save 改成 save "",也可以直接执行 " redis-cli config set save ""  "

 

优势

1、执行效率高,适用于大规模数据的备份恢复。自动备份不会影响主线程工作。

2、备份的文件占用空间小。其备份的是数据快照,相对于 AOF 来说文件大小要小一些。

 

劣势

1、可能会造成部分数据丢失。因为是自动备份,所以如果修改的数据量不足以触发自动备份,同时发生断电等异常导致 redis 不能正常关闭,所以也没有触发关闭的备份,那么在上一次备份到异常宕机过程中发生的写操作就会丢失。

2、自动备份通过 fork 进程来执行备份操作,而 fork 进程会将当前进程的内存数据完整的复制一份,所以这个过程占用的空间是原来的 2 倍,可能会导致内存不足。

 

AOF

AOF 是在 RDB 的补充备份方式,其本质是保存执行的每一条写操作(包括flushdb、flushall),所以其产生的备份文件是可以直接阅读的。默认备份文件名是 appendonly.aof,保存位置和 RDB 备份文件一样。因为其保存的是每一条写操作,所以会比较占用 CPU,同时生成的备份文件也比较占空间,所以默认是关闭的。使用时需要在配置文件中将其打开。

AOF 持久化规则

AOF 备份也是采用自动备份,但是备份的频率会比 RDB 要高,其备份方式分为三种:

1、always:同步持久化,每次写操作后都会立刻记录到磁盘,性能较差但是数据完整性最好。

2、everysec:异步操作,每一秒执行一次记录,可能会有少量数据丢失,但是性能更好。

3、no:不进行记录。

 

除此之外,redis 为了防止随着写操作越来越多,AOF 的备份文件越来越大,设置了 rewrite 机制。

Rewrite 机制类似于 RDB 的 bgsave,同样在后台开启一个子进程,其内存数据与当前进程的数据一致,然后将这些数据生成对应的写操作,然后将这些写操作依次一个临时文件,等到全部写入完毕,再将这个临时文件覆盖掉默认备份文件 appendonly.aof,在覆盖过程中 AOF 自动备份会被阻塞。因为 Rewrite 需要消耗额外的 CPU,同时在写入原文件时还会造成阻塞,所以应该避免执行 Rewrite。

 Rewrite触发机制:

1、通过配置文件中配置的规则默认触发。

  1)Auto-aof-rewrite-min-Size:触发重写最小的基准值。默认是 64M。

  2)Auto-aof-rewrite-percentage:触发重写的超出百分比。默认是 100%。

  如果配置按照上面默认配置,那么触发 AOF 自动配置需要当前 AOF 文件超过 64M,同时文件大小达到了上一次 Rewrite 后文件大小的两倍。如果没有 Rewrite 过那么会在达到 64M 后触发第一次。

2、手动通过 " bgrewriteaof " 来触发重写。 

 

AOF 文件恢复

和 RDB 备份文件恢复一样,在默认情况下将备份文件放在启动目录,然后启动服务器即可。

文件修复:因为 AOF 文件是可修改的,如果内部有一些异常操作,那么在下次启动时就会报错,此时可以通过 redis 提供的修复工具来修复备份文件。执行命令 " redis-check-aof  --fix  文件名" 来修复。

 

优势

总体上来说,要比 RDB 备份方式数据完整性要更好,在数据完整性要求高的场景下可以使用 AOF。

而因为 AOF 有两种不同的备份规则,所以在数据完整性最优先、性能可以不考虑的场景可以使用 always 方式;在数据完整性要求比较高,但是也允许少量的数据丢失,但是要求性能也不会差,那么可以选择 everysec。

 

劣势

1、因为保存的是每一步操作,所以执行效率低。

2、虽然引入 rewrite 来避免备份文件过大,但是 rewrite 造成的 CPU 资源消耗加上原本备份的 CPU 资源消耗会比只使用 RDB 要多得多,所以如果不是对数据完整性有特别高的要求建议只使用 RDB。

 

两种持久化总结

1、默认情况下,redis 只使用 RDB 持久化。因为 AOF 会消耗过多的 CPU,同时执行效率低。

2、如果开启了 AOF 持久化,那么在恢复数据时优先使用 AOF 配置文件来恢复,因为 AOF 保存的数据更完整。

3、如果 redis 只用于做缓存,那么可以直接禁用 RDB 和 AOF 的自动持久化。

4、RDB 持久化一般用作数据的定期备份,如果对数据完整性要求没有那么高,那么可以只使用 RDB,同时在配置文件中只保存 "save  900  1" 这条规则,以此来减少不必要的 CPU 消耗。如果需要使用 AOF ,应该调大 Auto-aof-rewrite-min-Size 来避免频繁的 Rewrite。

 

Redis 事务

mysql 中的事务拥有 ACID 特性,即原子性、一致性、可见性、持久性。那么 Redis 的事务呢? Redis 的事务拥有隔离性,但是不包证原子性。并且其没有隔离级别的概念,也就是说它不像 mysql 中执行了操作,但是因为隔离级别的影响而导致 " 操作未执行 " 的假象。

基本操作

开启事务:multi;

提交事务:exec;

放弃事务:discard;

事务执行的流程是 " 开启事务--->操作入队---->提交事务执行所有操作",下面的截图就是典型的事务执行过程

如果在提交之前想中断此次事务,可以通过 "discard" 来取消当前事务。 

 

特点

1、隔离性:事务的执行不会被其他客户端的操作打断。

2、不保证原子性:如果事务中的某一条操作执行失败,那么其不会影响该事务中的其他操作。

3、在事务提交时,如果某个操作有异常(操作本身的格式有问题,在入队时就报错,也就是编译异常),那么这个事务在提交后不会生效,内部的所有操作都不会生效。如下图。

 

4、在事务提交时,如果某个操作在入队时没有异常,在提交时发生异常,那么这个操作不会影响其他操作的执行。

 

除此之外,Redis 还可以通过 "watch  key" 来对指定的数据设置乐观锁,此后如果其他会话对该数据操作后,当前会话执行的事务就会被中断。

 

Redis 的发布订阅

虽然这模块功能一般是由消息队列来实现,但是如果是简单的 "发布-订阅" 操作通过 redis 也是可以实现的。

相关操作

1、订阅一个或多个:SUBSCRIBE  订阅名1  订阅名2 ...

2、消息发布: PUBLISH  订阅名   消息内容

  实现如下:

3、订阅通配符: PUBLISH  通配符*

  实现如下:

 

主从复制

在操作量较大时,一台 redis 服务器往往不能满足需求,所以需要搭建 redis 服务器集群,而实现的方式一般是搭建 "主从复制" 的服务器模式,其本质就是主机来处理写操作,从机处理读操作。

配置

首先,要知道的是每台服务器启动后,默认就是 master,也就是主。所以我们只需要去配置 salve(从机),配置方式就是在需要成为从机的客户端上执行 " slaveof  主库ip  主库端口 ",在执行后,可以通过 " info  replication"来查看当前服务器的状态。

在完成配置后,在主机上进行写操作后从机上就可以进行对应的读操作。

 

 

同步原理 

在从机与主机完成交互关系后,主机就会收到从机发送的一条 sync 命令,这条命令会使 master 启动后台的存盘进程,等到进程存盘完成后,就会将整个数据文件发送给 slave,slave 接收到数据然后加载到内存,这种所有数据全部复制给 slave 叫做 "全量复制"。而后续 master 进行写操作,相关的写操作会依次复制传给 slave,这种附加的复制叫做 "增量复制"

 

注意细节

配置上:

  在进行主从复制时,需要将配置文件拷贝服务器台数的数量,然后需要修改相应配置文件,打开守护模式,修改其端口号、pid 文件名字、Log 文件名、RDB 备份文件名,如果使用了 AOF 还需要修改 AOF 备份文件名。然后再启动服务器并指定相应的配置文件。如果一开始就确定主从机关系也可以通过配置 masterip、masterport 来避免手动配置。

执行时:

  1、默认情况下 master 用于处理写操作, slave 用于处理读操作。

  2、主机宕机后,从机会原地待命(还是不能执行写操作可以执行读操作), 等待 master 重新连接后又恢复正常。

  3、当一台 slave 成为另外一台 slave 的 " master "后,其身份还是 slave,不能执行写操作。

  4、从机断开后需要重新通过 " slaveof " 来连接成为从机,但如果配置进配置文件则不需要。

  5、使用 " salveof no  one " 可以让当前从机退出关联,重新成为 "master" 状态。

 

不足

在 master 向 slave 复制数据时会有一定的延迟,这种在数据量小的情况下不会有明显感觉,但是在操作数多的情况下,或者在 slave 服务器较多时,在 slave 读取的数据就不能保证是最新的值了。

 

哨兵模式

对于上面的配置方式是有明显缺陷的,如果 master 出现故障宕机,那么此系统就会无法正常工作,如果是 "一主二从" ,那么我们完全可以在 master 宕机后从两台 slave 中选择一台成为新的 master,以此来保证系统的正常执行。" 哨兵" 就是干这件事的,通过它可以在 master 宕机后自动从剩下的 slave 中选择一台成为新的 master。

配置

1、创建文件 sentinel.conf 。在文件中编写哨兵 "监视" 的服务器信息:" sentinel  monitor  数据库名字(自定义)   数据库所在ip   端口号  1 " 。 结尾的 1 表示投票数,也就是在 master 宕机后,哨兵会对剩下的 slave 进行投票,得票数多的成为下一个 master。而总票数就是 1。为了不让每台服务器得到的票数相同就设为 1。一个哨兵可以监视多个 master,也就是一个文件中可以配置多个。

2、另开一个窗口执行 " Redis-sentinel  sentinel.conf所在目录/sentinel.conf " 。

 

注意细节

1、在主机宕机从机成为新的 master后,前主机重新连接,那么其会被哨兵分配成 slave 执行读操作。

2、当前说的是配置一个哨兵,如果这个哨兵宕机,那么就存在着隐患,所以一般在项目中会搭建哨兵集群,来避免哨兵的宕机,同时哨兵搭建配置参数并没有这么少,这里展现的只是核心配置,如有其他要求需要另行配置。

 

SpringBoot 中的使用

在 springboot2.0 之前,底层默认使用的是 jedis,而 2.0 以后变成了 lettuce,这是因为 jedis 采用的是直连,当多个线程操作时,是不安全的。此时可以通过 jedis 的连接池来避免线程不安全,但是在执行时还是会比较慢。而 lettuce 底层使用的是 netty,实例可以在多个线程中共享,不会发生线程不安全的情况。

乱码问题

在使用 redisTemplate 将数据存入 redis 后往往会发现在 redis 客户端中读取会乱码,这是为什么?

redis 内部维护的 redisTemplate 底层使用的是 JDK 序列化器,在 redis 中以二进制形式保存,所以我们在客户端直接读取的是二进制数据,相关源码可以看下面代码

// RedisAutoConfiguration.class
    
    @Bean
    @ConditionalOnMissingBean(name = "redisTemplate")
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
            throws UnknownHostException {
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }   



// RedisTemplate.class

 public void afterPropertiesSet() {
        super.afterPropertiesSet();
        boolean defaultUsed = false;
        if (this.defaultSerializer == null) {
            this.defaultSerializer = new JdkSerializationRedisSerializer(this.classLoader != null ? this.classLoader : this.getClass().getClassLoader());
        }

        if (this.enableDefaultSerializer) {
            if (this.keySerializer == null) {
                this.keySerializer = this.defaultSerializer;
                defaultUsed = true;
            }

            if (this.valueSerializer == null) {
                this.valueSerializer = this.defaultSerializer;
                defaultUsed = true;
            }

            if (this.hashKeySerializer == null) {
                this.hashKeySerializer = this.defaultSerializer;
                defaultUsed = true;
            }

            if (this.hashValueSerializer == null) {
                this.hashValueSerializer = this.defaultSerializer;
                defaultUsed = true;
            }
        }
        ...
    }

关于 redis 的序列化器有以下几种:

我们着重看一下常用的几种:

1、JdkSerializationRedisSerializer。RedisTemplate默认的序列化器,存储的对象必须实现Serializable接口,不需要指定对象类型信息,在redis中以二进制格式来保存,不可读,且转化后的二进制数据往往比json数据要大。

2、StringRedisSerializer。已String类型进行保存,不需要指定,不需要实现Serializable接口

3、Jackson2JsonRedisSerializer。需要指定序列化对象的类型,不需要实现Serializable接口

4、GenericJackson2JsonRedisSerializer。不需要指定序列化对象类型。不需要实现Serializable接口,与 Jackson2JsonRedisSerializer 区别是其保存的对象数据虽然也是 Json 格式的,但是会显示存储对象的类型,以及元素对象所在的类路径。具体可以百度。

redisTemplate 默认 key 与 value 使用的都是 JDK 序列化器,我们可以自定义一个 redisTemplate 组件来覆盖默认的,key 可以使用 String 序列化器,value 使用 Jackson2 序列化器。代码如下:

@Configuration
public class RedisConfig {

    @Bean
    @SuppressWarnings("all")
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
        template.setConnectionFactory(factory);
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();

        // key采用String的序列化方式
        template.setKeySerializer(stringRedisSerializer);
        // hash的key也采用String的序列化方式
        template.setHashKeySerializer(stringRedisSerializer);
        // value序列化方式采用jackson
        template.setValueSerializer(jackson2JsonRedisSerializer);
        // hash的value序列化方式采用jackson
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();

        return template;
    }
}

同时如果要存储的数据就是字符串类型的,那么也可以直接使用 redisTemplate,其 key 与 value 都是使用 String 序列化器。

 

常用方法

redisTemplate.opsForValue();//操作字符串
redisTemplate.opsForHash();//操作hash
redisTemplate.opsForList();//操作list
redisTemplate.opsForSet();//操作set
redisTemplate.opsForZSet();//操作有序set
 
redistempalate.boundValueOps;
redistempalate.boundSetOps;
redistempalate.boundListOps;
redistempalate.boundHashOps;
redistempalate.boundZSetOps;
//两者区别:ops就相当于创建一个operator,前者是通过一个operator来执行各个数据类型的操作,后者是选中数据类型再为这个类型来创建一个operator,
//也就是前者是一个operator执行多种数据,后者是一个operator操作一种数据



// 通过 connection 对象来执行数据库相关的操作
RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
connection.flushDb();  
connection.flushAll();

 

posted on 2020-12-19 12:42  萌新J  阅读(717)  评论(2编辑  收藏