当前位置 : 主页 > 编程语言 > java >

redis 初探

来源:互联网 收集:自由互联 发布时间:2023-02-04
redis(端口6379) 单线程 基于内存操作 很快 cpu不是瓶颈 是内存和带宽 C语言写的 使用 redis-server --service-install redis.windows.conf --loglevel verbose select 3 切换到库3 总共16个?flushall 清空全部数

redis(端口6379)

单线程 基于内存操作 很快 cpu不是瓶颈 是内存和带宽

C语言写的

使用

redis-server --service-install redis.windows.conf --loglevel verbose

select 3 切换到库3 总共16个?flushall 清空全部数据库flushdb 清空当前

redis-cli

127.0.0.1:6379> config get requirepass1) "requirepass"2) ""127.0.0.1:6379> config set requirepass "1234"127.0.0.1:6379> auth 1234

密码1234

五种基本数据类型

string list set hash zset(有序集合)

String
incr 递增decr 递减##########################################################################127.0.0.1:6379> set key1 v1 # 设置值OK127.0.0.1:6379> get key1 # 获得值"v1"127.0.0.1:6379> keys * # 获得所有的key1) "key1"127.0.0.1:6379> EXISTS key1 # 判断某一个key是否存在(integer) 1127.0.0.1:6379> APPEND key1 "hello" # 追加字符串,如果当前key不存在,就相当于setkey(integer) 7127.0.0.1:6379> get key1"v1hello"127.0.0.1:6379> STRLEN key1 # 获取字符串的长度!(integer) 7127.0.0.1:6379> APPEND key1 ",kaungshen"(integer) 17127.0.0.1:6379> STRLEN key1(integer) 17127.0.0.1:6379> get key1"v1hello,kaungshen"########################################################################### i++# 步长 i+=127.0.0.1:6379> set views 0 # 初始浏览量为0OK127.0.0.1:6379> get views"0"127.0.0.1:6379> incr views # 自增1 浏览量变为1(integer) 1127.0.0.1:6379> incr views(integer) 2127.0.0.1:6379> get views"2"127.0.0.1:6379> decr views # 自减1 浏览量-1(integer) 1127.0.0.1:6379> decr views(integer) 0127.0.0.1:6379> decr views(integer) -1127.0.0.1:6379> get views"-1"127.0.0.1:6379> INCRBY views 10 # 可以设置步长,指定增量!(integer) 9127.0.0.1:6379> INCRBY views 10(integer) 19127.0.0.1:6379> DECRBY views 5bilibili:狂神说Java(integer) 14###########################################################################字符串范围 range127.0.0.1:6379> set key1 "hello,kuangshen" # 设置 key1 的值OK127.0.0.1:6379> get key1"hello,kuangshen"127.0.0.1:6379> GETRANGE key1 0 3 # 截取字符串 [0,3]"hell"127.0.0.1:6379> GETRANGE key1 0 -1 # 获取全部的字符串 和 get key是一样的"hello,kuangshen"#替换!127.0.0.1:6379> set key2 abcdefgOK127.0.0.1:6379> get key2"abcdefg"127.0.0.1:6379> SETRANGE key2 1 xx # 替换指定位置开始的字符串!(integer) 7127.0.0.1:6379> get key2"axxdefg"###########################################################################setex (set with expire) # 设置过期时间#setnx (set if not exist) # 不存在在设置 (在分布式锁中会常常使用!)127.0.0.1:6379> setex key3 30 "hello" # 设置key3 的值为 hello,30秒后过期OK127.0.0.1:6379> ttl key3(integer) 26127.0.0.1:6379> get key3"hello"127.0.0.1:6379> setnx mykey "redis" # 如果mykey 不存在,创建mykey(integer) 1127.0.0.1:6379> keys *1) "key2"2) "mykey"3) "key1"127.0.0.1:6379> ttl key3(integer) -2127.0.0.1:6379> setnx mykey "MongoDB" # 如果mykey存在,创建失败!(integer) 0127.0.0.1:6379> get mykey"redis"##########################################################################msetmget127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3 # 同时设置多个值OK127.0.0.1:6379> keys *1) "k1"2) "k2"3) "k3"127.0.0.1:6379> mget k1 k2 k3 # 同时获取多个值1) "v1"2) "v2"3) "v3"bilibili:狂神说Java数据结构是相同的!String类似的使用场景:value除了是我们的字符串还可以是我们的数字!计数器统计多单位的数量粉丝数对象缓存存储!List(列表)基本的数据类型,列表127.0.0.1:6379> msetnx k1 v1 k4 v4 # msetnx 是一个原子性的操作,要么一起成功,要么一起失败!(integer) 0127.0.0.1:6379> get k4(nil)#对象set user:1 {name:zhangsan,age:3} # 设置一个user:1 对象 值为 json字符来保存一个对象!#这里的key是一个巧妙的设计: user:{id}:{filed} , 如此设计在Redis中是完全OK了!127.0.0.1:6379> mset user:1:name zhangsan user:1:age 2OK127.0.0.1:6379> mget user:1:name user:1:age1) "zhangsan"2) "2"##########################################################################getset # 先get然后在set127.0.0.1:6379> getset db redis # 如果不存在值,则返回 nil(nil)127.0.0.1:6379> get db"redis127.0.0.1:6379> getset db mongodb # 如果存在值,获取原来的值,并设置新的值"redis"127.0.0.1:6379> get db"mongod
list

在redis里面,我们可以把list玩成 ,栈、队列、阻塞队列! 所有的list命令都是用l开头的,Redis不区分大小命令

##########################################################################127.0.0.1:6379> LPUSH list one # 将一个值或者多个值,插入到列表头部 (左)(integer) 1127.0.0.1:6379> LPUSH list two(integer) 2127.0.0.1:6379> LPUSH list three(integer) 3127.0.0.1:6379> LRANGE list 0 -1 # 获取list中值!1) "three"2) "two"3) "one"127.0.0.1:6379> LRANGE list 0 1 # 通过区间获取具体的值!1) "three"2) "two"127.0.0.1:6379> Rpush list righr # 将一个值或者多个值,插入到列表位部 (右)(integer) 4127.0.0.1:6379> LRANGE list 0 -11) "three"2) "two"3) "one"4) "righr"##########################################################################LPOPRPOP127.0.0.1:6379> LRANGE list 0 -11) "three"2) "two"3) "one"4) "righr"127.0.0.1:6379> Lpop list # 移除list的第一个元素"three"127.0.0.1:6379> Rpop list # 移除list的最后一个元素"righr"127.0.0.1:6379> LRANGE list 0 -11) "two"2) "one"##########################################################################Lindex127.0.0.1:6379> LRANGE list 0 -11) "two"2) "one"127.0.0.1:6379> lindex list 1 # 通过下标获得 list 中的某一个值!"one"127.0.0.1:6379> lindex list 0"two"##########################################################################Llen127.0.0.1:6379> Lpush list one(integer) 1127.0.0.1:6379> Lpush list twobilibili:狂神说Java(integer) 2127.0.0.1:6379> Lpush list three(integer) 3127.0.0.1:6379> Llen list # 返回列表的长度(integer) 3##########################################################################移除指定的值!取关 uidLrem127.0.0.1:6379> LRANGE list 0 -11) "three"2) "three"3) "two"4) "one"127.0.0.1:6379> lrem list 1 one # 移除list集合中指定个数的value,精确匹配(integer) 1127.0.0.1:6379> LRANGE list 0 -11) "three"2) "three"3) "two"127.0.0.1:6379> lrem list 1 three(integer) 1127.0.0.1:6379> LRANGE list 0 -11) "three"2) "two"127.0.0.1:6379> Lpush list three(integer) 3127.0.0.1:6379> lrem list 2 three(integer) 2127.0.0.1:6379> LRANGE list 0 -11) "two"##########################################################################trim 修剪。; list 截断!127.0.0.1:6379> keys *(empty list or set)127.0.0.1:6379> Rpush mylist "hello"(integer) 1127.0.0.1:6379> Rpush mylist "hello1"(integer) 2127.0.0.1:6379> Rpush mylist "hello2"(integer) 3127.0.0.1:6379> Rpush mylist "hello3"(integer) 4127.0.0.1:6379> ltrim mylist 1 2 # 通过下标截取指定的长度,这个list已经被改变了,截断了只剩下截取的元素!OK127.0.0.1:6379> LRANGE mylist 0 -11) "hello1"2) "hello2"##########################################################################rpoplpush # 移除列表的最后一个元素,将他移动到新的列表中!127.0.0.1:6379> rpush mylist "hello"bilibili:狂神说Java小结他实际上是一个链表,before Node after , left,right 都可以插入值如果key 不存在,创建新的链表如果key存在,新增内容(integer) 1127.0.0.1:6379> rpush mylist "hello1"(integer) 2127.0.0.1:6379> rpush mylist "hello2"(integer) 3127.0.0.1:6379> rpoplpush mylist myotherlist # 移除mylist列表的最后一个元素,将他移动到新列表中!"hello2"127.0.0.1:6379> lrange mylist 0 -1 # 查看原来的列表1) "hello"2) "hello1"127.0.0.1:6379> lrange myotherlist 0 -1 # 查看目标列表中,确实存在改值!1) "hello2"##########################################################################lset 将列表中指定下标的值替换为另外一个值,更新操作127.0.0.1:6379> EXISTS list # 判断这个列表是否存在(integer) 0127.0.0.1:6379> lset list 0 item # 如果不存在列表我们去更新就会报错(error) ERR no such key127.0.0.1:6379> lpush list value1(integer) 1127.0.0.1:6379> LRANGE list 0 01) "value1"127.0.0.1:6379> lset list 0 item # 如果存在,更新当前下标的值OK127.0.0.1:6379> LRANGE list 0 01) "item"127.0.0.1:6379> lset list 1 other # 如果不存在,则会报错!(error) ERR index out of range##########################################################################linsert # 将某个具体的value插入到列把你中某个元素的前面或者后面!127.0.0.1:6379> Rpush mylist "hello"(integer) 1127.0.0.1:6379> Rpush mylist "world"(integer) 2127.0.0.1:6379> LINSERT mylist before "world" "other"(integer) 3127.0.0.1:6379> LRANGE mylist 0 -11) "hello"2) "other"3) "world"127.0.0.1:6379> LINSERT mylist after world new(integer) 4127.0.0.1:6379> LRANGE mylist 0 -11) "hello"2) "other"3) "world"4) "new"
set(值不重复)
##########################################################################127.0.0.1:6379> sadd myset "hello" # set集合中添加(integer) 1127.0.0.1:6379> sadd myset "kuangshen"(integer) 1127.0.0.1:6379> sadd myset "lovekuangshen"(integer) 1127.0.0.1:6379> SMEMBERS myset # 查看指定set的所有值1) "hello"2) "lovekuangshen"3) "kuangshen"127.0.0.1:6379> SISMEMBER myset hello # 判断某一个值是不是在set集合中!(integer) 1127.0.0.1:6379> SISMEMBER myset world(integer) 0##########################################################################127.0.0.1:6379> scard myset # 获取set集合中的内容元素个数!(integer) 4##########################################################################rem127.0.0.1:6379> srem myset hello # 移除set集合中的指定元素(integer) 1127.0.0.1:6379> scard myset(integer) 3127.0.0.1:6379> SMEMBERS myset1) "lovekuangshen2"2) "lovekuangshen"3) "kuangshen"##########################################################################set 无序不重复集合。抽随机!127.0.0.1:6379> SMEMBERS myset1) "lovekuangshen2"2) "lovekuangshen"3) "kuangshen"127.0.0.1:6379> SRANDMEMBER myset # 随机抽选出一个元素"kuangshen"127.0.0.1:6379> SRANDMEMBER myset"kuangshen"127.0.0.1:6379> SRANDMEMBER myset"kuangshen"127.0.0.1:6379> SRANDMEMBER myset"kuangshen"127.0.0.1:6379> SRANDMEMBER myset 2 # 随机抽选出指定个数的元素bilibili:狂神说Java1) "lovekuangshen"2) "lovekuangshen2"127.0.0.1:6379> SRANDMEMBER myset 21) "lovekuangshen"2) "lovekuangshen2"127.0.0.1:6379> SRANDMEMBER myset # 随机抽选出一个元素"lovekuangshen2"##########################################################################删除定的key,随机删除key!127.0.0.1:6379> SMEMBERS myset1) "lovekuangshen2"2) "lovekuangshen"3) "kuangshen"127.0.0.1:6379> spop myset # 随机删除一些set集合中的元素!"lovekuangshen2"127.0.0.1:6379> spop myset"lovekuangshen"127.0.0.1:6379> SMEMBERS myset1) "kuangshen"##########################################################################将一个指定的值,移动到另外一个set集合!127.0.0.1:6379> sadd myset "hello"(integer) 1127.0.0.1:6379> sadd myset "world"(integer) 1127.0.0.1:6379> sadd myset "kuangshen"(integer) 1127.0.0.1:6379> sadd myset2 "set2"(integer) 1127.0.0.1:6379> smove myset myset2 "kuangshen" # 将一个指定的值,移动到另外一个set集合!(integer) 1127.0.0.1:6379> SMEMBERS myset1) "world"2) "hello"127.0.0.1:6379> SMEMBERS myset21) "kuangshen"2) "set2"##########################################################################(并集)数字集合类:- 差集 SDIFF- 交集 SINTER- 并集 SUNION127.0.0.1:6379> SDIFF key1 key2 # 差集1) "b"2) "a"127.0.0.1:6379> SINTER key1 key2 # 交集 共同好友就可以这样实现1) "c"127.0.0.1:6379> SUNION key1 key2 # 并集1) "b"2) "c"3) "e"4) "a"
Hash
127.0.0.1:6379> hset myhash field1 kuangshen(integer) 1127.0.0.1:6379> hget myhash field1"kuangshen"127.0.0.1:6379> hset myhash field1 hello field2 world(integer) 1127.0.0.1:6379> hget myhash field1 field2(error) ERR wrong number of arguments for 'hget' command127.0.0.1:6379> hmget myhash field1 field21) "hello"2) "world"127.0.0.1:6379> hgetall myhash1) "field1"2) "hello"3) "field2"4) "world"127.0.0.1:6379> hdel myhash field1(integer) 1127.0.0.1:6379> hgetall myhash1) "field2"2) "world"127.0.0.1:6379>##########################################################################hlen127.0.0.1:6379> hmset myhash field1 hello field2 worldOK127.0.0.1:6379> HGETALL myhash1) "field2"2) "world"3) "field1"4) "hello"127.0.0.1:6379> hlen myhash # 获取hash表的字段数量!(integer) 2##########################################################################127.0.0.1:6379> HEXISTS myhash field1 # 判断hash中指定字段是否存在!(integer) 1127.0.0.1:6379> HEXISTS myhash field3(integer) 0###########################################################################只获得所有field#只获得所有value127.0.0.1:6379> hkeys myhash # 只获得所有field1) "field2"2) "field1 127.0.0.1:6379> hvals myhash # 只获得所有value1) "world"2) "hello"##########################################################################incr decr127.0.0.1:6379> hset myhash field3 5 #指定增量!(integer) 1127.0.0.1:6379> HINCRBY myhash field3 1(integer) 6127.0.0.1:6379> HINCRBY myhash field3 -1(integer) 5127.0.0.1:6379> hsetnx myhash field4 hello # 如果不存在则可以设置(integer) 1127.0.0.1:6379> hsetnx myhash field4 world # 如果存在则不能设置(integer) 0
zset(有序集合)

三种特殊数据类型

Geospatial 地理位置
Hyperloglog

基数

Bitmap

位存储

事务

Redis 事务本质:一组命令的集合! 一个事务中的所有命令都会被序列化,在事务执行过程的中,会照顺序执行! 一次性、顺序性、排他性!执行一系列的命令!

Redis事务没有没有隔离级别的概念! 所有的命令在事务中,并没有直接被执行!只有发起执行命令的时候才会执行!Exec

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

redis的事务:

  • 开启事务(multi)
  • 命令入队(…)
  • 执行事务(exec)
正常执行事务
127.0.0.1:6379> multi # 开启事务OK# 命令入队127.0.0.1:6379> set k1 v1QUEUED127.0.0.1:6379> set k2 v2QUEUED127.0.0.1:6379> get k2QUEUED127.0.0.1:6379> set k3 v3QUEUED127.0.0.1:6379> exec # 执行事务1) OK2) OK3) "v2"4) OK
放弃事务
127.0.0.1:6379> multi # 开启事务OK127.0.0.1:6379> set k1 v1QUEUED127.0.0.1:6379> set k2 v2QUEUED127.0.0.1:6379> set k4 v4QUEUED127.0.0.1:6379> DISCARD # 取消事务OK127.0.0.1:6379> get k4 # 事务队列中命令都不会被执行!(nil)
编译型异常

(代码有问题! 命令有错!) ,事务中所有的命令都不会被执行!

127.0.0.1:6379> multiOK127.0.0.1:6379> set k1 v1QUEUED127.0.0.1:6379> set k2 v2QUEUED127.0.0.1:6379> set k3 v3QUEUED127.0.0.1:6379> getset k3 # 错误的命令(error) ERR wrong number of arguments for 'getset' command127.0.0.1:6379> set k4 v4QUEUED127.0.0.1:6379> set k5 v5QUEUED127.0.0.1:6379> exec # 执行事务报错!(error) EXECABORT Transaction discarded because of previous errors.127.0.0.1:6379> get k5 # 所有的命令都不会被执行!(nil)
运行时异常(1/0)

如果事务队列中存在错误语法性,那么执行命令的时候,其他命令是可以正常执行 的,错误命令抛出异常!

127.0.0.1:6379> set k1 "v1"OK127.0.0.1:6379> multiOK127.0.0.1:6379> incr k1 # 会执行的时候失败!QUEUED127.0.0.1:6379> set k2 v2QUEUED127.0.0.1:6379> set k3 v3QUEUED127.0.0.1:6379> get k3QUEUED127.0.0.1:6379> exec1) (error) ERR value is not an integer or out of range # 虽然第一条命令报错了,但是依旧正常执行成功了!2) OK3) OK4) "v3"127.0.0.1:6379> get k2"v2"127.0.0.1:6379> get k3"v3"
监控! Watch (面试常问!)

悲观锁:

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

乐观锁:

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

Redis测监视测试

jedis

使用Java来操作redis

什么是jredis Redis 官方推荐的Java连接开发工具!使用Java操作Redis中间件

测试

1、导入依赖

2、编码测试

public class TestPing { public static void main(String[] args) {// Jedis jedis = new Jedis(); Jedis jedis = new Jedis("127.0.0.1",6379); System.out.println(jedis.ping()); }}
  • 连接数据库
  • 连接数据库
  • 修改redis的配置文件vim /usr/local/bin/myconfig/redis.conf
  • 1.将只绑定本地注释
  • 保护模式为no
  • 允许后台运行
  • 开放端口6379
- - firewall-cmd --zone=public --add-port=6379/tcp --permanetsystemctl restart firewalld.service
  • 操作命令
public class TestPing { public static void main(String[] args) { Jedis jedis = new Jedis("192.168.xx.xxx", 6379); String response = jedis.ping(); System.out.println(response); // PONG }}
  • 断开连接

3、事务

public class TestTX { public static void main(String[] args) { Jedis jedis = new Jedis("39.99.xxx.xx", 6379); JSONObject jsonObject = new JSONObject(); jsonObject.put("hello", "world"); jsonObject.put("name", "kuangshen"); // 开启事务 Transaction multi = jedis.multi(); String result = jsonObject.toJSONString(); // jedis.watch(result) try { multi.set("user1", result); multi.set("user2", result); // 执行事务 multi.exec(); }catch (Exception e){ // 放弃事务 multi.discard(); } finally { // 关闭连接 System.out.println(jedis.get("user1")); System.out.println(jedis.get("user2")); jedis.close(); } }}

常用API

String

List

Map

spring boot 整合

1.导入依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId></dependency>

springboot 2.x后 ,原来使用的 Jedis 被 lettuce 替换。

jedis:采用的直连,多个线程操作的话,是不安全的。如果要避免不安全,使用jedis pool连接池!更像BIO模式lettuce:采用netty,实例可以在多个线程中共享,不存在线程不安全的情况!可以减少线程数据了,更像NIO模式

我们在学习SpringBoot自动配置的原理时,整合一个组件并进行配置一定会有一个自动配置类xxxAutoConfiguration,并且在spring.factories中也一定能找到这个类的完全限定名。Redis也不例外。

redis 初探_字符串

那么就一定还存在一个RedisProperties类

redis 初探_Java_02

之前我们说SpringBoot2.x后默认使用Lettuce来替换Jedis,现在我们就能来验证了。

先看Jedis:

redis 初探_字符串_03

@ConditionalOnClass注解中有两个类是默认不存在的,所以Jedis是无法生效的

然后再看Lettuce

redis 初探_字符串_04

redis完美生效。哈哈哈哈哈哈哈哈哈哈哈哈哈

现在我们回到RedisAutoConfiguratio

redis 初探_redis_05

只有两个简单的Bean

  • RedisTemplate
  • StringRedisTemplate

当看到xxTemplate时可以对比RestTemplat、SqlSessionTemplate,通过使用这些Template来间接操作组件。那么这俩也不会例外。分别用于操作Redis和Redis中的String数据类型。

在RedisTemplate上也有一个条件注解,说明我们是可以对其进行定制化的

说完这些,我们需要知道如何编写配置文件然后连接Redis,就需要阅读RedisProperties

redis 初探_Java_06

这是一些基本的配置属性。

还有一些连接池相关的配置。注意使用时一定使用Lettuce的连接池。

redis 初探_Java_07

2.编写配置文件
# 配置redisspring.redis.host=39.99.xxx.xxspring.redis.port=6379
3.使用RedisTemplate
@SpringBootTestclass Redis02SpringbootApplicationTests { @Autowired private RedisTemplate redisTemplate; @Test void contextLoads() { // redisTemplate 操作不同的数据类型,api和我们的指令是一样的 // opsForValue 操作字符串 类似String // opsForList 操作List 类似List // opsForHah // 除了基本的操作,我们常用的方法都可以直接通过redisTemplate操作,比如事务和基本的CRUD // 获取连接对象 //RedisConnection connection = redisTemplate.getConnectionFactory().getConnection(); //connection.flushDb(); //connection.flushAll(); redisTemplate.opsForValue().set("mykey","kuangshen"); System.out.println(redisTemplate.opsForValue().get("mykey")); }}
测试结果

此时我们回到Redis查看数据时候,惊奇发现全是乱码,可是程序中可以正常输出:

这时候就关系到存储对象的序列化问题,在网络中传输的对象也是一样需要序列化,否者就全是乱码。

我们转到看那个默认的RedisTemplate内部什么样子:

redis 初探_Java_08

在最开始就能看到几个关于序列化的参数。

默认的序列化器是采用JDK序列化器

redis 初探_Java_09

而默认的RedisTemplate中的所有序列化器都是使用这个序列化器:

redis 初探_Java_10

后续我们定制RedisTemplate就可以对其进行修改。

​​RedisSerializer​​提供了多种序列化方案:

  • 直接调用RedisSerializer的静态方法来返回序列化器,然后set
  • redis 初探_redis_11

  • 自己new 相应的实现类,然后set
  • 定制RedisTemplate的模板: 我们创建一个Bean加入容器,就会触发RedisTemplate上的条件注解使默认的RedisTemplate失效。
  • @Configurationpublic class RedisConfig { @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException { // 将template 泛型设置为 <String, Object> RedisTemplate<String, Object> template = new RedisTemplate(); // 连接工厂,不必修改 template.setConnectionFactory(redisConnectionFactory); /* * 序列化设置 */ // key、hash的key 采用 String序列化方式 template.setKeySerializer(RedisSerializer.string()); template.setHashKeySerializer(RedisSerializer.string()); // value、hash的value 采用 Jackson 序列化方式 template.setValueSerializer(RedisSerializer.json()); template.setHashValueSerializer(RedisSerializer.json()); template.afterPropertiesSet(); return template; }}

    这样一来,只要实体类进行了序列化,我们存什么都不会有乱码的担忧了。

    八、自定义redis工具类

    使用RedisTemplate需要频繁调用​​.opForxxx​​然后才能进行对应的操作,这样使用起来代码效率低下,工作中一般不会这样使用,而是将这些常用的公共API抽取出来封装成为一个工具类,然后直接使用工具类来间接操作Redis,不但效率高并且易用。

    工具类参考博客:

    ​​https://www.cnblogs.com/zeng1994/p/03303c805731afc9aa9c60dbbd32a323.html​​

    ​​https://www.cnblogs.com/zhzhlong/p/11434284.html​​

    九、redis.conf

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

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

    redis 初探_Java_12

    网络配置

    redis 初探_Java_13

    日志输出级别

    日志输出文件
    持久化规则

    由于Redis是基于内存的数据库,需要将数据由内存持久化到文件中

    持久化方式:

    • RDB
    • AOF
    RDB文件相关

    redis 初探_Java_14

    主从复制

    Security模块中进行密码设置

    客户端连接相关

    maxclients 10000 最大客户端数量maxmemory <bytes> 最大内存限制maxmemory-policy noeviction # 内存达到限制值的处理策略

    redis 中的默认的过期策略是 volatile-lru 。

    设置方式

    config set maxmemory-policy volatile-lru
    maxmemory-policy 六种方式

    **1、volatile-lru:**只对设置了过期时间的key进行LRU(默认值)

    2、allkeys-lru : 删除lru算法的key

    **3、volatile-random:**随机删除即将过期key

    **4、allkeys-random:**随机删除

    5、volatile-ttl : 删除即将过期的

    6、noeviction : 永不过期,返回错误

    AOF相关部分

    十、持久化—RDB

    RDB:Redis Databases

    什么是RDB

    在指定时间间隔后,将内存中的数据集快照写入数据库 ;在恢复时候,直接读取快照文件,进行数据的恢复 ;

    redis 初探_字符串_15

    默认情况下, Redis 将数据库快照保存在名字为 dump.rdb的二进制文件中。文件名可以在配置文件中进行自定义。

    工作原理

    在进行 ​​RDB​​​ 的时候,​​redis​​​ 的主线程是不会做 ​​io​​​ 操作的,主线程会 ​​fork​​

  • Redis 调用forks。同时拥有父进程和子进程。
  • 子进程将数据集写入到一个临时 RDB 文件中。
  • 当子进程完成对新 RDB 文件的写入时,Redis 用新 RDB 文件替换原来的 RDB 文件,并删除旧的 RDB 文件。
  • 这种工作方式使得 Redis 可以从写时复制(copy-on-write)机制中获益(因为是使用子进程进行写操作,而父进程依然可以接收来自客户端的请求。)

    redis 初探_字符串_16

    触发机制

  • save的规则满足的情况下,会自动触发rdb原则
  • 执行flushall命令,也会触发我们的rdb原则
  • 退出redis,也会自动产生rdb文件
  • save

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

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

    redis 初探_字符串_17

    flushall命令

    ​​flushall​​ 命令也会触发持久化 ;

    触发持久化规则

    满足配置条件中的触发条件 ;

    可以通过配置文件对 Redis 进行设置, 让它在“ N 秒内数据集至少有 M 个改动”这一条件被满足时, 自动进行数据集保存操作。

    redis 初探_redis_18

    redis 初探_Java_19

    bgsave

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

    redis 初探_字符串_20

    bgsave和save对比

    命令

    save

    bgsave

    IO类型

    同步

    异步

    阻塞?

    是(阻塞发生在fock(),通常非常快)

    复杂度

    O(n)

    O(n)

    优点

    不会消耗额外的内存

    不阻塞客户端命令

    缺点

    阻塞客户端命令

    需要fock子进程,消耗内存

    优缺点

    优点:

  • 适合大规模的数据恢复
  • 对数据的完整性要求不高
  • 缺点:

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

    ———————————————— 版权声明:本文为CSDN博主「每天进步一點點」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:​​​https://blog.csdn.net/DDDDeng_/article/details/108118544​​

    redis 面试题

    1.说一下你在项目中的应用场景

    • 5大value类型
    • 基本上就是缓存
    • 为的是服务无状态,延申思考,看你的项目有哪些数据结构或对象,在单击里需要单击锁,在分布式需要分布式锁,抽出来放到redis中
    • 无锁化

    2.redis是单线程还是多线程?

    • 无论什么版本,工作线程就是一个
    • 6.x高版本出现了IO多线程,
    • 你要真正的理解面向IO模型编程的时候,有内核的事,从内核把数据搬运到程序这里是第一步,然就搬运回来的数据做的计算式第二步 netty
    • 。。。

    3.redis存在线程安全问题么?为什么?

    • 重复2中的单线程串行
    • redis可以保障内部串行
    • 外界使用的时候要保障,业务上要自行保障顺序

    4.遇到过缓存穿透么?

    上一篇:What's new in Dubbo 3.1.4 and 3.2.0-beta.3
    下一篇:没有了
    网友评论