【分布式架构之旅】Redis入门
前言
DNA.png昨天和室友去包夜,玩了一晚上的
LOL
,跪了一整夜,但是很开心。从S1
末开始玩LOL
的我,到现在还是青铜,真是菜的抠脚。最近负能力满满的,唯有睡觉和学习才可解忧愁。今天也看了慕课网上面的《Redis
入门》,来记一下学习笔记。(写这篇文章开头的时候应该是一个星期之前)
NoSQL概述
-
NoSQL
就是Not Only SQL
的意思,是非关系型数据库。 -
为什么需要
NoSQL
?-
High performance
- 高并发读写 -
Huge Storage
- 海量数据的高效率存储和访问 -
High Scalability & High Availability
- 高可扩展性和高可用性
-
-
NoSQL数据库的四大分类
-
键值(
Key - Value
)存储:优点是快速查询,缺点存储的数据缺少结构化。 -
列存储:优点是查询比较快,扩展性比较强,缺点是功能相对局限。
-
文档数据库:对应的产品就是
MongoDB
。对数据结构要求不是特别严格,查询性能不能特别高,缺少统一查询的语法。 -
图形数据库:优点是利用图结构相关的算法,缺点是需要对整个图进行结算才能得出结果,不能作为分布式的解决方案。
-
- 现在来说说
NoSQL
的特点,美滋滋。- 易扩展
- 灵活的数据模型
- 高可用
- 大数据量,高性能
Redis的概述
-
高性能键值对数据库,支持的键值数据类型:
-
字符串类型 -
String
-
列表类型 -
Set
-
有序集合类型 -
Sorted Set
-
散列类型 -
Hash
-
集合类型 -
List
-
-
Redis
的应用场景-
缓存
-
任务队列
-
网站访问统计
-
应用排行榜
-
分布式集群架构中的session分离
-
Redis在Linux上的使用
可以看我这篇文章【Linux学习】 Redis常用的一些指令
Jedis的入门
- 我们要在
Java
平台上使用redis
,肯定需要Jedis
这个客户端。首先在pom
文件中引入Jedis
的依赖
<!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
- 普通方式创建Jedis对象。
public void methodOne() {
Jedis jedis = new Jedis("100.64.84.47", 6379);
jedis.set("name", "cmazxiaoma");
String value = jedis.get("name");
System.out.println(value);
jedis.close();
}
- 通过线程安全的连接池来创建Jedis对象。
public void methodTwo() {
//获得连接池的配置对象
JedisPoolConfig config = new JedisPoolConfig();
//设置最大连接数
config.setMaxTotal(30);
//设置最大空闲连接数
config.setMaxIdle(10);
//获得连接池
JedisPool jedisPool = new JedisPool(config, "100.64.84.47", 6379);
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
String value = jedis.get("name");
System.out.println(value);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (jedis != null) {
jedis.close();
}
jedisPool.close();
}
}
}
- 编写测试类。
public class JedisDemo1Test {
private JedisDemo1 demo;
@Before
public void setUp() {
demo = new JedisDemo1();
}
@Test
public void methodOne() throws Exception {
demo.methodOne();
}
@Test
public void methodTwo() throws Exception {
demo.methodTwo();
}
}
-
运行
image.pngTest Case
,测试成功。
-
我们在
image.pngXshell
软件,输入get name
指令,也可以看到输出cmazxiaoma
。
Redis的数据结构
String
-
Key
定义的注意点:- 不要过长。
- 不要过短。
- 统一的命名规范。
-
存储
String
:- 二进制安全的,存入和获取的数据相同。
-
Value
最多可以容纳的数据长度是512M
。
-
存储
String
常用的命令:-
赋值
image.png -
取值
image.png -
删除
image.png -
数值增减
-
如果属性不存在的话,那么integer
类型默认为0
。
如果name
属性的值不能转换成integer
类型,那么会抛出ERR is not an integer or out of range
异常。
decr
指令也是一样的。
- 扩展命令
incrby
、decrby
也是一样的,很简单。
image.png
append
指令可以拼接字符串。
如果append key cmazxiaoma
。这个key
不存在的话,首先会创建这个key
,然后存入cmazxiaoma
内容,接着输出cmazxiaoma
。
Hash
-
赋值,可以使用
image.pnghset myhash key value
单一赋值、hmset myhash key value key value
多次赋值。
-
取值,可以用
image.pnghget myhash key
单一取值、hmget myhash key key
多个取值、hgetall
取出所有key
所对应的值。
-
删除 ,可以用
image.pnghdel myhash key
删除单一的key
。
hdel myhash key key
删除多个的key
。
del myhash
删除myhash
中所有的key
。
-
数值增减,
image.pnghincrby myhash key 100
-
使用
image.pnghexists myhash key
判断key
是否在myhash
中存在,存在返回1
,不存在返回0
。
-
image.pnghlen myhash
获取myhash
中存在key
的数量。
-
image.pnghkeys myhash
获取myhash
中所有的key
。
-
image.pnghvals myhash
获取myhash
中所有的values
。
List
-
存储list:
-
ArrayList
使用数组方式 -
LinkedList
使用双向链表方式
-
-
两端添加
- 使用
lpush a b c
命令在左端添加,那么c
肯定是在最左端。
image.png - 使用
rpush a b c
命令在右端添加,那么c
肯定是在最右端。
image.png
- 使用
-
两端弹出
- 使用
lpop mylist
弹出mylist
中头部元素、rpop mylist2
弹出mylist2
尾部中的元素。它们都是3
。
image.png - 我们接着来查看
mylist
、mylist2
。
image.png
- 使用
- 查看列表
- 使用
lrange mylist 0 5
来查看mylist
列表,mylist
插入的方式从头结点开始添加的,那么输出肯定是321abc
。
image.png - 使用
lrange mylist2 0 5
查看mylist2
列表,mylist2
插入的方式是从最右端添加的,那么输出肯定是abc123
image.png
- 使用
-
获取列表元素的个数
- 使用
llen mylist
命令
image.png
- 使用
-
扩展命令
-
使用
image.pnglpushx mylist x
,使插入的元素在头部位置。
-
使用
image.pngrpushx mylist x
,使插入的元素在尾部位置。
-
使用
image.pnglrem mylist count element
,count
代表是删除的次数,element
代表是需要删除的元素。如果count > 0
代表删除的方式从头到尾,删除count
个element
,count < 0
代表删除的方式从尾到头,删除count
个element
。如果count = 0
,删除mylist
中所有和element
相同的元素。
image.png -
image.pnglrem mylist 2 test1
-
image.pnglrem mylist -2 test1
-
image.pnglrem mylist 0 cmazxiaoma
-
在某一个下标位置插入元素。
image.pnglset mylist index element
-
在目标元素之前插入指定的元素。
image.pnglinsert mylist before helloworl before_helloworld
image.png -
在目标元素之后插入指定的元素。
image.pnglinsert mylist helloworld after after_helloworld
-
弹出
image.pngmylist
中最后一个元素,并插入到mylist
中的头部。rpoplpush mylist mylist
-
image.pngrpoplpush
使用场景
-
Set
和List
类型不同的是,Set
集合中不允许出现重复的元素。
- 添加/删除元素
- 添加
sadd myset a b c
,如果我们重复添加相同的元素,肯定是不成功的。比如sadd myset a
。
image.png - 删除
srem myset a
,删除myset
中的a
元素。
image.png - 查看
myset
中的元素。smembers myset
image.png - 查看指定元素是否是
myset
中的成员。sismember myset a
,返回0
代表不存在,返回1
代表存在。
image.png
- 添加
-
获得集合中的元素,
smembers myset
-
集合中的差集运算,
image.pngsdiff myset2 myset
。myset2
中元素有b,c,d
。myset
中元素有b,c
。它们之间的差集运算结果应该为d
。
-
集合中的交集运算,
image.pngsinter myset2 myset
,应该输出cb
。
-
集合中的并集运算,
image.pngsunion myset2 myse
t,应该输出cbd
。
-
-
扩展命令
-
image.pngscard myset
查看myset
有多少个元素。
-
image.pngsrandmember myset
随机返回myset
中的一个元素。
-
image.pngsdiffstore new_myset myset2 myset
把myset
和myset2
差集元素的结果存储到new_myset
中。(sinterstore
,sunionstore
也是一样的用法)
-
-
Set使用场景
- 跟踪一些唯一性的数据。
- 用于维护数据对象之间的关联关系。
SortedSet
SortedSet
中的成员在集合中的位置是有序的。
-
添加元素
image.pngzadd mysort 70 cmazxiaoma 80 xiaoma 100 doudou
-
获得元素
-
image.pngzcard mysort
获得mysort
中所有元素的个数
-
获得
image.pngmysort
中name
为cmazxiaoma
对应的成绩。zscore mysort cmazxiaoma
-
-
删除元素,
image.pngzrem mysort cmazxiaoma deli doudou xiaoma
-
范围查询
image.pngzrange mysort 0 -1
,输出的key
是按成绩正序排列。
-
如果输出的数据项要带上成绩的话,指令应该是
image.pngzrange mysort 0 -1 withscores
-
如果输出的数据项想按成绩逆序排序,那么就应该
image.pngzrevrange mysort 0 -1 withscores
-
如果想按排名范围进行删除的话,那么应该
image.pngzremrangebyrank mysort 0 2
-
如果想按成绩范围进行删除的话,那么应该
image.pngzremrangebyscore mysort 0 10
。顾名思义删除成绩在0-10
之内的数据项。
-
-
扩展命令
-
查看分数在
image.png0-10
之内的学生信息,zrangebyscore mysort 0 10 withscores
-
查看分数在
image.png0-100
之内且在第1
行-第2
行的学生信息。zrangebyscore mysort 0 100 withiscores limit 0 2
-
给
image.pngcmazxiaoma
的成绩加100
分。zincrby mysort cmazxiaoma 100
-
查看成绩
image.png0-10
之间的学生的个数。zcount mysort 0 10
-
-
Sorted Set
使用场景-
如大型在线游戏积分排行榜
-
构建索引数据
-
Redis中的通用命令
-
image.pngkey *
获取所有redis中的key
-
image.pngkeys my*
获取所有redis
中以my
开头的key
-
image.pngexists mylist
查看redis
中是否存在mylis
t,0
代表不存在,1
代表存在。
-
image.pngrename name new_name
给名字为name
的数据结构重命名为new_name
-
image.pngexpire new_name 10
设置redis
中new_name
过期时间,通过ttl new_name
看到其距离过期的时间。
-
image.pngtype mylist
,可以查看mylist
对应的数据结构类型。
Redis的事务
-
Redis
相关的特性:-
多数据库
-
Redis
事务
-
-
一个
image.pngRedis
最多可以提供16
个数据库,下标分别是0-15
。客户端默认连接的是第0
号数据。我们可以通过select index
来选择数据库。
-
我们想把第
image.png0
号的数据库中某些key
移动到第1
号数据库里面,那么我们该怎么做呢。通过move cmazxiaoma_test_mayday_5 1
就可以完成。
-
我们可以通过
multi
、exec
、discard
来完成事务操作。事务执行期间,Redis
不会为其他客户端提供任何服务,以保证事务中的所有命令原子执行。multi
相当于开启事务,exec
相当于提交,discard
相当于回滚。 -
首先我们在第一个客户端进行如下操作。我们在第一个客户端开启了事务,在事务中我们
redis第一个客户端.pngincr num
2
次,按理来说,get num
应该等于4
。
-
我们在第二个客户端输入
image.pngget num
,发现num
还是2
。那么证明了我们的结论:事务执行期间,Redis
不会为其他客户端提供任何服务,以保证事务中的所有命令原子执行。
-
如果我们在第一个客户端提交了事务。
image.png -
接着我们在第二个客户端
image.pngget num
,发现可以num
的值得到了更新。
-
我们可以演示一下回滚操作,首先
image.pngset user cmazxiaoma
,接着开启事务,在事务中set user xiaoma
,然后进行回滚操作,发现get user
依然是cmazxiaoma
。
Redis的持久化
Redis
的性能体现在它把数据都保存在内存当中。我们把内存中的数据同步到硬盘当中的操作称之为持久化。
-
Redis
持久化方式:-
RDB
方式,在指定的时间内,把内存中的数据快照写入到硬盘当中。 -
AOF
方式,将以日志的形式记录服务器所处理的每一个操作。当Redis
服务器启动之初,它会读取该aof
文件,会重新构建我们的数据库。保证我们启动之后,保证数据的完整性。 -
无持久化,我们可以通过配置禁止
Redis
服务器的持久化,我们认为Redis
就是缓存的一种机制了。 -
同时使用。
-
-
RDB
:-
默认情况下,每隔一段时间
redis
服务器程序会自动对数据库做一次遍历,把内存快照写在一个叫做"dump.rdb"
的文件里,这个持久化机制叫做SNAPSHOT
。有了SNAPSHOT
后,如果服务器宕机,重新启动redis
服务器时,redis
会自动加载"dump.rdb"
,将数据库状态恢复上一次SNAPSHOT
的状态。 -
Redis
服务器初始化过程中,设定了定时时间,每隔一段时间就会触发持久化操作,进入定时事件处理程序中,就会fork
出子进程来进行持久化操作。 -
Redis
服务器预设了save
指令,客户端可要求服务器进程中断服务,执行持久化操作。 -
我们可以通过
image.pngvim /etc/redis.conf
打开配置文件,可以看到以下配置。
-
同时我们还可以看到内存快照输出在
image.pngfile
文件名。
优缺点:
-
如果数据集很大,
RDB
相对于AOF
启动效率很更高。 -
如果想保证数据的高可用性,最大限度的避免数据的丢失,
RDB
将不是一个好的选择。因为系统在定时持久化操作之前,还没来得及在硬盘写入数据就发生宕机的话,就造成了数据的丢失。 -
RDB
通过fork
出子线程来完成数据持久化操作,如果当数据集很大的时候,可能会导致服务器停止几百ms
,或者几s
。
-
-
AOF
(append only file
): -
对于
image.pngRedis
服务器而言,其缺省的机制是RDB
,如果需要使用AOF
,则需要修改appendonly no
改成appendonly yes
。Redis
在每一次收到数据修改的命令之后,都会将其追加到AOF
文件中。在Redis
下一次重新启动时,需要加载AOF
文件中的信息来构建最新的数据到内存中。
image.png -
可以记录服务器的所有写操作。在服务器重新启动时,会把所有的写操作重新执行一遍从而实现数据的备份。当写操作集过大(比原有的数据集还大),
Redis
会重写写操作集。 -
带来更好的数据安全性,有
3
种同步策略,每秒同步,每修改同步,不同步。 每秒同步也是异步完成的,效率也非常高。缺点是一旦系统发生宕机的现象,那么这一秒中的修改的数据就会发生丢失。每修改同步,我们可以视为同步持久化。每一次发生数据的变化,就会立即的记录在磁盘,这种效率很低,但是很安全。 -
采用
append
追加的模式,就算系统发生宕机,也不会影响我们日志文件中已经存在的内容。然而我们本次操作中,只写入了一半数据就出现了系统崩溃的问题。在Redis下一次启动之前,我们可以通过"redis-check-aof --fix <filename>"
命令来修复坏损的AOF
文件,解决数据一致性的问题。 -
对于相同数量的数据集而言,
AOF
文件通常要大于RDB
文件。 -
AOF
在运行效率上往往会慢于RDB
。
-
RDB
和AOF
的区别
前者是保存了数据本身,而后者是记录了数据的变更。
尾言
这篇文章最后在网吧完成的,勿以善小而不为。