前言
- 在大型应用场景中,常常使用nignx做负载均衡以分摊请求压力,随之而来的是session的存储成为难题,即服务器那么多,与客户端建立可靠而不影响性能的session连接十分麻烦。所以,单独为缓存建立一台服务器是十分必要的。redis可以处理数据缓存和读写分离。
NoSQL数据库概述
- NoSQL(Not Only SQL),意为“不仅仅是SQL”,泛指非关系型数据库。不同于关系型数据库将数据存储在磁盘中,它将数据存储在内存中。
- NoSQL不依赖业务逻辑方式存储,而以简单的键值对模式存储,因此大大的增加了数据库的扩展能力。
- NoSQL不遵循SQL标准。
- NoSQL不支持事务的四个特性(ACID,即:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability))。
- NoSQL将数据存储在内存中,因而具有远超SQL的性能。
NoSQL适用场景
- 对数据高并发的读写。
- 海量数据的读写。
- 对数据高可扩展性的。
NoSQL不适用的场景
- 需要ACID事务支持的场景。
- 需要处理复杂关系查询时
- 需要基于SQL的结构化查询存储时。
Redis操作
Redis安装
- 去官网下载redis的liunx版本发行包(Windows版本发行包在GitHub上,官网只有linux版本的)
- 上传到linux主机上
- 解压,解压命令:
tar -zxvf 压缩报名.tar.gz
,解压之后得到目录
- 安装步骤:
- redis的安装方式为编译安装,由于redis是由c语言开发的软件,因此需要gcc和gcc++的环境
- 在redis解压目录下运行
make
命令,如果报错,则证明没有gcc和gcc++的环境
- 分别运行
yum install gcc
和yum install gcc-c++
即可
- 严查gcc和gcc++环境是否安装成功:
gcc -v
和g++ -v
- 待环境安装好之后,输入
make distclean
命令清楚之前的编译失败的文件残留
- 再次输入
make
命令进行编译
- 编译好之后,跳过
make test
直接运行安装命令make install
Redis服务端与客户端操作
-
redis-server
:按默认配置文件启动redis服务端
-
redis-server /root/aaa/myRedis/redis.conf
:按路径下的自定义配置文件启动redis
- 将redis.conf文件复制一份到其他目录:
cp redis.conf /root/aaa/myRedis/redis.conf
- 将其中的
daemonize no
改为daemonize yes
,这个配置即是允许redis服务端在后台开启
-
redis-cli
:按默认配置启动客户端
- 客户端默认配置为:ip:127.0.0.1 端口6379
- 后面加上
--raw
可以显示中文
-
redis-cli -h 127.0.0.1 -p 6379
:指定端口和ip启动客户端
-
ping
:检测客户端与服务端是否连通成功。如果结果为PONG
,即连通成功。
-
redis-cli shutdown
:关闭单例redis实例。
Redis基本指令
- redis共有16个数据库,可以使用
select <0~15>
来切换这16个数据库,下标从0开始,默认存储在第一个数据库(下标为0)中.
- redis统一密码管理,所有库都是一个密码,要么都允许访问要么一个都连不上。
- redis属于键值对key-value的存储结构,关于redis所谈到的数据类型,均指值value的数据结构。键可以简单理解为变量名。
- redis的原子性:
- redis中所谓的原子操作是指不会被线程调度机制打断的操作,这种操作一旦开始,就一直运行到结束。
- 在单线程中,能够在单条指令中完成的操作都可以认为是“原子操作”,因为中断只能发生于指令之间。
- 在多线程中,不能被其他线程打断的操作就叫做原子操作。
- redis单命令的的原子性主要得益于redis的单线程。
Redis键操作
-
keys *
:查看当前数据库所有的键
-
exists <key>
:判断某个键是否存在于当前数据库
-
type <key>
:查看某个键对应值的数据类型
-
expire <key> <seconds>
:为已存在的键设置过期时间,单位为秒,到期键值对自动删除
-
ttl <key>
:查看当前键还有多少秒过期,-1表示永不过期,-2表示已经过期
-
dbsize
:查看当前数据库key的数量
-
Flushdb
:清空当前数据库
-
Flushall
:清空所有数据库
Redis数据类型
string
- string是二进制安全的,因此redis的string可以包含任何数据,比如jpg图片或者序列化的对象。
- string是redis最基本的类型,一个redis中字符串value最大可以是512M
- string操作:
-
get <key>
:根据键查询键值对
-
set <key> <value>
:添加键值对
-
append <key> <value>
:根据键向已经存在的键值对的值末尾追加内容
-
strlen <key>
:根据键获得值的长度
-
setnx <key> <value>
:查询该键是否存在,如果不存在则添加键值对,存在则不操作。
-
incr <key>
:只能对数字值进行操作,将键中的值加1,如果键为空,则新增值为1。
-
decr <key>
:只能对数字值进行操作,将键中的值减1。
-
incrby/decrby <key> <步长>
:将key中存储的数字值进行自定义增减,自定义数值
-
mset <key1> <value1> <key2> <value2> ....
:同时设置一个或多个键值对
-
mget <key1> <key2> <key3> ....
:同时获取一个或者多个value
-
msetnx <key1> <value1> <key2> <value2> ....
:同时设置一个或多个键值对,当且仅当所有给定的键都不存在时
-
getrange <key> <起始位置> <结束位置>
:根据给定区间和键获得值的部分,包含起始位置而不包含结束位置,即左闭右开区间
-
setrange <key> <起始位置> <value>
:用<value>覆写<key>所存储的字符串值,从起始位置开始。
-
setex <key> <过期时间> <value>
:设置键值对的同时设置过期时间,单位为秒
-
getset <key> <value>
:以新换旧,为键设置新的值,同时获得旧值
list
- redis中的list为单键多值的列表数据结构。
- redis列表是简单的字符串列表,按照插入顺序排序。可以添加元素到列表的头部(左边)或者尾部(右边)。
- redis列表的底层实际是一个双向链表,对两端的操作性能很高,通过索引下标的操作中间节点的性能较差。
- 列表操作:
-
lpush/rpush <key> <value1> <value2> <value3> ...
:创建列表,从左边/右边插入一个或者多个值
-
lpop/rpop <key>
:从列表左边/右边弹出一个值,当所有值全部弹出删除后,键随之销毁。
-
rpoplpush <key1> <key2>
:从<key1>列表中弹出一个元素,插入到<key2>列表右边。
-
lrange <key> <start> <stop>
:从左到右按照开始位置与结束位置(闭区间)获得元素。(从0到1代表从表头到表尾,即可获得全部元素)
-
lindex <key> <index>
:按照索引下标获得元素(从左到右)
-
llen <key>
:获得列表长度
-
linsert <key> before/after <value> <newvalue>
:在<value>前/后面插入<newvalue>插入值
-
lrem <key> <n> <value>
:删除n个value(n值为正删除顺序从左到右,n值为负删除顺序从右往左,n值为0删除全部的value)
set
- redis的set与list类似,都是string类型的无序列表结构。特殊之处在于set是可以自动排重的,set不会存储重复的数据。
- redis的set底层其实是一个value为null的hash表(相当于map集合只含键,值全为空),因此增删查的复杂度都是O(1)。
- redis的set提供了判断某个成员是否存在于一个set集合内的重要接口,这是list所无法提供的。
- set操作:
-
sadd <key> <value1> <value2>
:将一个或多个member元素加入到集合key当中,已经存在于key中的元素将被忽略。
-
smembers <key>
:取出该集合中的所有元素。
-
sismember <key> <value>
:判断集合<key>是否含有该value值,有则返回1,没有则返回0。
-
scard <key>
:返回该集合的元素个数。
-
srem <key> <value1> <value2>
:删除集合中的某个元素。
-
spop <key>
:随即从集合中弹出一个值
-
srandmember <key> <n>
:随机从元素中返回n个值,但元素不会被删除。
-
sinter <key1> <key2>
:返回两个集合的交集元素。
-
sunion <key1> <key2>
:返回两个集合的并集元素。
-
sdiff <key1> <key2>
:返回两个集合的差集元素。
hash
- redis的hash是一个键值对集合,是一个string类型的键值映射表,特别适合用于存储对象。
- 类似java中的Map<string, string>
- hash操作:
-
hset <key> <field> <value>
:给key集合中的field键赋值value
-
hget <key> <field>
:从key集合中根据field键取出对应的值
-
hmset <key> <field1> <value1> <field2> <value2> ...
:批量设置hash值
-
hexists <key> <field>
:查看key集合中field键是否存在
-
hkeys <key>
:列出该hash集合中的所有field
-
hvals <key>
:列出该hash集合中的所有value
-
hgetall <key>
:列出该hash集合中所有的键值对
-
hincrby <key> <field> <increment>
:向hash集合中的field键对应的值添加增量increment(当value值为数字)
-
hsetnx <key> <field> <value>
:将哈希表key中的field键对应的值设置为value,当且仅当field键不存在
zset
- redis中的zset是有序集合列表,是一个没有重复元素的字符串集合。
- 相较于set,zset的每个成员都关联了一个评分(score),这个评分被用来按照从最低分到最高分的方式排序集合中的成员,集合中的成员不可重复,但评分是可重复的。
- zset操作:
-
zadd <key> <score1> <value1> <score2> <value2>...
:将一个或多个元素及其分数添加进有序集合key中
-
zrange <key> <start> <stop> [withscores]
:
- 返回key列表中从start开始到stop的元素(闭区间),按从小到大排列。
- 0到-1表示从表头显示到表尾。
- 带上withscores参数,会显示分数。
-
zrangebyscore key min max [withscores] [limit offset count]
:返回有序集合key中,所有score值在min和max之间的元素(包括等于min和max的元素)。按照score值从小到大排序。
-
zrevrangebyscore key max min [withscores] [limit offset count]
:返回有序集合key中,所有score值在max和min之间的元素(包括等于max和min的元素)。按照score值从大到小排序。
-
zincrby <key> <increment> <value>
:为元素value的score加上增量increment
-
zrem <key> <value>
:删除key列表中值为value的元素
-
zcount <key> <min> <max>
:统计该集合,分数区间内的元素个数。(包含score等于min和max的元素)
-
zrank <key> <value>
:返回该值在集合中的排名,从0开始
Redis事务
- redis的事务并不同于关系型数据库的事务,它并不支持ACID。它的主要作用就是串联多个命令防止别的命令插队,简单来说,批量处理命令
- redis的事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序的执行。事务在执行的过程中不会被其他客户端发送过来的请求所打断。
- 事务命令:
-
multi
:开启事务
-
exec
:执行
-
discard
:回滚事务
-
watch <key> <key>...
:根据键监视某一条数据,如果在事务执行之前这个数据被其他命令所改动,那么事务就会被回滚。
- 事务的错误处理:
- 当组队中某个命令出现报告错误(比如单词写错了,即redis无法识别此命令),执行时所有操作都会被回滚
- 当组队中某个命令报错(即正确的命令但执行发生异常),则只有报错的命令不会被执行,而其他命令都会执行,不会回滚。
-
悲观锁与乐观锁:
- 当程序中可能出现并发的情况时,就需要通过一定的手段来保证在并发情况下数据的准确性,通过这种手段保证了当前用户和其他用户一起操作时,所得到的结果和他单独操作时的结果是一样的。这种手段就叫做并发控制。并发控制的目的是保证一个用户的工作不会对另一个用户的工作产生不合理的影响。
- 悲观锁:一旦事务开启,就对数据进行加锁,其他用户不可修改或是访问该项数据,必须等待第一个请求完成解锁。
- 乐观锁:乐观锁相对于悲观锁采用了更加宽松的加锁方式,它认为并发控制下数据的修改是乐观的。它的加锁方式是对数据进行版本控制,在数据修改之前比较数据的版本,如果版本不一致则认为数据已经被其他请求进行了修改,由此而回滚请求。
Redis持久化
- 持久化:将数据保存到磁盘上
- Redis提供了两种持久化的方案:RDB(Redis DataBase)和AOF(Append Of File)
RDB
- RDB(Redis DataBase):采用快照思想,在指定时间间隔内将内存中的数据集fork快照写入磁盘,恢复时将快照文件直接加载到内存中,因此十分迅速。
- 快照文件名、存放位置、保存策略均在redis.conf文件当中(默认文件名dump.rdb,存放位置为redis.conf文件当前目录)
- 当使用shutdown关闭redis实例时也会进行一次RDB持久化
- 持久化恢复:
- 在启动redis之前,将快照备份文件名添加到redis的工作目录下,备份文件会直接加载
AOF
- AOF(Append Of File):以日志的形式来记录每一个写操作,将redis执行过程中所有的写操作指令记录下来(读指令不记录),只能追加文件但不可以改写文件,redis启动之初会读取该文件重新构建数据,即将所有的写指令重新运行一次以完成数据恢复。加载速度很慢。
- AOF默认不开启,需要在redis.conf配置文件当中开启(appendonly no)
- 日志文件名可在redis.conf中修改,但日志文件路径与RDB备份文件dump.rdb文件路径共用。
- AOF和RDB同时开启时,redis采用AOF进行文件备份
- AOF持久化恢复:
- 在启动redis之前,将快照备份文件名添加到redis的工作目录下,备份文件会直接加载
- AOF和RDB同时开启时,redis默认读取AOF的备份文件进行备份
- AOF同步频率
appendfsync
:
-
always
:始终同步,每次redis的写入都会立刻记入日志
-
everysec
:每秒计入日志一次,如果发生宕机,本秒数据可能丢失。
-
no
:不主动进行同步,把同步时机交给操作系统。
Redis主从复制
- 主从复制,即主机数据更新后根据配置和策略,自动同步到备机的master/slaver机制,master以写为主,slave以读为主。
- 主从复制的用处即实现读写分离,以达到性能扩展的目的(减少读写压力)
- 主从复制中只有主服务器可以对数据进行修改操作,从服务器只能读数据
- 主从复制操作:
- 复写多份redis.conf文件,引入主文件配置,并另写一些自定义配置(端口、RDB文件名等)
- 启动这多个服务端,并可以用
info replication
命令查看主从复制的相关信息
-
slaveof <ip> <port>
:设置当前服务器成为某个实例的从服务器
-
slaveof no one
:使当前从机变成主机
- 不论从服务器何时连接,何时确立主从关系,从服务器的数据情况一定跟随主服务器,如果主服务器断开,从服务器会原地待命等待连接
- 持久化主从关系:redis.conf里的slaveof配置
- 主从复制去中心化:
- 薪火相传,链条化主从复制,即主服务器的从服务器作为下一台从服务器的主服务器
- 上一个slave是下一个slave的master,slave同样可以接受其他slaves的连接和同步请求,则该slave作为链条中的下一个master,可以有效减低master的写压力
- 薪火相传模型中后续slave与主服务器并没有直接关系(我附庸的附庸不是我的附庸),如果中间有slave服务器宕机,那么整个链条会断掉,后面的slave都无法备份数据了。
- 如果主服务器宕机,从服务器仍然会等待连接,但是也可以使用
slaveof no one
命令使这台服务器成为主服务器
- 哨兵模式:
-
slaveof no one
命令的自动版,后台监控主机是否发生故障,如果故障了自动将从机转换为主机
- 配置哨兵:
- 调整模式:一主二从
- 自定义哨兵配置文件
sentinel.conf
文件,向其中写入sentinel monitor 哨兵名 主机ip 主机端口 票数n
- 启动哨兵:
redis-sentinel sentinel.conf
,当有至少n个哨兵认为主机发生故障时启用从机反客为主。(选取从机的策略这里不再阐述)
Redis集群
- redis集群实现了对redis的水平扩容,分摊了并发控制下的请求压力。
- redis集群实质上是运行更多redis实例节点,同样需要复写redis.conf文件并配置到不同端口,同时需要加入一些配置
-
cluster-enabled yes
:打开集群模式
-
cluster-config-file nodes-6379.conf
:设置节点配置文件名(此处以nodes-6379.conf为例)。
-
cluster-node-timeout 15000
:设定节点失联时间(单位为毫秒),超过该时间,集群自动进行主从切换
-
redis-cli -c -p 端口号
:以集群的方式进入客户端
-
cluster nodes
:查看集群信息
- redis一个集群至少包含3个节点,一共包含16384个插槽(slots),数据库中的每一个键都属于其中一个插槽,而集群中的每一个节点负责处理一部分插槽
Jedis
- jedis是Java操作redis的类库,方法名基本等同redis命令
- 依赖:
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>版本号</version>
</dependency>
检测jedis连接
public static void main(String[] args) {
Jedis jedis = new Jedis("192.168.88.128", 6379);
String result = jedis.ping();
System.out.println(result); //输出PONG则连接成功
jedis.close();
}