Redis安装、数据结构、命令、Jedis操作、持久化
Redis
Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。
Redis是一个开源的使用ANSI C语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
默认开启,会按照配置的指定时间将内存中的数据快照到磁盘中,创建一个dump.rdb文件,Redis启动时再恢复到内存中。
Redis会单独创建fork()一个子进程,将当前父进程的数据库数据复制到子进程的内存中,然后由子进程写入到临时文件中,持久化的过程结束了,再用这个临时文件替换上次的快照文件,然后子进程退出,内存释放。
需要注意的是,每次快照持久化都会将主进程的数据库数据复制一遍,导致内存开销加倍,若此时内存不足,则会阻塞服务器运行,直到复制结束释放内存;都会将内存数据完整写入磁盘一次,所以如果数据量大的话,而且写操作频繁,必然会引起大量的磁盘I/O操作,严重影响性能,并且最后一次持久化后的数据可能会丢失;
Redis官网
Redis中文网
Redis下载地址
本文使用Redis-x64-3.2.100版本。本文使用版本
Redis官网响应较慢的可以用中文网下载,本人下载的Redis-x64-3.2.100本进行学习,下载完解压即可,无需安装。
如上图所示,解压后的目录结构。
- redis-server.exe为服务端;
- redis-cli.exe为客户端。
- redis.windows.conf为配置文件;
一、Redis为什么效率快
- Redis是纯内存数据库,一般都是简单的存取操作,线程占用的时间很多,时间的花费主要集中在IO上,所以读取速度快。(纯内存操作)
- 使用的是非阻塞IO,IO多路复用,使用了单线程来轮询描述符,将数据库的开、关、读、写都转换成了事件,减少了线程切换时上下文的切换和竞争。(单线程操作,避免了频繁的上下文切换)
- 采用了单线程的模型,保证了每个操作的原子性,也减少了线程的上下文切换和竞争。(非阻塞I/O多路复用机制)
- 采用自己实现的事件分离器,效率比较高,内部采用非阻塞的执行方式,吞吐能力比较大。
- 全程使用hash(key-value)结构,读取速度快,还有一些特殊的数据结构,对数据存储进行了优化,如压缩表,对短数据进行压缩存储,再如,跳表,使用有序的数据结构加快读取的速度。
二、Redis的数据结构及命令
字符串(strings)
-
设置:
- set key value (设置的key相同时,覆盖之前的值)
127.0.0.1:6379> set username zhangsan
- set key value (设置的key相同时,覆盖之前的值)
-
查询:
- get key
127.0.0.1:6379> get username
- get key
- 删除:
- del key
127.0.0.1:6379> del username
- del key
散列(hashes)
- 设置:
- hset key field value (设置的值里面又是一个键值对)
127.0.0.1:6379> hset user name zhangsan
- hset key field value (设置的值里面又是一个键值对)
- 查询:
- hget key field
127.0.0.1:6379> hget user name
127.0.0.1:6379> hget user age
- hget key field
- 删除:
- hdel key field
127.0.0.1:6379> hdel user name
- hdel key field
- 查询所有:
- hgetall key
127.0.0.1:6379> hgetall user
- hgetall key
列表(lists)
- 设置:
- lpush key value1 将一个或多个值插入到列表头部。
127.0.0.1:6379> lpush username zhangsan
- rpush key value1 将一个或多个值插入到列表尾部。
127.0.0.1:6379> rpush username lisi
- lpush key value1 将一个或多个值插入到列表头部。
- 查询:
- lrange key start end
127.0.0.1:6379> lrange username 0 -1
- lrange key start end
- 删除:
- lpop key 从列表头部移除一个元素,并返回移除的值
127.0.0.1:6379> lpop username
- rpop key 从列表尾部移除一个元素,并返回移除的值
127.0.0.1:6379> rpop username
- lpop key 从列表头部移除一个元素,并返回移除的值
集合(sets)(无序集合)
- 设置:
- sadd key value
127.0.0.1:6379> sadd username zhangsan
127.0.0.1:6379> sadd username lisi
- sadd key value
- 查询:
- smembers key
127.0.0.1:6379> smembers username
- smembers key
- 删除:
- del key(移除并返回集合中的一个随机元素)
127.0.0.1:6379> del username
- del key(移除并返回集合中的一个随机元素)
有序集合(sorted sets)
-
设置:
- zadd key score value (scoure分数用来排序)
127.0.0.1:6379> zadd username 10 zhangsan
127.0.0.1:6379> zadd username 18 lisi
127.0.0.1:6379> zadd username 5 wangwu
- zadd key score value (scoure分数用来排序)
-
查询:
- zrange key start end(可以看到是根据分数排序的)
127.0.0.1:6379> zrange username 0 -1
- zrange key start end(可以看到是根据分数排序的)
-
删除:
- zrem key value
127.0.0.1:6379> zrem username zhangsan
- zrem key value
全局常用命令
- keys * (查询所有key值)
- type key (查询key的类型)
三、持久化
- Redis本身运行时数据保存在内存中,支持RDB和AOF两种持久化机制(这两种方式可以单独使用其中一种,或者混合使用),持久化功能有效地避免因进程退出造成的数据丢失问题,当下次重启时利用之前持久化的文件即可实现数据恢复。Redis默认就采用了一种持久化方式,即RDB。(Redis持久化的优先级:AOF>RDB)
RDB持久化(默认)
Redis默认支持RDB持久化。
- RDB方式是通过快照完成的,一段时间内Redis会自动将内存中的所有数据进行快照,并且存储到硬盘上(进行快照的条件在配置文件中指定)。当下次启动redis的时候,回去读取硬盘上的快照恢复数据,进而实现Redis的持久化。
持久化时,会在指定的目录中生成一个.rdb结尾的快照文件。
redis.conf配置文件
- 设置触发条件:
- 设置rdb文件路径:
测试
-
修改配置文件
-
启动Redis服务,指定配置文件
-
启动客户端,触发持久化
-
验证
-
关闭服务重新打开(直接获取可获得值)
AOF持久化(耗内存,不建议)
- RDB方式不能提供强一致性,如果Redis进程崩溃,那么两次RDB之间的数据也随之消失。那么AOF的出现很好的解决了数据持久化的实时性,AOF以独立日志的方式记录每次写命令,重启时再重新执行AOF文件中的命令来恢复数据。AOF会先把命令追加在AOF缓冲区,然后根据对应策略写入硬盘(appendfsync)。
Redis主进程fork子进程来执行AOF重写,这个子进程创建新的AOF文件来存储重写结果,防止影响旧文件。因为fork采用了写时复制机制,子进程不能访问在其被创建出来之后产生的新数据。Redis使用“AOF重写缓冲区”保存这部分新数据,最后父进程将AOF重写缓冲区的数据写入新的AOF文件中然后使用新AOF文件替换老文件。
以日志的形式记录每个写操作(读操作不记录),只需追加文件但不可以改写文件,Redis启动时会根据日志从头到尾全部执行一遍以完成数据的恢复工作。包括flushDB也会执行。
主要有两种方式触发:有写操作就写、每秒定时写(也会丢数据)。
因为AOF采用追加的方式,所以文件会越来越大,针对这个问题,新增了重写机制,就是当日志文件大到一定程度的时候,会fork出一条新进程来遍历进程内存中的数据,每条记录对应一条set语句,写到临时文件中,然后再替换到旧的日志文件(类似rdb的操作方式)。默认触发是当aof文件大小是上次重写后大小的一倍且文件大于64M时触发。
当两种方式同时开启时,数据恢复Redis会优先选择AOF恢复。一般情况下,只要使用默认开启的RDB即可,因为相对于AOF,RDB便于进行数据库备份,并且恢复数据集的速度也要快很多。
开启持久化缓存机制,对性能会有一定的影响,特别是当设置的内存满了的时候,更是下降到几百reqs/s。所以如果只是用来做缓存的话,可以关掉持久化。
-
配置文件修改
- always:命令写入aof缓冲区后,每一次写入都需要同步,直到写入磁盘(阻塞,系统调用fsync)结束后返回。显然和Redis高性能背道而驰,不建议配置。
- everysec:命令写入aof缓冲区后,在写入系统缓冲区直接返回(系统调用write),然后有专门线程每秒执行写入磁盘(阻塞,系统调用fsync)后返回。
- no:命令写入aof缓冲区后,在写入系统缓冲区直接返回(系统调用write)。之后写入磁盘(阻塞,系统调用fsync)的操作由操作系统负责,通常最长30s。
-
启动Redis服务时指定配置文件
-
启动客户端,触发持久化
-
验证
-
关闭服务重新打开(直接获取可获得值)
四、Java操作Redis
下载 jedis.jar
Java工程目录结构:
字符串(strings)
@Test
public void test_String() {
Jedis jedis = new Jedis();
jedis.set("username", "zhangsan");
System.out.println(jedis.get("username"));
}
散列(hashes)
@Test
public void test_Hash() {
Jedis jedis = new Jedis();
jedis.hset("user", "name", "mengmeng");
jedis.hset("user", "age", "18");
System.out.println(jedis.hget("user", "name"));
System.out.println(jedis.hget("user", "age"));
System.out.println(jedis.hgetAll("user").toString());
}
列表(lists)
@Test
public void test_list() {
Jedis jedis = new Jedis();
jedis.lpush("list", "hehe"); // hehe
jedis.lpush("list", "1111"); // 1111, hehe
jedis.rpush("list", "heihei"); // 1111, hehe, heihei
jedis.rpush("list", "xixi"); // 1111, hehe, heihei, xixi
System.out.println(jedis.lrange("list", 0, -1));
System.out.println(jedis.lpop("list"));
System.out.println(jedis.lrange("list", 0, -1));
System.out.println(jedis.rpop("list"));
System.out.println(jedis.lrange("list", 0, -1));
}
集合(sets)(无序集合)
@Test
public void test_set() {
Jedis jedis = new Jedis();
jedis.sadd("setdemo", "zhangsan", "lisi", "wangwu", "zhaoliu");
System.out.println(jedis.smembers("setdemo"));
System.out.println(jedis.srem("setdemo", "lisi"));
System.out.println(jedis.smembers("setdemo"));
}
有序集合(sorted sets)
@Test
public void test_sortedset() {
Jedis jedis = new Jedis();
jedis.zadd("sortedset", 10, "zhangsan");
jedis.zadd("sortedset", 12, "list");
jedis.zadd("sortedset", 2, "wangwu");
jedis.zadd("sortedset", 80, "zhanxing");
System.out.println(jedis.zrange("sortedset", 0, -1));
System.out.println(jedis.zrem("sortedset", "wangwu"));
System.out.println(jedis.zrange("sortedset", 0, -1));
}