Redis - 初级二
Redis高级命令(jedis)
keys *
返回满足的所有键,支持模糊查询
jedis.set("name","xiaoming");
jedis.hset("user","name","xiaoming");
jedis.lpush("score ","100");
jedis.sadd("height","180");
jedis.zadd("body_weight",1,"120");
// [height, score , user, name, body_weight]
jedis.keys("*");
// 以s结尾的,[]
System.out.println(jedis.keys("*s"));
// 以s开头的,[score ]
System.out.println(jedis.keys("s*"));
// 含有s的,[score , user]
System.out.println(jedis.keys("*s*"));
exists
是否存在指定的key
jedis.set("name","xiaoming");
jedis.set("age","18");
// 2
System.out.println(jedis.exists("name", "age"));
// true
System.out.println(jedis.exists("name"));
// false
System.out.println(jedis.exists("666"));
expire、ttl、persist
expire
设置某个key的过期时间
ttl
查看剩余时间
persist
取消过期时间
惰性删除:过期不会被马上删除,只是把状态改为过期状态。获取它的时候,先判断key的状态,已经过期的话就删除,然后告诉操作人该key不存在
// 存入键值对,设置失效时间为10s
jedis.setex("age", 10, "18");
// 获取指定的key:18
System.out.println(jedis.get("age"));
// 线程暂停6s
Thread.sleep(6000);
// 查询当前key还剩多少时间过期:4
System.out.println(jedis.ttl("age"));
// 再暂停6s
Thread.sleep(6000);
// 获取指定的key:null
System.out.println(jedis.get("age"));
// 存入键值对,设置失效时间为10s
jedis.setex("age", 3, "18");
// 取消过期时间
jedis.persist("age");
// 线程暂停4s
Thread.sleep(4000);
// 获取指定的key:18
System.out.println(jedis.get("age"));
flushdb、flushall
flushdb
清除当前数据库数据
flushall
清除所有数据库数据
jedis.set("name", "xiaoming");
// xiaoming
System.out.println(jedis.get("name"));
// 清除当前数据库数据
jedis.flushDB();
// null
System.out.println(jedis.get("name"));
jedis.set("name", "xiaoming");
// xiaoming
System.out.println(jedis.get("name"));
// 清除所有数据库数据
jedis.flushAll();
// null
System.out.println(jedis.get("name"));
select
选择数据库0~15
jedis.select(2);
move
将当前数据库指定的key转移到指定的数据库中
// 当前数据库0
jedis.set("name", "xiaoming");
// 转移到数据库2中
jedis.move("name", 2);
randomKey
随机返回数据库的一个key
System.out.println(jedis.randomKey());
rename
重命名key
jedis.set("name", "xiaoming");
// [name]
System.out.println(jedis.keys("*"));
jedis.rename("name","username");
// [username]
System.out.println(jedis.keys("*"));
dbSize
查看当前数据库key的数量
jedis.set("name", "xiaoming");
jedis.set("age", "18");
// 2
System.out.println(jedis.dbSize());
info、config get、config get *
info
获取数据库信息
config get
用于获取 redis服务的配置参数
config get *
用于获取所有的配置
Redis安全问题
-
Redis 默认情况下,会绑定在 0.0.0.0:6379,这样将会将 Redis 服务暴露到公网上,如果在没有开启认证的情况下,可以导致任意用户在可以访问目标服务器的情况下未授权访问 Redis 以及读取 Redis 的数据。攻击者在未授权访问 Redis 的情况下可以利用 Redis 的相关方法,可以成功在 Redis 服务器上写入公钥,进而可以使用对应私钥直接登录目标服务器。
-
redis速度非常快,一个外部用户在一秒内可进行大量的密码尝试,这意味你需要设定非常强大的密码来方式暴力破解.
方法:
- 禁止一些高危命令
修改 redis.conf 文件
rename-command FLUSHALL ""
rename-command CONFIG ""
rename-command EVAL ""
来禁用远程修改 DB 文件地址
- 以低权限运行 Redis 服务
为 Redis 服务创建单独的用户和家目录,并且配置禁止登陆
- 为 Redis 添加密码验证
修改 redis.conf 文件
requirepass mypassword
- 禁止外网访问 Redis
修改 redis.conf 文件,添加或修改
bind 127.0.0.1
使得 Redis 服务只在当前主机可用
Redis事务之面试题
Redis 事务的本质是一组命令的集合。事务支持一次执行多个命令,一个事务中所有命令都会被序列化。在事务执行过程,会按照顺序串行化执行队列中的命令,其他客户端提交的命令请求不会插入到事务执行命令序列中。
redis事务就是一次性、顺序性、排他性的执行一个队列中的一系列命令。
Redis事务的三个阶段:
- 开始事务
- 命令入队
- 执行事务
面试问题:redis存在事务吗?
Redis中,单条命令是原子性执行的,但事务不保证原子性,且没有回滚。事务中任意命令执行失败,其余的命令仍会被执行。
Redis的事务只是把多个命令入队,等待一起执行而已。
所以说redis不存在事务
redis持久化机制(浅谈)
什么是持久化?简单来讲就是将数据放到断电后数据不会丢失的设备中,也就是我们通常理解的硬盘上
首先我们来看一下redis数据库在进行写操作时到底做了哪些事,主要有下面五个过程:
- 客户端向服务端发送写操作(数据在客户端的内存中)。
- 数据库服务端接收到写请求的数据(数据在服务端的内存中)。
- 服务端调用write这个系统调用,将数据往磁盘上写(数据在系统内存的缓冲区中)。
- 操作系统将缓冲区中的数据转移到磁盘控制器上(数据在磁盘缓存中)。
- 磁盘控制器将数据写到磁盘的物理介质中(数据真正落到磁盘上)。
故障分析
- 当数据库系统故障时,这时候系统内核还是完好的。那么此时只要我们执行完了第3步,那么数据就是安全的,因为后续操作系统会来完成后面几步,保证数据最终会落到磁盘上。
- 当系统断电时,这时候上面5项中提到的所有缓存都会失效,并且数据库和操作系统都会停止工作。所以只有当数据在完成第5步后,才能保证在断电后数据不丢失。
这里得出一个结论,redis存储的数据可能会丢失,说明redis的定位是缓存
在这探究的是redis,就不对其他有过多的讲解
上面提到了数据库系统故障会导致存储在内存的数据会丢失,那么我们关心的应该是服务端什么时候调用write把数据写到磁盘上。
RDB持久化和AOF持久化机制
RDB机制
RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘。(redis默认是这种方式)这种方式是就是将内存中数据以快照的方式写入到二进制文件中,默认的文件名为dump.rdb。可以通过配置设置自动做快照持久化的方式。
save 900 1 #900秒内如果超过1个key被修改,则发起快照保存
save 300 10 #300秒内容如超过10个key被修改,则发起快照保存
save 60 10000
AOF机制
AOF机制使每一个写命令都通过write函数追加到文件中(默认是 appendonly.aof),保存起来。
当redis重启时会通过重新执行文件中保存的写命令来在内存中重建整个数据库的内容。当然由于os会在内核中缓存 write做的修改,所以可能不是立即写到磁盘上。这样aof方式的持久化也还是有可能会丢失部分修改。不过我们可以通过配置文件告诉redis我们想要 通过fsync函数强制os写入到磁盘的时机。
appendonly yes //启用aof持久化方式
appendfsync always //每次收到写命令就立即强制写入磁盘,最慢的,但是保证完全的持久化,不推荐使用
appendfsync everysec //每秒钟强制写入磁盘一次,在性能和持久化方面做了很好的折中,推荐
appendfsync no //完全依赖os,性能最好,持久化没保证
Redis内存淘汰机制及过期Key处理
Redis内存淘汰机制
Redis内存淘汰机制是指当内存使用达到上限(可通过maxmemory配置,0为不限制,即服务器内存上限),根据一定的算法来决定淘汰掉哪些数据,以保证新数据的存入。
常见的内存淘汰机制分为四大类:
1. LRU:LRU是Least recently used,最近最少使用的意思,简单的理解就是从数据库中删除最近最少访问的数据,该算法认为,你长期不用的数据,那么被再次访问的概率也就很小了,淘汰的数据为最长时间没有被使用,仅与时间相关。
2. LFU:LFU是Least Frequently Used,最不经常使用的意思,简单的理解就是淘汰一段时间内,使用次数最少的数据,这个与频次和时间相关。
3. TTL:Redis中,有的数据是设置了过期时间的,而设置了过期时间的这部分数据,就是该算法要解决的对象。如果你快过期了,不好意思,我内存现在不够了,反正你也要退休了,提前送你一程,把你干掉吧。
4. 随机淘汰:生死有命,富贵在天,是否被干掉,全凭天意了。
通过maxmemroy-policy可以配置具体的淘汰机制,看了网上很多文章说只有6种,其实有8种,可以看Redis5.0的配置文件,上面有说明:
1. volatile-lru -> 找出已经设置过期时间的数据集,将最近最少使用(被访问到)的数据干掉。
2. volatile-ttl -> 找出已经设置过期时间的数据集,将即将过期的数据干掉。
3. volatile-random -> 找出已经设置过期时间的数据集,进行无差别攻击,随机干掉数据。
4. volatile-lfu -> 找出已经设置过期时间的数据集,将一段时间内,使用次数最少的数据干掉。
5. allkeys-lru ->与第1个差不多,数据集从设置过期时间数据变为全体数据。
6. allkeys-lfu -> 与第4个差不多,数据集从设置过期时间数据变为全体数据。
7. allkeys-random -> 与第3个差不多,数据集从设置过期时间数据变为全体数据。
8. no-enviction -> 什么都不干,报错,告诉你内存不足,这样的好处是可以保证数据不丢失,这也是系统默认的淘汰策略。
Redis部分内存淘汰机制会导致数据的丢失,是Redis
Redis过期Key清除策略
Redis中大家会对存入的数据设置过期时间,那么这些数据如果过期了,Redis是怎么样把他们消灭掉的呢?我们一起来探讨一下。下面介绍三种清除策略:
惰性删除:当访问Key时,才去判断它是否过期,如果过期,直接干掉。这种方式对CPU很友好,但是一个key如果长期不用,一直存在内存里,会造成内存浪费。
定时删除:设置键的过期时间的同时,创建一个定时器,当到达过期时间点,立即执行对Key的删除操作,这种方式最不友好。
定期删除:隔一段时间,对数据进行一次检查,删除里面的过期Key,至于要删除多少过期Key,检查多少数据,则由算法决定。举个例子方便大家理解:Redis每秒随机取100个数据进行过期检查,删除检查数据中所有已经过期的Key,如果过期的Key占比大于总数的25%,也就是超过25个,再重复上述检查操作。
Redis服务器实际使用的是惰性删除和定期删除两种策略:通过配合使用这两种删除策略,可以很好地在合理使用CPU和避免浪费内存之间取得平衡。