Redis笔记

Redis

1.Redis概念

​ Redis(Remote Dictionary Server ),即远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。特点:速度快.从2010年3月15日起,Redis的开发工作由VMware主持。从2013年5月开始,Redis的开发由Pivotal赞助。

​ 由介绍可知,Redis是一种Key-Value存储类型的数据库.所以在Reids数据库中,每一个Value都有对应的一个Key.

2.Redis入门

1.下载安装:Windos下的redis下载:https://github.com/microsoftarchive/redis/releases/tag/win-3.2.100 下载里面的压缩文件

2.下载后解压,看到整个目录是这样的:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NiM505VW-1633012690009)(D:\学习\typore代码笔记\Redis\图片\image-20210729190857906-16275569420411.png)]

2.我们首先打开redis-server.exe,开启redis服务.我们可以看到redis的服务端口为6379

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b1LhA24R-1633012690012)(D:\学习\typore代码笔记\Redis\图片\image-20210729191103725-16275570656213.png)]

3.我们打开目录中的redis-cli.exe,输入ping指令,得到pong后即表示连接成功

127.0.0.1:6379> ping
PONG

3.Redis基础语法以及五大类型的输入

Redis基础语法

Redis中,一共有16个数据库可以供我们使用(下标为从0-15)

  • 选择一个数据库 select

    
    127.0.0.1:6379> select 0 
    OK
    
  • 对数据库进行插入值的操作: set

    127.0.0.1:6379> set key1 hello1 #插入值操作
    OK
    
  • 查看当前库所有的key: keys *

    127.0.0.1:6379> keys *
    1) "key1"
    
  • 判断某个键是否存在: exists

    127.0.0.1:6379> exists key1
    (integer) 1
    
  • 查看key的类型: type

    127.0.0.1:6379> type key1
    string
    
  • 为key设置过期时间(单位:S): expire

    127.0.0.1:6379> expire key1 10
    (integer) 1
    
  • 查看某个key的过期时间(-1表示永不过期,-2表示已过期,返回其他数字表示距离过期时间): ttl

    127.0.0.1:6379> ttl key1
    (integer) -2
    
  • 查看当前数据库key的数量: dbsize

    127.0.0.1:6379> dbsize
    (integer) 1
    
  • 删除某个键: del

    127.0.0.1:6379> del key1
    (integer) 1
    
  • 清空当前库: flushdb

    127.0.0.1:6379> flushdb
    OK
    

String类型的输入

  • 输入和获取: set``````get

    127.0.0.1:6379> set k1 v1
    OK
    127.0.0.1:6379> get k1
    "v1"
    
  • 输入和获取(多个)

    127.0.0.1:6379> mset a1 a b1 b c1 c d1 d
    OK
    127.0.0.1:6379> mget a1 b1 c1 d1
    1) "a"
    2) "b"
    3) "c"
    4) "d"
    
  • 将特定的value值追加到原值的末尾: append

    127.0.0.1:6379> append k1 v2
    (integer) 4
    127.0.0.1:6379> get k1
    "v1v2"
    
  • 获取键对应的值的长度: strlen

    127.0.0.1:6379> strlen k1
    (integer) 4
    
  • 当key不存在时,设置key的值: setnx

    127.0.0.1:6379> setnx k1 v1
    (integer) 0  #设置已经存在的k1时,返回为0
    127.0.0.1:6379> setnx k2 v2
    (integer) 1 #返回为1,说明已经成功录入数据
    
  • 在输入键值的同时,设置过期时间(10即表示过期时间): setex

    127.0.0.1:6379> setex k3 10 v3 #setnx作出区别
    OK
    
  • 将key对应的value值+1,只能对数字操作,如果为空,新增值为1 incr

  • 将key对应的value值增加任意值 incrby

    127.0.0.1:6379> set k3 1
    OK    # 输入一个key为k3,值为1 的键值对
    127.0.0.1:6379> incr k3
    (integer) 2
    127.0.0.1:6379> get k3
    "2"  #k3的value值+1了
    #增加任意值
    127.0.0.1:6379> incrby k3 10
    (integer) 12
    127.0.0.1:6379> get k3
    "12"
    
  • 将key对应的value值-1,只能对数字操作,如果为空,新增值为1 decr

  • 将key对应的value值减少任意值 decrby

    127.0.0.1:6379> get k3
    "12"
    127.0.0.1:6379> decr k3
    (integer) 11 
    127.0.0.1:6379> get k3
    "11" #k3的value值-1了
    #减少任意值
    127.0.0.1:6379> decrby k3 10
    (integer) 1
    127.0.0.1:6379> get k3
    "1"
    
  • 设置新值的同时,获得旧值 getset

    127.0.0.1:6379> getset k3 new
    "1"   #得到了k3中的value值
    127.0.0.1:6379> get k3
    "new" #把k3的值设置成了new
    

List类型的输入

  • 从左边/右边插入一个或多个值 lpush/rpush

    127.0.0.1:6379> lpush list a
    (integer) 1
    127.0.0.1:6379> lpush list c
    (integer) 2
    127.0.0.1:6379> rpush list b
    (integer) 3
    #查看List:
    127.0.0.1:6379> lrange list 0 -1
    1) "c"
    2) "a"
    3) "b" #由此可见b是从list的右边插入的,C从list的左边插入的
    

    添加多个值:

    127.0.0.1:6379> lpush list a b c d e f
    (integer) 9
    127.0.0.1:6379> lrange list 0 -1 #查看list
    1) "f"
    2) "e"
    3) "d"
    4) "c"
    5) "b"
    6) "a"
    7) "c" #7,8,9是之前插入的, 由此往上是lpush批量插入
    8) "a"
    9) "b"
    127.0.0.1:6379> rpush list a b c d e f g
    (integer) 16
    127.0.0.1:6379> lrange list 0 -1
     1) "f"
     2) "e"
     3) "d"
     4) "c"
     5) "b"
     6) "a"
     7) "c" 
     8) "a"
     9) "b" #7,8,9是之前插入的, 由此往下是rpush批量插入
    10) "a"
    11) "b"
    12) "c"
    13) "d"
    14) "e"
    15) "f"
    16) "g"
    
  • 从左边/右边吐出一个值:lpop/rpop

    127.0.0.1:6379> lpop list
    "f"
    127.0.0.1:6379> rpop list
    "g"
    
  • 按照下标索引范围获得元素(0 -1表示列出所有元素):lrange

    127.0.0.1:6379> lrange list 0 -1
     1) "e"
     2) "d"
     3) "c"
     4) "b"
     5) "a"
     6) "c"
     7) "a"
     8) "b"
     9) "a"
    10) "b"
    11) "c"
    12) "d"
    13) "e"
    14) "f"
    
  • 按照索引下标获得元素: lindex

    127.0.0.1:6379> lindex list 0
    "e"
    
  • 获得list列表的长度: llen

    127.0.0.1:6379> llen list
    (integer) 14
    
  • 在一个value的前面或者后面插入新的value: linsert

    127.0.0.1:6379> linsert list before f hello
    (integer) 15
    127.0.0.1:6379> lrange list 0 -1
     1) "e"
     2) "d"
     3) "c"
     4) "b"
     5) "a"
     6) "c"
     7) "a"
     8) "b"
     9) "a"
    10) "b"
    11) "c"
    12) "d"
    13) "e"
    14) "hello" # 在f的前面,插入了hello
    15) "f"
    
  • 从左边删除n个指定的value: lrem

    127.0.0.1:6379> lrem list 3 a
    (integer) 3 #删除了3个value为a的值
    127.0.0.1:6379> lrange list 0 -1
     1) "e"
     2) "d"
     3) "c"
     4) "b"
     5) "c"
     6) "b"
     7) "b"
     8) "c"
     9) "d"
    10) "e"
    11) "hello"
    12) "f"
    
  • 从一个列表吐出一个值,放到另一个列表: rpoplpush

    127.0.0.1:6379> rpoplpush list list1
    "f"
    127.0.0.1:6379> lrange list1 0 -1
    1) "f"
    

Set类型的输入

  • 将一个或多个元素加入到集合key中: sadd

    127.0.0.1:6379> sadd set hello1 hello2
    (integer) 2
    127.0.0.1:6379> sadd set hello3
    (integer) 1
    
  • 取出set集合中所有的值: smembers

    127.0.0.1:6379> smembers set
    1) "hello2"
    2) "hello3"
    3) "hello1"
    
  • 判断集合中是否含有该值,有则返回1,没有则返回0: sismember

    127.0.0.1:6379> sismember set hello1
    (integer) 1
    127.0.0.1:6379> sismember set hello4
    (integer) 0
    
  • 返回该集合中的元素个数: scard

    127.0.0.1:6379> scard set
    (integer) 3
    
  • 删除集合中的某个元素: srem

    127.0.0.1:6379> srem set hello1
    (integer) 1
    
  • 删除集合中的某个元素: srem

    127.0.0.1:6379> spop set
    "hello2"
    
  • 随机从该集合中取出n个值,取出的值不会被删除: srandmember

    127.0.0.1:6379> srandmember set 1
    1) "hello3"
    
  • 返回两个集合的交集元素: sinter

    127.0.0.1:6379> sadd set1 a b c d
    (integer) 4
    127.0.0.1:6379> sadd set2 c d e f
    (integer) 4
    127.0.0.1:6379> sinter set1 set2
    1) "d"
    2) "c"
    
  • 返回两个集合的并集元素: sunion

    127.0.0.1:6379> sunion set1 set2
    1) "d"
    2) "e"
    3) "f"
    4) "a"
    5) "b"
    6) "c"
    
  • 返回两个集合的差集元素: sdiff

127.0.0.1:6379> sdiff set1 set2
1) "b"
2) "a"

Hash类型的输入

Hash类型:即在键值对的值类型是Map类型.

  • 给集合中的filed键赋值value: hset

    127.0.0.1:6379> hset hash k1 v1
    (integer) 1
    
  • 获取集合中filed键对应的值: hget

    127.0.0.1:6379> hget hash k1
    "v1"
    
  • 批量设置集合中的键值对: hmset

    127.0.0.1:6379> hmset hash k2 v2 k3 v3
    OK
    
  • 判断哈希表中给定field键的值是否存在: hexists

    127.0.0.1:6379> hexists hash k1
    (integer) 1
    
  • 列出集合中所有的filed键: hkeys

    127.0.0.1:6379> hkeys hash
    1) "k1"
    2) "k2"
    3) "k3"
    
  • 列出该集合的所有value: hvals

    127.0.0.1:6379> hvals hash
    1) "v1"
    2) "v2"
    3) "v3"
    
  • 为哈希表中的field键对应的值加上increment值: hincrby

    127.0.0.1:6379> hset hash k4 2
    (integer) 1  #设置了一个field键为k4值为2的hash
    127.0.0.1:6379> hincrby hash k4 3
    (integer) 5 #加3
    127.0.0.1:6379> hget hash k4
    "5"
    
  • 当集合中的field键不存在时,设置对应的field键和值: hsetnx

    127.0.0.1:6379> hsetnx hash k5 hello
    (integer) 1
    

Zest类型的输入

Zset,相比于set多了一个Z,与set有类似的地方,比如元素都不能重复,不同之处在于每个元素多了一个评分,元素不可以重复,但评分可以重复

  • 将一个元素或多个元素及其评分值加入到有序集合中: zadd

    127.0.0.1:6379> zadd zest 1 v1
    (integer) 1
    127.0.0.1:6379> zadd zest 2 v2 3 v3
    (integer) 2
    
  • 返回有序集合中下标在start和stop之间的元素,带Withscores,可以让分数和值一起返回: zrange

    127.0.0.1:6379> zrange zest 0 -1 withscores
    1) "v1"
    2) "1"
    3) "v2"
    4) "2"
    5) "v3"
    6) "3"
    
  • 返回有序集合中score值介于min和max之间的所有成员.有序集合成员按score值递增的次序排列: zrangebyscore

127.0.0.1:6379> zrangebyscore zest 0 2 withscores
1) "v1"
2) "1"
3) "v2"
4) "2"
  • 返回有序集合中score值介于min和max之间的所有成员.有序集合成员按score值递减的次序排列: zrevrangebyscore

    127.0.0.1:6379> zrevrangebyscore zest 2 0 withscores
    1) "v2"
    2) "2"
    3) "v1"
    4) "1"
    
  • 为有序集合中的score值加上指定的数值: zincrby

    127.0.0.1:6379> zincrby zest 2 v2
    "4"
    
  • 删除指定值: zrem

    127.0.0.1:6379> zrem zest v1
    (integer) 1
    
  • 统计有序集合分数区间内的元素个数: zcount

    127.0.0.1:6379> zcount zest 0 5
    (integer) 2
    
  • 返回该值有序集合中的排名,从0开始: zrank

    127.0.0.1:6379> zrank zest v2
    (integer) 1
    

## 四、Redis的三大特殊数据类型

Geospatial(地理位置)

使用经纬度定位地理坐标并用一个有序集合zset保存,所以zset命令也可以使用

命令描述
geoadd key longitud(经度) latitude(纬度) member […]将具体经纬度的坐标存入一个有序集合
geopos key member [member…]获取集合中的一个/多个成员坐标
geodist key member1 member2 [unit]返回两个给定位置之间的距离。默认以米作为单位。
georadius key longitude latitude radius m|km|mi|ft [WITHCOORD][WITHDIST] [WITHHASH] [COUNT count]以给定的经纬度为中心,返回集合包含的位置元素当中,与中心的距离不超过给定最大距离的所有位置元素。
georadiusbymember key member radius…功能与GEORADIUS相同,只是中心位置不是具体的经纬度,而是使用结合中已有的成员作为中心点。
geohash key member1 [member2…]返回一个或多个位置元素的Geohash表示。使用Geohash位置52点整数编码。
  • 有效的经度从-180度到180度。
  • 有效的纬度从-85.05112878度到85.05112878度。

指定单位的参数 unit 必须是以下单位的其中一个:

  • m 表示单位为米。
  • km 表示单位为千米。
  • mi 表示单位为英里。
  • ft 表示单位为英尺

关于GEORADIUS的参数

通过georadius就可以完成 附近的人功能

withcoord:带上坐标

withdist:带上距离,单位与半径单位相同

COUNT n : 只显示前n个(按距离递增排序)

----------------georadius---------------------
127.0.0.1:6379> GEORADIUS china:city 120 30 500 km withcoord withdist # 查询经纬度(120,30)坐标500km半径内的成员
1) 1) "hangzhou"
   2) "29.4151"
   3) 1) "120.20000249147415"
      2) "30.199999888333501"
2) 1) "shanghai"
   2) "205.3611"
   3) 1) "121.40000134706497"
      2) "31.400000253193539"
     
------------geohash---------------------------
127.0.0.1:6379> geohash china:city yichang shanghai # 获取成员经纬坐标的geohash表示
1) "wmrjwbr5250"
2) "wtw6ds0y300"

Hyperloglog(基数统计)

Redis HyperLogLog 是用来做基数统计的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定的、并且是很小的。花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基数。因为 HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素本身,所以 HyperLogLog 不能像集合那样,返回输入的各个元素。其底层使用string数据类型

什么是基数?

数据集中不重复的元素的个数。

应用场景:

网页的访问量(UV):一个用户多次访问,也只能算作一个人。

传统实现,存储用户的id,然后每次进行比较。当用户变多之后这种方式及其浪费空间,而我们的目的只是计数,Hyperloglog就能帮助我们利用最小的空间完成。

命令描述
PFADD key element1 [elememt2…]添加指定元素到 HyperLogLog 中
PFCOUNT key [key]返回给定 HyperLogLog 的基数估算值。
PFMERGE destkey sourcekey [sourcekey…]将多个 HyperLogLog 合并为一个 HyperLogLog
----------PFADD--PFCOUNT---------------------
127.0.0.1:6379> PFADD myelemx a b c d e f g h i j k # 添加元素
(integer) 1
127.0.0.1:6379> type myelemx # hyperloglog底层使用String
string
127.0.0.1:6379> PFCOUNT myelemx # 估算myelemx的基数
(integer) 11
127.0.0.1:6379> PFADD myelemy i j k z m c b v p q s
(integer) 1
127.0.0.1:6379> PFCOUNT myelemy
(integer) 11

----------------PFMERGE-----------------------
127.0.0.1:6379> PFMERGE myelemz myelemx myelemy # 合并myelemx和myelemy 成为myelemz
OK
127.0.0.1:6379> PFCOUNT myelemz # 估算基数
(integer) 17

BitMaps(位图)

使用位存储,信息状态只有 0 和 1

Bitmap是一串连续的2进制数字(0或1),每一位所在的位置为偏移(offset),在bitmap上可执行AND,OR,XOR,NOT以及其它位操作。

应用场景

签到统计、状态统计

命令描述
setbit key offset value为指定key的offset位设置值
getbit key offset获取offset位的值
bitcount key [start end]统计字符串被设置为1的bit数,也可以指定统计范围按字节
bitop operration destkey key[key…]对一个或多个保存二进制位的字符串 key 进行位元操作,并将结果保存到 destkey 上。
BITPOS key bit [start] [end]返回字符串里面第一个被设置为1或者0的bit位。start和end只能按字节,不能按位
------------setbit--getbit--------------
127.0.0.1:6379> setbit sign 0 1 # 设置sign的第0位为 1 
(integer) 0
127.0.0.1:6379> setbit sign 2 1 # 设置sign的第2位为 1  不设置默认 是0
(integer) 0
127.0.0.1:6379> setbit sign 3 1
(integer) 0
127.0.0.1:6379> setbit sign 5 1
(integer) 0
127.0.0.1:6379> type sign
string

127.0.0.1:6379> getbit sign 2 # 获取第2位的数值
(integer) 1
127.0.0.1:6379> getbit sign 3
(integer) 1
127.0.0.1:6379> getbit sign 4 # 未设置默认是0
(integer) 0

-----------bitcount----------------------------
127.0.0.1:6379> BITCOUNT sign # 统计sign中为1的位数
(integer) 4

五、Redis的事务

Redis的单条事务是保证原子性的,但多条的Redis事务不能保证原子性

Redis事务本质:一组命令的集合。

----------------- 队列 set set set 执行 -------------------

事务中每条命令都会被序列化,执行过程中按顺序执行,不允许其他命令进行干扰。

Redis事务本质:一组命令的集合。

----------------- 队列 set set set 执行 -------------------

事务中每条命令都会被序列化,执行过程中按顺序执行,不允许其他命令进行干扰。

  • 一次性
  • 顺序性
  • 排他性

1.Redis事务没有隔离性的概念

2.Redis单条命令是保证原子性的,但是事务不保证原子性

Redis的事务操作过程:

开启事务(multi)

命令入队

执行事务(exec)

所以事务中的命令在加入时都没有被执行,直到提交时才会开始执行(Exec),一次性完成

127.0.0.1:6379> multi # 开启事务
OK
127.0.0.1:6379> set k1 v1 # 命令入队
QUEUED
127.0.0.1:6379> set k2 v2 # ..
QUEUED
127.0.0.1:6379> get k1
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> keys *
QUEUED
127.0.0.1:6379> exec # 事务执行
1) OK
2) OK
3) "v1"
4) OK
5) 1) "k3"
   2) "k2"
   3) "k1"

取消事务(discard):

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> DISCARD # 放弃事务
OK
127.0.0.1:6379> EXEC 
(error) ERR EXEC without MULTI # 当前未开启事务
127.0.0.1:6379> get k1 # 被放弃事务中命令并未执行
(nil) 

事务产生错误

代码中的语法错误(编译时异常),事务会停止,所有命令都不执行

 127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> error k1 # 这是一条语法错误命令
(error) ERR unknown command `error`, with args beginning with: `k1`, # 会报错但是不影响后续命令入队 
127.0.0.1:6379> get k2
QUEUED
127.0.0.1:6379> EXEC
(error) EXECABORT Transaction discarded because of previous errors. # 执行报错
127.0.0.1:6379> get k1 
(nil) # 其他命令并没有被执行

代码的逻辑错误(运行时异常),事务不会停止,其他命令继续执行=>所以不保证原子性

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> INCR k1 # 这条命令逻辑错误(对字符串进行增量)
QUEUED
127.0.0.1:6379> get k2
QUEUED
127.0.0.1:6379> exec
1) OK
2) OK
3) (error) ERR value is not an integer or out of range # 运行时报错
4) "v2" # 其他命令正常执行

# 虽然中间有一条命令报错了,但是后面的指令依旧正常执行成功了。
# 所以说Redis单条指令保证原子性,但是Redis事务不能保证原子性。

监控:

悲观锁:

  • 很悲观,认为什么时候都会出现问题,无论做什么都会加锁

乐观锁:

  • 很乐观,认为什么时候都不会出现问题,所以不会上锁!更新数据的时候去判断一下,在此期间是否有人修改过这个数据

  • 获取version

  • 更新的时候比较version

    使用watch key监控指定数据,相当于乐观锁加锁。

    正常执行

127.0.0.1:6379> set money 100 # 设置余额:100
OK
127.0.0.1:6379> set use 0 # 支出使用:0
OK
127.0.0.1:6379> watch money # 监视money (上锁)
OK
127.0.0.1:6379> multi #开启事务 
OK
127.0.0.1:6379> DECRBY money 20 # 余额:减少20
QUEUED
127.0.0.1:6379> INCRBY use 20 # 支出使用:增加20
QUEUED
127.0.0.1:6379> exec # 监视值没有被中途修改,事务正常执行
1) (integer) 80
2) (integer) 20

我们使用一个多线程对当前的money进行修改,通过watch实现乐观锁操作

线程1:

127.0.0.1:6379> watch money # money上锁
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> DECRBY money 20 # 余额:减少20
QUEUED
127.0.0.1:6379> INCRBY use 20 # 支出使用:增加20
QUEUED
127.0.0.1:6379> 	# 此时事务并没有执行

线程2:

127.0.0.1:6379> INCRBY money 500 # 修改了线程一中监视的money
(integer) 600 

回到线程1,执行事务:

127.0.0.1:6379> EXEC # 执行之前,另一个线程修改了我们的值,这个时候就会导致事务执行失败
(nil) # 没有结果,说明事务执行失败
127.0.0.1:6379> get money # 线程2 修改生效
"600"
127.0.0.1:6379> get use # 线程1事务执行失败,数值没有被修改
"0"

解锁获取最新值,然后再加锁进行事务。

unwatch进行解锁。

注意:每次提交执行exec后都会自动释放锁,不管是否成功

六、使用Jedis操作Redis

我们已经知晓了Redis的基本操作,如果想要在Java程序中完成对Redis的操作,我们就需要用到Jedis.

1.导包

<!--导入jredis的包-->
<dependency>
	<groupId>redis.clients</groupId>
	<artifactId>jedis</artifactId>
	<version>3.2.0</version>
</dependency>

2.使用–创建Jedis对象

public class JedisDemo {
    public static void main(String[] args) {
        //构造方法自己进源码看,这么大个人了还不会看源码可以去死了
        Jedis jedis = new Jedis("127.0.0.1",6379);
    }

3.使用jedis操作数据

public class JedisDemo {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1",6379);
        System.out.println(jedis.ping());
        jedis.set("k1","v1");
        System.out.println("jedis.get(\"k1\") = " + jedis.get("k1"));
    }
}

输出:

PONG
jedis.get(“k1”) = v1

4.使用jedis操作事务

public class JedisDemo {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1",6379);
        Transaction multi = jedis.multi();
        try {
            multi.set("money", String.valueOf(100));
            multi.set("out", String.valueOf(0));
            multi.decrBy("money",20);
            multi.incrBy("out",20);
            multi.exec();
        }catch (Exception e) {
            System.out.println("执行失败");
        }
        multi.close();
        System.out.println("jedis.get(\"money\") = " + jedis.get("money"));
        System.out.println("jedis.get(\"out\") = " + jedis.get("out"));
        jedis.close();
    }
}

七、Redis配置文件的分析

打开Redis目录下的redis.windows.conf,里面的配置我们需要认识:

容量单位不区分大小写,G和GB有区别

# Note on units: when memory size is needed, it is possible to specify
# it in the usual form of 1k 5GB 4M and so forth:
#
# 1k => 1000 bytes
# 1kb => 1024 bytes
# 1m => 1000000 bytes
# 1mb => 1024*1024 bytes
# 1g => 1000000000 bytes
# 1gb => 1024*1024*1024 bytes
#
# units are case insensitive so 1GB 1Gb 1gB are all the same.

可以使用 include 组合多个配置问题

#
# include .\path\to\local.conf
# include c:\path\to\other.conf

网络配置

bind 127.0.0.1   =绑定ip

# Protected mode is a layer of security protection, in order to avoid that
# Redis instances left open on the internet are accessed and exploited.
#
# When protected mode is on and if:
#
# 1) The server is not binding explicitly to a set of addresses using the
#    "bind" directive.
# 2) No password is configured.
#
# The server only accepts connections from clients connecting from the
# IPv4 and IPv6 loopback addresses 127.0.0.1 and ::1, and from Unix domain
# sockets.
#
# By default protected mode is enabled. You should disable it only if
# you are sure you want clients from other hosts to connect to Redis
# even if no authentication is configured, nor a specific set of interfaces
# are explicitly listed using the "bind" directive.
protected-mode yes  =是否开启保护模式

# Accept connections on the specified port, default is 6379 (IANA #815344).
# If port 0 is specified Redis will not listen on a TCP socket.
port 6379			=端口

日志输出级别

# Specify the server verbosity level.
# This can be one of:
# 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)
loglevel notice 

日志输出文件名

# Specify the log file name. Also 'stdout' can be used to force
# Redis to log on the standard output.
logfile ""

数据库个数

# Set the number of databases. The default database is DB 0, you can select
# a different one on a per-connection basis using SELECT <dbid> where
# dbid is a number between 0 and 'databases'-1
databases 16

持久化规则

#持久化的规则:
#每900秒有1个key进行了修改,则进行持久化
save 900 1
#每300秒有10个key进行了修改,则进行持久化
save 300 10
#每60秒有10000个key进行了修改,则进行持久化
save 60 10000

RDB持久化相关设置

top-writes-on-bgsave-error yes =持久化错误继续工作

# Compress string objects using LZF when dump .rdb databases?
# For default that's set to 'yes' as it's almost always a win.
# If you want to save some CPU in the saving child set it to 'no' but
# the dataset will likely be bigger if you have compressible values or keys.
rdbcompression yes  =压缩RDB文件

# Since version 5 of RDB a CRC64 checksum is placed at the end of the file.
# This makes the format more resistant to corruption but there is a performance
# hit to pay (around 10%) when saving and loading RDB files, so you can disable it
# for maximum performances.
#
# RDB files created with checksum disabled have a checksum of zero that will
# tell the loading code to skip the check.
rdbchecksum yes =检验RDB文件

# The filename where to dump the DB
dbfilename dump.rdb =RDB持久化的文件名

持久化文件保存的目录

# Note that you must specify a directory here, not a file name.
dir ./

等等等等…配置太多自己去研究,这里只列举一些.

八、Redis的持久化

什么是持久化?

在指定时间间隔后,将内存中的数据集快照写入数据库 ;在恢复时候,直接读取快照文件,进行数据的恢复.默认情况下,Redis 将数据库快照保存在名字为 dump.rdb的二进制文件中。文件名可以在配置文件中进行自定义。

RDB持久化

RDB持久化原理

在这里插入图片描述

  1. 在进行 RDB 的时候,redis 的主线程是不会做 io 操作的,主线程会 fork 一个子线程来完成该操作;
  2. 子进程将数据集写入到一个临时 RDB 文件中。
  3. 当子进程完成对新 RDB 文件的写入时,Redis 用新 RDB 文件替换原来的 RDB 文件,并删除旧的 RDB 文件。

这种工作方式使得 Redis 可以从写时复制(copy-on-write)机制中获益(因为是使用子进程进行写操作,而父进程依然可以接收来自客户端的请求。)

在这里插入图片描述

触发机制:

  1. 当save规则满足的情况下,会自动触发rdb原则,sa
    ve规则可以在Redis的配置文件中修改

    #持久化的规则:
    #每900秒有1个key进行了修改,则进行持久化
    save 900 1
    #每300秒有10个key进行了修改,则进行持久化
    save 300 10
    #每60秒有10000个key进行了修改,则进行持久化
    save 60 10000
    
  2. 执行flushall命令,也会触发我们的rdb原则

  3. 退出redis,也会自动产生rdb文件

    还有一种使用save命令,会立刻对当前内存中的数据进行持久化,但是会阻塞,也就是不接收其他操作了.

    由于 save 命令是同步命令,会占用Redis的主进程。若Redis数据非常多时,save命令执行速度会非常慢,阻塞所有客户端的请求。

save命令的流程图:

在这里插入图片描述

如果不想造成阻塞,则可以用bgsave命令

bgsave 是异步进行,进行持久化的时候,redis 还可以将继续响应客户端请求 ;

bgsave命令的流程图:

在这里插入图片描述

savebgsave的比较

命令savebgsave
IO类型同步异步
是否阻塞是(阻塞发生在fork,非常快)
复杂度O(n)O(n)
优点不会消耗额外的内存不阻塞客户端命令
缺点阻塞客户端命令需要frok子进程,消耗内存

RDB持久化优点:

  • 适合大规模数据的恢复
  • 对数据完整性要求不高(容易损失一些数据)

缺点:

  • 需要一定的时间间隔进行操作,如果redis意外宕机,最后一次修改的数据就没有了
  • fork进程的时候,会占用一定的内容空间

AOF持久化

AOF持久化:以日志的形式来记录每个写的操作,将Redis执行过的所有指令记录下来(读操作不记录),只许追加文件但不可以改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。

AOF持久化流程图:

在这里插入图片描述

开启AOF持久化需要进行设置(在redis.window.conf文件中):

appendonly yes =yes表示开启aof持久化

# The name of the append only file (default: "appendonly.aof")
appendfilename "appendonly.aof"  持久化后的文件名

如果这个aof文件有错位(比如被修改了内容),这时候redis是启动不起来的,我需要修改这个aof文件,redis给我们提供了一个命令redis-check-aof --fix

优点:

  1. 每一次修改都会同步,文件的完整性会更加好
  2. 没秒同步一次,可能会丢失一秒的数据
  3. 从不同步,效率最高

缺点:

  1. 相对于数据文件来说,aof远远大于rdb,修复速度比rdb慢!
  2. Aof运行效率也要比rdb慢,所以我们redis默认的配置就是rdb持久化

RDB和AOF的比较:

比较RDBAOF
启动优先级高(前提是开启了)
体积
恢复速度
数据安全性丢数据根据策略决定

如何选择使用哪种持久化方式?

一般来说, 如果想达到足以媲美 PostgreSQL 的数据安全性, 你应该同时使用两种持久化功能。

如果你非常关心你的数据, 但仍然可以承受数分钟以内的数据丢失, 那么你可以只使用 RDB 持久化。

有很多用户都只使用 AOF 持久化, 但并不推荐这种方式: 因为定时生成 RDB 快照(snapshot)非常便于进行数据库备份, 并且 RDB 恢复数据集的速度也要比 AOF 恢复的速度要快。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值