Redis
NOSQL:是指非关系型数据库的统称,redis就是其中最常用的一种nosql数据库,经常用于关系型数据库的缓存
NOSQL数据库(非关系型数据库)与SQL数据库(关系型数据库)有什么区别
- NOSQL数据库不遵循SQL标准
- NOSQL数据库不支持ACID
- NOSQL数据库拥有远超sql数据库的性能
什么情况下使用NOSQL数据库
- 对数据库高并发的读写
- 海量数据的读写
- 需要高扩展性的数据
也就是说,当数据量特别大,并且需要频繁操作的时候,关系型数据库就有可能承受不住压力而崩溃掉,那么此时就可以采用非关系型数据库来缓解数据库压力
什么情况下不适合使用NOSQL
- 需要使用事物的情况下
- 当我们需要结构化查询的时候(有条件的进行查询)
常见的NOSQL数据库
- Redis
- MongoDB
- HBase
Redis支持非常高速的读写,达到一秒可以读取几十万次数据,但是redis只是一个单纯的key-value型数据库,跟我们的map集合比较类似,只能根据key来获取value,不能实现任何条件查询操作,所以redis永远无法代替我们的关系型数据库,但是由于其高速的读写效率,所有可以用来当作关系型数据库的缓存使用,以及消息队列,分布式事务管理,分布式注册中心
CentOS安装Redis
-
解压安装包
tar -zxvf redis-5.0.3.tar.gz
-
安装redis需要的环境(redis是C语言编写的)
yum -y install gcc
-
跳转redis目录
cd redis-5.0.3
-
编译安装redis
make MALLOC=libc
cd src && make install
redis启动方式
-
前台启动
进入到redis的src目录下输入./redis-server即可启动redis,但是此时需要一直打开窗口,并且不能进行任何其他操作,非常不方便
-
后台启动
后台启动模式默认是关闭的,我们需要去修改redis.conf配置文件,将daemonize no修改成daemonize yes,并且启动redis的时候指定通过配置文件来进行启动
-
启动
src/redis-server /usr/local/redis-5.0.3/redis.conf
-
关闭
查询redis进程ps -aux | grep redis kill杀死进程
-
-
安装RedisDesktopManager
用于在win中桌面化管理redis当我们使用RedisDesktopManager去连接虚拟机redis服务的时候,发现无法连接,原因在于redis默认是禁止远程连接的(跟mysql一样),所以我们需要去修改配置文件开启远程连接
-
开启redis的远程连接
vim redis-conf 修改内容如下
- bind 127.0.0.1 修改为 0.0.0.0
- protected-mode yes 修改为 protected-mode no
- requirepass 123456
-
使用redis cli客户端操控redis
- 进入到redis的src目录下
- 执行./redis-cli -a 123456,为了解决中文乱码我们执行./redis-cli --raw -a 123456
Redis中常用的六种命令(key与五大数据类型)
切换数据库
select 小标
redis中默认有16个数据库
String
String是redis中最常用的数据类型,其存储方式是安全的二进制方式存储,字符串最大value值为512M,那就意味String这个类型并不只是单纯的可以存储字符串而已,我们也可以将图片或者文件的序列化对象存储到里面String类型采用类似于ArrayList的动态扩容机制,每次扩容都是2倍
-
set key value
存储一个字符串到redis之中,如果key存在则覆盖value
-
setnx key value
存储一个字符串到redis之中,如果key存在则不做任何操作。
-
get key
根据key从redis中获取一个字符串类型的value
-
append key value
可以将value拼接到之前key的value之后
-
strlen key
返回key的value值占用的字节数(一个中文占用三个字节)
-
incr key
让value自增1,要求value的值必须是数字类型,否则会抛出异常,如果key存在则value自增,如果key不存在则会创建对应的key并且value为1
-
decr key
与incr相反,对value进行自减操作,如果key存在则value自减,如果key不存在则会创建对应的key并且value为-1
-
incrby key N
与incr一样,但是自增量是N
-
decrby key N
与decr一样,但是自减量是N
-
mset key value…
可以一次存储多个key value
-
mget key…
可以一次取出多个value
-
msetnx key value
与mset不同的是,mset存值的时候如果key存在会覆盖掉对应的value,而使用msetnx如果key存在则不会进行任何操作,并且msetnx在操控多个key value的时候,要么全部成功,要么全部失败,也就是操作具有原子性不可分割
-
getrange
可以截取字符串,从开始下标截取到结束下标,并将值返回
-
setrange
对value进行替换,从指定的下标位置开始替换
-
setex
设置key value的同时对其设置过期时间,时间单位是秒,当时间到了之后,会自动删除对应的key value
-
psetex
设置key value的同时对其设置过期时间,时间单位是毫秒,当时间到了之后,会自动删除对应的key value
-
getset
先获取原有的key值,然后在使用新的value覆盖过去的value
-
exists
查看key是否存在,如果存在返回1,如果不存在返回0
-
set命令参数说明
-
key 必须的
-
value 必须的
-
EX 可选的
在设置key value的同时对其设置过期时间,类似于setex命令,时间单位是秒
-
PX 可选的
在设置keyvalue的同时对其设置过期时间,类似于psetex命令,时间单位是毫秒,并且只能与EX之间二选一
-
NX 可选的
在设置key value的时候,如果key存在则不进行任何操作,相当于setnx
-
XX 可选的
在设置key value的时候,如果key 不存在,则不会进行任何操作(只有key存在才会有操作),只能与NX二选一。
-
list(列表)
底层是双向链表,数据可以重复,对于数据两端操作效率较高,如果通过索引去操控中间的数据,效率就会较低
-
lpush/rpush key value…
从左侧或者右侧插入值,如果key不存在则创建key,并返回值的个数
-
lpop/rpop key
从左侧或者右侧从list中拿出一个值(拿出后list中就没了),当值取空后key会被删除。
-
rpoplpush key key
从第一个key的右侧拿出一个值,放入到第二个集合的左侧。
-
lrange key 起始下标 结束下标
根据下标进行范围取值,-1代表是最后一个下标,此操作值还在list中。
-
lindex key 下标
根据下标从list的左侧取值。
-
llen key
获取list长度。
-
linsert key before value newvalue
在value的前面插入newvalue
-
lrem key 个数 value
从list的左侧删除指定个数的value值,然后返回删除掉的个数。
-
lset key 下标 value
将list中下标的值改成value。
set(集合)
类似于Java中的set集合,里面元素无序,并且没有下标索引,set中的值不能重复,也就是说如果我们需要存储重复数据的话,不建议使用set存储而是建议使用list来存储数据,如果我们的数据不准出现重复的数据,就可以使用set来存储数据,set的底层实际上是一个key value形式的hash表
-
sadd key value…
存储数据,如果数据重复则会直接忽略掉value。
-
smembers key
取出集合中所有的值,不会删除。
-
scard key
返回集合中元素的个数。
-
srem key value…
删除集合中的value,并返回删除的个数。
-
spop key [个数]
从集合中随机拿走一个或多个值(值会被删除)
-
srandmember key [个数]
从集合中随机取出一个或多个值(不会被删除)
-
smove key1 key2 value
将key1中的value拿走放入到key2中
-
sinter key1 key2
返回两个集合中的重复元素
-
sunion key1 key2
返回两个集合中所有的元素(重复的只显示一个)
-
sdiff key1 key2
返回key2中没有的key1元素。
-
sismember key value
判断key中是否存在value,如果存在返回1,否则返回0
zset(有序集合)
与set集合类似,都是不能有重复数据存在,但是与set不同的是里面有自己的排序规则,所以zset是有序的(这个顺序不是我们的存储顺序),适合存储不需要重复的,但是需要进行排序的数据
-
zadd key score value …
添加元素,按照一个score 一个值的方式添加。
-
zrange key 起始score 结束score
返回key里面两个score 之间的值(包含俩score),值是无序取出的
-
zrangebyscore key 起始score 结束score [withscores]
按照score的顺序取出范围内的值,升序输出。
-
zrevrangebyscore key 起始score 结束score
按照score的顺序取出范围内的值,降序输出。
-
zincrby key i value
将key中value的score增加i值。
-
zrank key value
查询value在key中排第几位(从0开始计算)
-
zcount key起始score 结束score
查询key的score范围有多少个元素
-
zrem key value…
删除key中的value
Hsah
与Java中的map很像,是一个键值对集合,hash结构非常适合存储我们单个Java对象。
-
hset key field value
根据键值对的形式向key中添加值,或修改值
-
hget key field
根据key以及属性名取出对应的value
-
hexists key field
判断key中是否存在属性,如果存在返回1,否则返回0
-
hkeys key
取出key中所有的属性
-
hvals key
取出所有的value
-
hincrby key 属性 正负数
对属性的值进行加或减
-
hsetnx key 属性 值
当属性不存在的时候,添加属性以及值,如果属性存在则不进行任何操作。
Key
key 是redis中最重要的东西,因为所有的操作都是通过key来完成的。
Redis原子性
一次存储多个key value与多次存储key value有什么区别
原子是不可分割的组织,在redis中单条命令能完成的操作都是原子性的,因为redis是单线程的,所以redis在执行一条命令的途中是不会被其他线程打断的,并且也不会切换到其他线程,但是如果是多条命令的情况下,就有可能在执行完命令A之后执行命令B之前这个过程中就有可能被其他线程打断。
Redis配置文件的内容
Redis在删除过期key的时候是有惰性的,并不是我们key过期之后key立马就被删除,在每一次我们通过key去获取数据的时候,redis会先查看key的过期时间,如果发现key过期了会直接删除掉然后给我们返回null
-
bind
默认值是127.0.0.1 代表只能接受本机ip访问
-
0.0.0.0
和不写都是默认可以无限制的接受任何ip访问。
-
protected-mode
默认值是yes 表示受保护默认,不允许其他主机访问,如果我们想在其他主机访问这个redis需要改成no
-
Port
默认6379 代表这个redis实例的端口号,一般不建议修改。
-
tcp-backlog
默认值:511 是一个连接的队列,代表TCP连接时候的队列值。
-
timeout 0
超时时间表示空闲的连接过了多久会被关闭掉,0代表的是永不关闭。
-
tcp-keepalive 300
心跳检测时间,指的是redis间隔多久检测一下连接是否正常默认是5分钟1次。
-
daemonize yes
是否后台开启redis实例。
-
Pidfile
指定了存放redis进程ID的位置
-
loglevel notice
redis的日志级别
-
Logfile
redis的日志文件名称
-
Databases
默认数据库数量
-
Requirepass
数据库的密码
-
Maxclients
数据库的最大连接数
-
Maxmemory
内容可以使用的内存的容量,如果不设置内存满了后服务会宕机,如果设置了内存满了后就会试图移除数据,策略根据MAXMEMORY POLICY来进行移除。
-
MAXMEMORY POLICY
key的移除策略。
-
volatile-lru lru
是一个算法名称,在过期集合中使用lru算法移除设置了过期时间的key。
-
allkeys-lru
使用lru算法移除所有集合中的key
-
volatile-lfu
在设置了过期时间的集合key中移除使用最少的key
-
allkeys-lfu
按照最少使用策略移除key
-
volatile-random
在过期集合中移除随机的key
-
allkeys-random
全体进行随机移除
-
volatile-ttl
移除ttl最小的key(就是指快过期的key)
-
Noeviction
不移除,内存满了就返回err
Redis集群
为什么需要集群,单个的主机实例,访问量一般都是有限制的,此时我们可以搭建集群来解决访问量的问题,redis的集群方案目前主要有三种
主从复制
所谓的主从里面有俩角色,一个角色是master主(负责写数据),一个角色是slave从(负责读数据),主机负责写入数据,然后从服务器负责将主服务器上的数据同步拷贝到自身,客户端在读取数据的时候从服务器中读取即可,由于数据库查询的工作量是最大的,所以一般搭建主从的时候都是一主多从,今天我们以三台实例为例搭建一主二从的主从服务
Redis主从搭建
- 将redis.conf复制三份,内容分别改成
第一份:
bind 0.0.0.0
protected-mode no
port 6381
pidfile /var/run/redis_6381.pid
daemonize yes
第二份:
bind 0.0.0.0
protected-mode no
port 6382
pidfile /var/run/redis_6382.pid
daemonize yes
replicaof 192.168.153.128 6381
#masterauth 123456
第三份:
bind 0.0.0.0
protected-mode no
port 6383
pidfile /var/run/redis_6383.pid
daemonize yes
replicaof 192.168.153.128 6381
#masterauth 123456
- 然后通过配置文件分别启动3个服务
/usr/local/redis-5.0.3/src/redis-server /usr/local/redis-5.0.3/redis6381.conf
/usr/local/redis-5.0.3/src/redis-server /usr/local/redis-5.0.3/redis6382.conf
/usr/local/redis-5.0.3/src/redis-server /usr/local/redis-5.0.3/redis6383.conf
- 连接主服务器客户端
/usr/local/redis-5.0.3/src/redis-cli -p 6381 -a 123456
- 连接从服务器客户端
/usr/local/redis-5.0.3/src/redis-cli -p 6382
- 查看主服务器状态
Info replication
- 查看从服务器状态
Info replication
- 接下来测试,在主服务器写入数据,在从服务器上读取。
Redis的同步策略
-
全量复制
slave在连接主机会将主机所有的数据全部都存盘,并加载到内存中。
-
增量复制
主机,会将所有增加以及修改的指令收集起来,并将命令依次发送给所有的slave,完成数据的同步。
-
每当slave重新连接主机的时候都会自动进行一次全量复制,让数据与主机同步。
主从的缺点
- 由于在操作的时候,所有的数据都是写在一个主服务器上,然后由主服务器异步将命令发送给slave完成数据的同步,这个操作会有一定的延时,尤其是当业务特别繁忙的时候,或者slave过多的时候会更加严重。
- 当主服务器宕机之后,就没有办法提供服务的。
- 不具备自动切换,以及恢复功能。
- 主机在发送命令的过程中如果宕机,则会导致数据不一致。
主从故障
- 主服务器出现故障之后,我们可以挑选一个slave发送slaveof no one让这个slave变成主,并且让其他的slave继续跟随新的主服务器。
- 我们可以根据日志去修复之前出现故障的主服务器,当修复完成之后,我们启动服务后,将其变成slave继续提供服务。
哨兵模式
其实所谓的哨兵模式就是基于主从复制,只是在主从基础上引入了哨兵线程,由哨兵来监控以及自动处理故障,哨兵的职责如下:
- 监控master、salve是否正常运行
- 如果mester宕机了,会从salve中选取一个自动转换为master继续对客户端提供写的服务
- 可以让多个哨兵监控一个redis,同时也可以让哨兵之间也可以相互监控
当主服务器挂掉之后,由于哨兵需要进行投票选取新的主服务器,所以为了防止出现一直平票的情况,我们不建议配置偶数的哨兵,哨兵的数量要尽可能是奇数
优点
- 由于其基于主从,所以主从有的优点,哨兵模式都有
- 主服务器挂掉之后,可以自动切换
缺点
- 解决不掉单个redis实例容量问题,因为三个实例中数据是完全一致的
- 需要单独的哨兵进程,稍微麻烦一些
Cluster模式
哨兵解决了主从不能自动切换(故障转移)的问题,但是存在难以扩容,redis的容量受到单机的限制,Cluster模式实现了redis分布式存储,也就是指每个redis节点存储的内容是不相同的,解决了扩容问题
Cluster采用的是无中心结构
- redis所有的节点之间是互通的,内部使用二进制协议来优化传输速度
- 节点失效,需要集群中超过半数节点检测其失效才会失效
- 为了保证高可用,Cluster引用了主从模式,每个节点都会有一个或多个从节点,当主节点宕机之后,可以自动切换到从节点提供服务
搭建三主三从的集群模式(清空数据库再进行配置)
- 首先准备redis6381.conf~redis6386.conf的配置文件
- 编写配置文件内容如下,端口号记得全部改成与文件名称一致,所有文件内容全部相同,注意port pidfile config-file 需要不同,其他全部一样
bind 0.0.0.0
protected-mode no
port 6381
daemonize yes
pidfile "/var/run/redis_6381.pid"
#开启集群模式
cluster-enabled yes
#集群的配置文件
cluster-config-file node6381
#集群请求的超时时间
cluster-node-timeout 30000
- 启动六个进程
src/redis-server redis6381.conf
src/redis-server redis6382.conf
src/redis-server redis6383.conf
src/redis-server redis6384.conf
src/redis-server redis6385.conf
src/redis-server redis6386.conf
- 拉取集群关系,指定关系为一主一从(中间需要输入一次yes)
src/redis-cli --cluster create --cluster-replicas 1 127.0.0.1:6381 127.0.0.1:6382 127.0.0.1:6383 127.0.0.1:6384 127.0.0.1:6385 127.0.0.1:6386
-
查看控制台情况,集群配置成功,进入6381的cli 查看集群状态 cluster info 查看节点信息cluster nodes
-
客户端重定向
在连接客户端的时候可以加上src/redis-cli -c -h 192.168.25.64 -p 6380,这样在存值以及取值的时候会自动根据哈希槽进行客户端重定向操作。
槽(slot)的基本概念
从集群的简单操作中,我们已经知道redis存取key的时候,都要定位相应的槽(slot)。
Redis 集群键分布算法使用数据分片(sharding)而非一致性哈希(consistency hashing)来实现: 一个 Redis 集群包含 16384 个哈希槽(hash slot), 它们的编号为0、1、2、3……16382、16383,这个槽是一个逻辑意义上的槽,实际上并不存在。redis中的每个key都属于这 16384 个哈希槽的其中一个,存取key时都要进行key->slot的映射计算。
集群分区好处
redis的集群分区,最主要的目的都是在移除、添加一个节点时对已经存在的缓存数据的定位影响尽可能的降到最小。redis将哈希槽分布到不同节点的做法使得用户可以很容易地向集群中添加或者删除节点, 比如说:
- 如果用户将新节点 D 添加到集群中, 那么集群只需要将节点 A 、B 、 C 中的某些槽移动到节点 D 就可以了。
- 与此类似, 如果用户要从集群中移除节点 A , 那么集群只需要将节点 A 中的所有哈希槽移动到节点 B 和节点 C , 然后再移除空白(不包含任何哈希槽)的节点 A 就可以了。
新增删除节点
因为将一个哈希槽从一个节点移动到另一个节点不会造成节点阻塞, 所以无论是添加新节点还是移除已存在 节点, 又或者改变某个节点包含的哈希槽数量, 都不会造成集群下线,从而保证集群的可用性。下面我们就来学习下集群中节点的增加和删除。
-
准备配置文件6387 与6388 文件内容与上面一致
-
启动新添加的节点 src/redis-serverredis-6387.conf src/redis-serverredis-6388.conf
-
添加节点到集群src/redis-cli --cluster add-node 127.0.0.1:6387 127.0.0.1:6387 src/redis-cli --cluster add-node 127.0.0.1:6388 127.0.0.1:6388
-
查看集群信息src/redis-cli -c -p 6387 cluster nodes 发现新的节点没有槽位
-
从添加主节点输出信息和查看集群信息中可以看出,我们已经成功的向集群中添加了一个主节点,但是这个主节还没有成为真正的主节点,因为还没有分配槽(slot),也没有从节点,现在要给它分配槽(slot)
-
src/redis-cli --cluster reshard 127.0.0.1:6381
系统提示要移动多少个配槽(slot),并且配槽(slot)要移动到哪个节点,任意输入一个数,如1024,再输入新增节点的ID(刚刚查看信息中有)然后提示要从哪几个节点中移除1024个槽(slot),这里输入‘all’表示从所有的主节点中随机转移,凑够1024个哈希槽,然后就开始从新分配槽(slot)了。从新分配完后再次查看集群节点信息 cluster nodes
-
由于刚刚随便分的slot,所以不平衡,现在想要各个节点槽的数量相当,所以要进行平衡
src/redis-cli --cluster rebalance --cluster-threshold 1 127.0.0.1:6381
-
指定从节点进入到从节点之中 src/redis-cli -c -p 6388
cluster replicate 主节点的id
查看节点信息cluster nodes
-
删除从节点
src/redis-cli --cluster del-node 127.0.0.1:6388 ID值
-
删除主节点6387
- 如果主节点有从节点,将从节点转移到其他主节点
- 如果主节点有slot,去掉分配的slot,然后在删除主节点
redis-cli --cluster reshard 127.0.0.1:6381 输入所有的slot数量 输入接收6387节点slot的node-id 输入被删除master的node-id 输入yes确定删除 src/redis-cli --cluster info 127.0.0.1:6381 查看6387已经没有slot 删除掉节点 src/redis-cli --cluster del-node 127.0.0.1:6387 node id