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"mongodlist
在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) 0zset(有序集合)
三种特殊数据类型
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
- 操作命令
- 断开连接
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也不例外。
那么就一定还存在一个RedisProperties类
之前我们说SpringBoot2.x后默认使用Lettuce来替换Jedis,现在我们就能来验证了。
先看Jedis:
@ConditionalOnClass注解中有两个类是默认不存在的,所以Jedis是无法生效的
然后再看Lettuce
redis完美生效。哈哈哈哈哈哈哈哈哈哈哈哈哈
现在我们回到RedisAutoConfiguratio
只有两个简单的Bean
- RedisTemplate
- StringRedisTemplate
当看到xxTemplate时可以对比RestTemplat、SqlSessionTemplate,通过使用这些Template来间接操作组件。那么这俩也不会例外。分别用于操作Redis和Redis中的String数据类型。
在RedisTemplate上也有一个条件注解,说明我们是可以对其进行定制化的
说完这些,我们需要知道如何编写配置文件然后连接Redis,就需要阅读RedisProperties
这是一些基本的配置属性。
还有一些连接池相关的配置。注意使用时一定使用Lettuce的连接池。
2.编写配置文件
# 配置redisspring.redis.host=39.99.xxx.xxspring.redis.port=63793.使用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内部什么样子:
在最开始就能看到几个关于序列化的参数。
默认的序列化器是采用JDK序列化器
而默认的RedisTemplate中的所有序列化器都是使用这个序列化器:
后续我们定制RedisTemplate就可以对其进行修改。
RedisSerializer提供了多种序列化方案:
这样一来,只要实体类进行了序列化,我们存什么都不会有乱码的担忧了。
八、自定义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是基于内存的数据库,需要将数据由内存持久化到文件中
持久化方式:
- RDB
- AOF
RDB文件相关
主从复制
Security模块中进行密码设置
客户端连接相关
maxclients 10000 最大客户端数量maxmemory <bytes> 最大内存限制maxmemory-policy noeviction # 内存达到限制值的处理策略redis 中的默认的过期策略是 volatile-lru 。
设置方式
config set maxmemory-policy volatile-lrumaxmemory-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 将数据库快照保存在名字为 dump.rdb的二进制文件中。文件名可以在配置文件中进行自定义。
工作原理
在进行 RDB 的时候,redis 的主线程是不会做 io 操作的,主线程会 fork
这种工作方式使得 Redis 可以从写时复制(copy-on-write)机制中获益(因为是使用子进程进行写操作,而父进程依然可以接收来自客户端的请求。)
触发机制
save
使用 save 命令,会立刻对当前内存中的数据进行持久化 ,但是会阻塞,也就是不接受其他操作了;
由于 save 命令是同步命令,会占用Redis的主进程。若Redis数据非常多时,save命令执行速度会非常慢,阻塞所有客户端的请求。
flushall命令
flushall 命令也会触发持久化 ;
触发持久化规则
满足配置条件中的触发条件 ;
可以通过配置文件对 Redis 进行设置, 让它在“ N 秒内数据集至少有 M 个改动”这一条件被满足时, 自动进行数据集保存操作。
bgsave
bgsave 是异步进行,进行持久化的时候,redis 还可以将继续响应客户端请求 ;
bgsave和save对比
命令
save
bgsave
IO类型
同步
异步
阻塞?
是
是(阻塞发生在fock(),通常非常快)
复杂度
O(n)
O(n)
优点
不会消耗额外的内存
不阻塞客户端命令
缺点
阻塞客户端命令
需要fock子进程,消耗内存
优缺点
优点:
缺点:
十一、持久化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.遇到过缓存穿透么?