NoSQL
NoSQL = Not only SQL(不仅仅是SQL,也叫非关系型数据库)
NoSQL 和 关系型数据库 是同一级别的
随着互联网访问量大、高并发、高可用、海量数据的需求的逐渐增大,传统关系型数据库已经有点跟不上需求了,
一张关系型数据的表最多只能有256个字段,横向受限,纵向上可承受的能力也是受限的,一旦记录数达到百万级,读写速度很明显会降下来,面对海量数据,只能主从复制,分库分表,这样的架构是很难维护的,而且无法通过简单的增加硬件、服务节点来质地提高性能,另外关系型数据库大多数是收费的,而且对硬件要求很高
NoSQL的优势在哪?
-
**处理大量数据,高性能。**NoSQL数据库都具有非常高的读写性能,尤其在大数据量下,同样表现优秀。这得益于它的无关系性,数据库的结构简单。关系型数据库(例如MySQL)使用查询缓存。这种查询缓存在更新数据后,缓存就是失效了。在频繁的数据读写交互应用中。缓存的性能不高。NoSQL的缓存性能要高的多。
-
**灵活的数据模型。**NoSQL无需事先为要存储的数据建立字段,随时可以存储自定义的数据格式。而在关系数据库里,增删字段是一件非常麻烦的事情。如果是非常大数据量的表,增加字段简直就是一个噩梦。尤其在快速变化的市场环境中,用户的需求总是在不断变化的。
-
**高可用。**NoSQL在不太影响性能的情况,就可以方便的实现高可用的架构。NoSQL能很好的解决关系型数据库扩展性差的问题。弥补了关系数据(比如MySQL)在某些方面的不足,在某些方面能极大的节省开发成本和维护成本。MySQL和NoSQL都有各自的特点和使用的应用场景,两者结合使用。让关系数据库关注在关系上,NoSQL关注在存储上。
-
**低成本。**大多免费
NoSQL的劣势在哪?
- 无关系,数据之间是无联系的。
- 不持标准的SQL,没有公认的NoSQL标准
- 没有关系型数据库的约束,大多数也没有索引的概念
- 没有事务,不能依靠事务实现ACID,即Atomicity 原子性,Consistency 一致性,Isolation 隔离性,Durability 耐久性
- 没有丰富的数据类型(数值,日期,字符,二进制,大文本等)
Redis入门
Redis:Remote Dictionary Server,意为远程字典服务,就像字典一样,里面是key-value的形式
Redis是当今非常流行的基于K-V结构的作为Cache使用的NoSQL数据库,可以减轻对数据库的查询压力
浏览器 <------------> View <------------> Service <------------> Dao <------------> 数据库
|
|
|
Redis
Redis一般存用户经常访问的数据,Service层要是在Redis里能找到想要的数据,就不会去Dao操作数据库了
redis里操作数据用的是命令
注意,Redis生产环境中都是运行在Linux系统中的,windows上的redis是微软公司改造出来的,仅供开发学习使用
安装
windows下安装redis
Windows环境的安装包:github.com/microsoftar…
启动:
在安装目录下启动cmd,输入redis-server.exe redis.windows.conf,会出现
_._
_.-``__ ''-._
_.-`` `. `_. ''-._ Redis 3.0.504 (00000000/0) 64 bit
.-`` .-```. ```\/ _.,_ ''-._
( ' , .-` | `, ) Running in standalone mode
|`-._`-...-` __...-.``-._|'` _.-'| Port: 6379
| `-._ `._ / _.-' | PID: 18604
`-._ `-._ `-./ _.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' | http://redis.io
`-._ `-._`-.__.-'_.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' |
`-._ `-._`-.__.-'_.-' _.-'
`-._ `-.__.-' _.-'
`-._ _.-'
`-.__.-'
[18604] 12 Oct 11:28:43.938 # Server started, Redis version 3.0.504
[18604] 12 Oct 11:28:43.941 * The server is now ready to accept connections on port 6379
出现图形,表示服务启动成功,此时cmd窗口不能关,关了服务就停了!可以看到,redis在6379端口等着用户的请求
双击安装目录里的 redis-cli.exe 开启客户端,在里面输入命令进行交互
linux下安装redis
安装
先安装gcc编译器(centos)
yum -y install gcc
下载安装包
wget https://download.redis.io/releases/redis-6.2.6.tar.gz
解压安装包
tar xzf redis-6.2.6.tar.gz
将解压后的目录移动到/usr/local/redis下
mv redis-6.2.6 /usr/local/redis
进入到移动后的安装目录
cd /usr/local/redis
编译
make
安装,并指定安装目录,至此,安装成功~
make install PREFIX=/usr/local/redis
启动redis服务
先在安装目录下,使用make install可添加环境变量,以后在任何地方都能用,更方便,当然不添加也可以(需要在安装目录里的src目录下执行程序,麻烦)
make install
添加环境变量后直接可以启动服务,前台启动,不能干别的
redis-server
推荐后台启动,输入后回车,会退出redis界面,可以干别的(常用)
redis-server &
启动后可以查看进程
ps -ef | grep redis
测试
配置环境变量后在任意目录输入redis-cli,便可打开客户端
redis-cli
输入
set k1 v1
get k1
此时发现输出"v1",成功
关闭redis服务
粗暴方式(不推荐,可能对数据有影响,只有在redis死机的情况下使用)
先查看redis进程的pid
ps -ef | grep redis
然后kill或kill -9
kill "redis的pid"
kill -9 "redis的pid"
使用shutdown命令关闭(推荐)
启动客户端的同时跟上关闭命令(当然进到客户端再输关闭也行)
redis-cli shutdown
指定连接的地址和端口号
redis-cli -h IP地址 -p 端口号
当然redis也有类似mysql的Navicat一样的图形化工具,如Redis Desktop Manager和redisclient,可以百度下载
java想连接redis,需要jar包,叫jedis
常用命令
ping
查看状态,输入ping命令,返回是PONG的话说明一切正常
dbsize
返回当前redis数据库key的数量
redis默认使用了16个库
0-15,默认是第0个库,想修改数据库的数量的话,在redis.conf文件里修改databases的值,默认是16
select 数字
选择使用哪个数据库,如select 4表示使用第5个库
flushdb
表示删除当前数据库中的所有数据
exit或quit
表示只退出客户端,服务依旧还在
key操作命令
keys
查找符合模式的key,后面跟通配符
- *:
keys *查询所有的key,keys w*表示查询所有以w开头的key - ?:如
keys wo?d就可以查到word,wood
exists
判断key是否存在,返回一个整数,若存在返回1,其他返回0,使用多个key,返回key的数量
如 exists username表示判断username是否存在,存在就返回1
如 exists username userage表示判断username和userage是否存在,都存在就返回2,只存在一个就返回1
expire
设置key的生存时间,超时后自动删除该key,单位是秒,设置成功返回1,其他返回0
如expire username 10表示10秒后自动删除username这个key
ttl
以秒为单位,返回key的剩余生存时间,返回-1表示永久有效,返回-2表示该key不存在,返回的正数表示还能活多少秒
如ttl username表示查看username的剩余存活时间
type
查看key所存储值的数据类型
如type userage查看该key对应值的数据类型
- string(字符串)
- list(列表)
- set(集合)
- zset(有序集)
- hash(哈希表)
- none(key不存在)
del
删除存在的key
返回一个数字,表示已经删除的数量
如del username表示删除username这个key
如del username userage表示删除username和userage两个key
5种数据类型
一、字符串类型string
字符串类型是Redis中最基本的数据类型,它能存储任何形式的字符串,包括二进制数据,序列化后的数据,JSON化的对象甚至是一张图片。最大512M。
基本命令
set
将字符串值value设置到key中
如set username tom,假如key已经存在,则会覆盖原先的值
get
获取该key对应的值
如get username,假如没有这个key,会返回nil
incr
将key中储存的数字值加1,如果key不存在,则key的值先被初始化为0再执行,incr操作(只能对数字类型的数据操作)多线程下是线程安全的
如incr userage
decr
incr的反操作,减1
append
说明:如果key存在,则将value追加到key原来旧值的末尾,如果key不存在,则将key设置值为value 返回值:追加字符串之后的总长度
如append useraddr beijing表示给该key的值上追加一个”beijing“
常用命令
strlen
求key对应值字符串的长度,若key不存在,返回0,存在返回长度
如strlen username
getrange
截取子串,获取key中字符串值从start开始到end结束的子字符串,包括start和end,负数表示从字符串的末尾开始,-1表示最后一个字符(下表从0开始)
如getrange useraddr 2 5,getrange useraddr 2 -1
setrange
setrange key offset value,说明:用value覆盖(替换)key的存储的值从offset开始,不存在的key做空白字符串。返回值:修改后的字符串的长度
如setrange useraddr 7 changpingqu
mset
一次性创建一个或多个key-value,返回ok
如 mset k1 v1 k2 v2 k3 v3
mget
一次型获取一个或多个key对应的value值
如 mget k1 k2 k3
二、哈希类型hash
Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。
基本命令
hset
hset hash表的key field value
作用:将哈希表key中的域field的值设为value,如果key不存在,则新建hash表进行赋值,如果有field,则覆盖值。
返回值: ①如果field是hash表中新field,且设置值成功,返回1 ②如果field已经存在,则对应的旧值会被覆盖,返回0
如hset websites baidu baidu.com和hset websites google google.com是往websites这个hash表中添加两个键值对
hget
获取指定hash表中指定域field的对应的值
返回的是值,若hash表或field不存在,返回nil
如hget websites google会返回google.com
hmset
同时将多个field-value(域-值)设置到哈希表key中,此命令会覆盖己经存在的field,hash表key不存在,会创建空的hash表,执行hmset。返回值:设置成功返回ok,如果失败返回一个错误
如 hmset websites sina sina.com mi mi.com,可以给hash表里同时设置两个键值对
hmget
获取指定hash表中多个指定域field的对应的值
如 hmget websites mi sina
hgetall
获取指定hash表中所有的键值对
如hgetall websites
hdel
删除指定hash表中一个或多个域field,若域不存在直接忽略,删除成功返回删除的域的数量
如hdel websites baidu google mi
hkeys
取出指定hash表中所有的field
如hkeys websites
hvals
取出指定hash表中所有的值
如hvals websites
hexists
查看指定hash表中给定的域field是否存在,存在返回1,其他返回0
如hexists websites google
三、列表类型list
Redis列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)
从头部操作的命令都是以 l 开头,从尾部操作的命令都是以 r 开头
lpush
lpush key value
将一个或多个值value插入到列表key的表头(最左边),从左边开始加入值,从左到右的顺序依次插入到表头
返回一个数字,是新列表的长度
lpush mylist a b c d e往mylist列表中依次插入abcde,后插的在最左面
rpush
将一个或多个值value插入到列表key的表尾(最右边),从左到右的顺序依次插入到表尾
返回一个数字,是新列表的长度
如rpush mylist f g h
lrange
返回列表指定区间的成员,-1表示最后一个元素,-2表示倒数第二个元素,以此类推(下标从0开始)
如lrange mylist 0 -1表示列出所有元素
lindex
获取列表指定下标index的元素,只是查询,不改变原列表,找不到返回nil
如lindex mylist 1,查询下标为1的元素
llen
返回指定列表里元素的个数
如llen mylist
lrem
删除指定列表里的元素,但是不是按照下标删除,跟上数字0表示删除所有,正数表示删除从左往右前几个,负数是从右往左
如lrem mylist 2 tom表示将mylist列表中从左起前2个tom删掉
lrem mylist 0 jack表示将mylist列表中所有的jack都删掉
lset
将指定列表的下标为index的元素替换成指定值
如lset mylist 2 jerry表示将下表为2的元素替换成jerry
linsert
linsert key before| after pivot value
将值value插人到列表key当中位于值pivot之前或之后的位置。key不存在,pivot不在列表中,不执行任何操作
返回值:命令执行成功,返回新列表的长度。没有找到pivot返回1,key不存在返回0
如linsert mylist after jerry zhangsan表示在jerry后面插入一个zhangsan
linsert mylist before jerry lisi表示在jerry前面插入一个lisi
四、集合类型set
Redis的Set是string类型的无序集合,集合成员是唯一的,即集合中不能出现重复的数据
命令都是以s开头
sadd
sadd key member
将一个或多个member元素加入到集合key当中,己经存在于集合的member元素将被忽略,不会再加入。 返回值:加入到集合的新元素的个数。不包括被忽略的元素。
如sadd sql insert update和sadd sql delete都是往sql集合里添加元素,插进去是乱序的
smembers
显示集合中的所有成员
不存在的集合会被视为空集合
如smembers sql,查看sql集合中的所有成员
sismember
判断指定成员在指定集合中是否存在
有就返回1,没有就返回0
如sismember sql update
scard
获取集合中成员个数
如scard sql
srem
作用:删除集合key中的一个或多个member元素,不存在的元素被忽略。返回值:数字,成功删除的元素个数,不包括被忽略的元素。
如srem sql insert和srem sql select update
srandmember
随机返回几个集合中的元素进行显示,不影响原集合
srandmember sql 2表示随机从sql集合里显示2个元素,不重复
srandmember sql -2表示随机从sql集合里显示2个元素,可能重复
spop
随机从集合中删除1个元素
spop sql表示随机删除1个元素
五、有序集合类型zset
Redis有序集合zset和集合set一样也是string类型元素的集合,且不允许重复的成员。不同的是zset的每个元素都会关联一个分数(分数可以重复),redis通过分数来为集合中的成员进行从小到大的排序。
命令都是z开头
zadd
zadd key score member
将一个或多个member元素及其score值加入到有序集合key中,如果member存在集合中,则更新值;score可以是整数或浮点数,返回值:数字,新添加的元素个数
如 zadd studentscore 59 zhangsan 60 lisi为向studentscore 集合中添加学生,每人都对应一个分数,按这个分数从小到大排序,分数一样按字母排序
zrange
查询有序集合,指定区间的内的元素。集合成员按score值从小到大来排序,默认不返回分数,加WITHSCORES选项让score和value一同返回
zrange studentscore 0 -1查询所有元素
zrange studentscore 0 -1 withscores查询所有元素且带上分数
zrevrange
和上面zrange用法一样,不过是按照分数倒序的(从大到小)
如zrevrange studentscore 0 -1 withscores
zrem
表示从集合中删除一个或多个成员,不存在的成员会被忽略,返回成功删除的成员数量,忽略的不包括
如zrem studentscore zhangsan删除zhangsan
zcard
获取有序集key的元素成员的个数
如 zcard studentscore
常用命令
zrangebyscore
zrangebyscore key min max [WITHSCORES] [LIMIT offset count]
获取分数在min和max之间的所有成员,有序成员按递增顺序(从小到大)排序
zrangebyscore studentscore 99 200表示分数在99-200区间内的成员,包括99和200
zrangebyscore studentscore (99 (200表示分数在99-200区间内的成员,不包括99和200
zrangebyscore studentscore 100 +inf表示找出分数最大的
zrangebyscore studentscore 100 -inf表示找出分数最小的
zrangebyscore studentscore -inf +inf表示找出所有成员
zrangebyscore studentscore -inf +inf limit 2 4表示找出所有成员,但是显示第3到第5条结果(分页)
zrevrangebyscore
和上面用法一样,但是结果是从大到小的,zrevrangebyscore key max min
如zrevrangebyscore studentscore 200 100
zcount
zcount key min max
统计在分数区间的成员有多少个
如 zcount studentscore 100 200返回个数,包含100和200
高级话题
事务
Redis中的事务主要是保证两条及以上的命令一定会被执行,作为一个整体
注意,Redis里没有回滚这一说
multi
标记一个事务的开始。事务内的多条命令会按照先后顺序被放进一个队列当中,返回ok
exec
标志着事务的提交,作用:执行所有事务块内的命令 返回值:事务内的所有执语句内容,事务被打断(影响)返回nil
discard
作用:取消事务,放弃执行事务块内的所有命令 返回值:总是返回ok
watch
语法:watch key
作用:监视一个(或多个)key,如果在事务执行之前这个(或这些)key被其他命令所改动,那么事务将被打断。
返回值:总是返回ok
unwatch
语法:unwatch 作用:取消WATCH命令对所有key的监视。如果在执行WATCH命令之后,EXEC命令或DISCARD命令先被执行了的话,那么就不需要再执行UNWATCH了 返回值:总是返回ok
事务的实现
正常执行事务的例子
事务的执行步骤:首先开启事务,其次向事务队列中加入命令。最后执行事务提交
例:事务的执行:
- multi:用multi命令告诉Redis,接下来要执行的命令你先不要执行,而是把它们暂时存起来(开启事务)
- sadd workers john:第一条命令进入等待队列(命令入队)
- sadd workers rose:第二条命令进入等待队列(命令入队)
- exce:告知redis执行前面发送的两条命令(提交事务)
入队命令语法错误的例子
例:事务的执行:
- multi:正常命令
- set key value:正常命令
- incr a b c d 666 677:命令语法错误
- exce:无法执行事务,那么第一条正确的命令也不会执行,所以key的值不会设置成功
语法正确,但执行错误的例子
例:事务的执行:
- multi:正常命令
- set username zhangsan:正常命令
- Ipop username:正常命令,语法没有错误,执行命令时才会有错误。
- exce:正常执行事务,发现第三行有错误,但是前两条已经执行成功了,没有回滚
主动放弃事务的例子
例:事务的执行:
- multi:正常命令
- set username zhangsan:正常命令
- Ipop username:正常命令,语法没有错误,执行命令时才会有错误。
- discard:我突然发现第3行有误,可以主动放弃事务管理
Redis的watch机制
WATCH机制:使用WATCH监视一个或多个key,跟踪key的value修改情况,如果有key的value值在事务EXEC执行之前被修改了,整个事务被取消。EXEC返回提示信息,表示事务己经失败。
WATCH机制使得事务EXEC变的有条件,事务只有在被WATCH的key没有修改的前提下才能执行。不满足条件,事务被取消。使用WATCH监视了一个带过期时间的键,那么即使这个键过期了,事务仍然可以正常执行
大多数情况下,不同的客户端会访问不同的键,相互同时竞争同一key的情况一般都很少,乐观锁能够以很好的性能解决数据冲突的问题。
watch的例子
- 在A客户端设置key:str.lp登录人数为10
- 在A客户端监视key:str.lp
- 在A客户端开启事务muti
- 在A客户端修改str.lp的值为11
- 在B客户端修改str.lp的值为15
- 在A客户端执行事务exec
- 在A客户端查看str.p值,A客户端执行的事务没有提交,因为WATCH的str.lp的值己经被修改了,所有放弃事务。
持久化
持久化可以理解为存储,就是将数据存储到一个不会丢失的地方,如果把数据放在内存中,电脑关闭或重启数据就会丢失,所以放在内存中的数据不是持久化的,而放在磁盘就算是一种特久化
Redis的数据存储在内存中,内存是瞬时的,如果linux宕机或重启,又或者Redis崩溃或重启,所有的内存数据都会丢失,为解决这个问题,Redis提供两种机制对数据进行持久化存储,便于发生故障后能迅速恢复数据。
RDB方式
什么是RDB方式? Redis Database(RDB),就是在指定的时间间隔内将内存中的数据集快照写入磁盘,数据恢复时将快照文件直接再读到内存。RDB保存了在某个时间点的数据集(全部数据)。存储在一个二进制文件中,只有一个文件。默认是dump.rdb。RDB技术非常适合做备份,可以保存最近一个小时,一天,一个月的全部数据。保存数据是在单独的进程中写文件,不影响Redis的正常使用。DB恢复数据时比其他AOF速度快。
如何实现? RDB方式的数据持久化,仅需在redis.conf文件配置即可,默认配置是启用的。 在配置文件redis.conf中搜索SNAPSHOTTING,查找在注释开始和结束之间的关于RDB的配置说明。配置SNAPSHOTTING地方有三处
-
配置执行RDB生成快照文件的时间策略。 对Redis进行设置,让它在 “N秒内数据集至少有M个key改动” 这一条件被满足时,自动保存一次数据集。
如:save 300 10 表示300秒内至少有10个key被改动时,会保存一次数据(可以有多条策略)
-
dbfilename:设置RDB的文件名,默认文件名为dump.rdb
-
dir:指定RDB文件的存储位置,默认是 ./ 当前目录
优缺点
优点:由于存储的是数据快照文件,恢复数据很方便,也比较快 缺点:
- 会丢失最后一次快照以后更改的数据。如果你的应用能容忍一定数据的丢失,那么使用rdb是不错的选择:如果你不能容忍一定数据的丢失,使用db就不是一个很好的选择。
- 由于需要经常操作磁盘,RDB会分出一个子进程。如果你的redis数据库很大的话,子进程占用比较多的时间,并且可能会影响Redis暂停服务一段时间(millisecond级别),如果你的数据库超级大并且你的服务器CPU比较弱,有可能是会达到一秒。
AOF方式
Append-only File(AOF),Redis每次接收到一条修改数据的命令时,它将把该命令写到一个AOF文件中(只记录写操作,读操作不记录),Redis重启时,它通过执行AOF文件中所有的命令来恢复数据。
如何实现?
AOF方式的数据持久化,仅需在redis.conf文件中配置即可
配置项:
- appendonly:默认是no,改成yes即开启了aof持久化
- appendfilename:指定AOF文件名,默认文件名为appendonly.aof
- dir:指定RDB和AOF文件存放的目录,默认是 ./
- appendfsync:配置向aof文件写命令数据的策略:
- no:不主动进行同步操作,而是完全交由操作系统来做(即每30秒一次),比较快但不是很安全。
- always:每次执行写入都会执行同步,慢一些但是比较安全。
- everysec:每秒执行一次同步操作,比较平衡,介于速度和安全之间。这是默认项。
- auto-aof-rewrite-min-size:允许重写的最小AOF文件大小,默认是64M。当aof文件大于64M时,开始整理aof文件,去掉无用的操作命令。缩小aof文件。
主从复制(高可用)
通过持久化功能,Redis保证了即使在服务器重启的情况下也不会丢失(或少量丢失)数据,但是由于数据是存储在一台服务器上的,如果这台服务器出现故障,比如硬盘坏了,也会导致数据丢失。为了避免单点故障,我们需要将数据复制多份部署在多台不同的服务器上,即使有一台服务器出现故障其他服务器依然可以继续提供服务这就要求当一台服务器上的数据更新后,自动将更新的数据同步到其他服务器上,那该怎么实现呢?Redis的主从复制。
主Redis(负责写) Master
|
---------|---------
| | |
从Redis 从Redis 从Redis(负责读) Slaver
如何实现?
可以在同一台机器上启动三次服务模拟一下
-
新建三个Redis的配置文件
如果Redis启动,先停止。作为Master的Redis端口是6380,作为Slaver的Redis端口分别是6382,6384
从原有的redis.conf拷贝三份,分别命名为redis6380.conf,redis6382.conf,redis6384.conf
-
编辑Master的配置文件redis6380.conf,在空文件加入如下内容 include /usr/local/redis-3.2.9/redis.conf daemonize yes port 6380 pidfile /var/run/redis_6380.pid logfile 6380.log dbfilename dump6380.rdb
-
编辑Slave的配置文件redis6382.conf和redis6384.conf,在空文件加入如下内容
- redis6382.conf: include /usr/local/redis-3.2.9/redis.conf daemonize yes port 6382 pidfile /var/run/redis_6382.pid logfile 6382.log dbfilename dump6382.rdb slaveof 127.0.0.1 6380
- redis6384.conf: include /usr/local/redis-3.2.9/redis.conf daemonize yes port 6384 pidfile /var/run/redis_6384.pid logfile 6384.log dbfilename dump6384.rdb slaveof 127.0.0.1 6380
-
启动三个服务,redis-server命令后跟上配置文件
-
进入客户端,输入
info replication查看信息 -
在主服务器写入,从服务器读取,注意,从服务器只读
容灾处理
当Master服务出现故障,需手动将slave中的一个提升为master,剩下的slave挂至新的master上(冷处理:机器挂掉了,再处理) 命令:
- slaveof no one:将一台slave服务器提升为Master(提升某6382slave为master)
- slaveof 127.0.0.1 6382(将slave挂至新的master上)
这种需要人为参与,有延迟
Sentinel哨兵(高可用)
Sentinel哨兵是redis官方提供的高可用方案,可以用它来监控多个Redis服务实例的运行情况。Redis Sentinel是一个运行在特殊模式下的Redis服务器。Redis Sentinel是在多个Sentinel进程环境下互相协作工作的。
Sentinel系统有三个主要任务:
- 监控:Sentinel不断的检查主服务和从服务器是否按照预期正常工作。
- 提醒:被监控的Redis出现问题时,Sentinel会通知管理员或其他应用程序。
- 自动故障转移:监控的主Redis不能正常工作,Sentinel会开始进行故障迁移操作。
哨兵在独立的服务器上,一块监控主从服务器,一旦发现主服务器ping不通,就会投票,多数哨兵认为主服务器挂掉了,则直接进行主从复制
如何实现?
复制三份sentinel.conf文件
Sentinel系统默认port是26379,三个配置port分别设置为26380,26382,26384。 三个文件分别命名:
- sentinel26380.conf
- sentinel26382.conf
- sentinel26384.conf
分别修改三个配置文件
- 分别修改成port26380、port26382、port26384
- 修改sentinel monitor mymaster 127.0.0.1 6380 2
格式:
sentinel monitor <name> <masterIP> <masterPort> <Quorum投票数>
用redis-sentinel命令带上各自的配置文件分别启动sentinel服务,
安全设置
设置密码
访问Redis默认是没有密码的,这样不安全,任意用户都可以访问。可以启用使用密码才能访问Redis。设置Redis的访问密码,修改redis.conf中这行requirepass。密码要比较复杂,不容易破解,而且需要定期修改。因为redis速度相当快,所以在一台比较好的服务器下,一个外部的用户可以在一秒钟进行150K次的密码尝试,需要指定非常非常强大的密码来防止暴力破解。
加上密码后访问客户端只能 redis-cli -a 密码 开启客户端了
或者redis-cli回车,输入auth 密码
绑定IP
修改redis.conf文件,把# bind 127.0.0.1 前面的注释#号去掉,然后把127.0.0.1改成允许访问你redis服务器的ip地址,表示只允许该ip进行访问。多个ip使用空格分隔。例如bind 192.168.1.100 192.168.2.10
修改端口号
修改redis的端,这一点很重要,使用默认的端口很危险,redis.conf中修改port 6379,将其修改为自己指定的端口(可随意),端口1024是保留给操作系统使用的。用户可以使用的范围是1024-65535
使用-p参数指定端口,例如:redis-cli -p 新设置端口
Jedis操作Redis
使用Redis官方推荐的Jedis,在java应用中操作Redis。Jedis几乎函盖了Redis的所有命令。操作Redis的命令在Jedis中以方法的形式出现。jedis完全兼容redis2.8.x和3.x.x
下载Jedis的jar包,创建Jedis对象,调用方法即可
引入依赖
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>4.2.3</version>
</dependency>
package com.pan.test;
import org.junit.Test;
import redis.clients.jedis.Jedis;
public class JedisTest {
@Test
public void test() {
String host = "127.0.0.1";
int port = 6379;
Jedis jedis = new Jedis(host, port);
jedis.auth("12345");// 可以输入密码
jedis.set("name", "小明");
String name = jedis.get("name");
System.out.println(name);// 小明
}
}
通过网络,访问Redis服务器:
- 修改redis.conf启动redis需要指定redis.conf的位置
- 关闭linux防火墙,或者让redis的端口通过防火墙
直接连接线程是不安全的,可以通过连接池
先引入依赖
<dependency>
<groupId>commons-pool</groupId>
<artifactId>commons-pool</artifactId>
<version>1.6</version>
</dependency>
创建工具类
package com.pan.test;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
public class RedisUtil {
private static JedisPool pool;
// 创建JedisPool对象
public static JedisPool open(String ip, int port) {
if (pool == null) {
// 创建JedisPool
// 创建JedisPoolConfig,给config设置连接池的参数,使用config对象创建JedisPool
JedisPoolConfig config = new JedisPoolConfig();
// 给config设置连接池参数
config.setMaxTotal(20);// 设置最大线程数,一个线程就是一个Jedis
// 设置最大空闲数
config.setMaxIdle(2);
// 设置检查项为true,表示从线程池中获取的对象一定是经过检查可用的
config.setTestOnBorrow(true);
// 创建pool对象
pool = new JedisPool(config, ip, port, 6000);// 最后的参数是超时时间,当然也可以加上密码
}
return pool;
}
// 关闭池对象
public static void close() {
if (pool != null) {
pool.close();
}
}
}
测试
package com.pan.test;
import org.junit.Test;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
public class JedisByPool {
@Test
public void test() {
String host = "127.0.0.1";
int port = 6379;
JedisPool pool = null;
Jedis jedis = null;
try {
pool = RedisUtil.open(host, port);
// 从pool中获取Jedis
jedis = pool.getResource();
// 操作
jedis.set("name", "小明");
String name = jedis.get("name");
System.out.println(name);// 小明
} finally {
// 关闭对象Jedis,把从pool中获取的Jedis放回到pool,共其它请求使用
if (jedis != null) {
jedis.close();
}
}
}
}