扫二维码与项目经理沟通
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流
创新互联数据库Redis标准版-双副本模式采用主从(master-replica)模式搭建。主节点提供日常服务访问,备节点提供HA高可用,当主节点发生故障,系统会自动在30秒内切换至备节点,保证业务平稳运行。
创新互联公司是一家集网站建设,克州企业网站建设,克州品牌网站建设,网站定制,克州网站建设报价,网络营销,网络优化,克州网站推广为一体的创新建站企业,帮助传统企业提升企业形象加强企业竞争力。可充分满足这一群体相比中小企业更为丰富、高端、多元的互联网需求。同时我们时刻保持专业、时尚、前沿,时刻以成就客户成长自我,坚持不断学习、思考、沉淀、净化自己,让我们为更多的企业打造出实用型网站。
开源、底层采用 C语言、遵守 BSD 协议。
高性能 key/value 分布式内存数据库,基于内存运行。
支持持久化的 NOSQL 数据库。
和其他 NoSQL 相比,Redis 有三个特点:
支持数据的持久化,可以将内存中的数据持久化到磁盘中,重启之后可以再次使用。
不仅仅支持 key/value,还提供对 list、set、zset、hash 等数据类型的支持。
支持主从备份。
将 Redis 安装包移动到 opt 目录下。
通过 tar -zxvf redis-3.0.4.tar.gz 解压。
进入解压后的目录执行 make,假如没有安装 gcc,就执行 yum install gcc-c++ 命令,然后运行 make distclean 之后执行 make。
make install。
Redis 是一个单进程的。
它有16个数据库,默认为0号数据库。
SELECT:切换数据库。
DBSIZE:查看当前数据库的 key 的数量。
FLUSHDB:清空当前库。
FLUSHALL:清空所有库。
同意密码管理,16个库都是相同密码。
默认端口是 6379。
keys * 查看当前库的所有 key
exists key 判断 key 是否存在
move key db 将当前库的某个 key 移到其他库
expire key 设置 key 的过期时间
ttl key 查看 key 的剩余使用实现
type key 查看 key 的类型
String 是 Redis 最基本的类型,一个 key 对应一个 vlaue,String 是二进制安全的,所以它可以包含所有数据,value 最多可以包含512M数据。
// 设置一个值
SET k1 v1
// 获取key对应的value
GET k1
// 添加一个value
APPEND k1
// 查看key对应的value的长度
STRLEN k1
// 每次增加1
INCR k1
// 每次减少1
DECR k1
// 每次增加2
INCRBY k1 2
// 每次减少2
DECRBY k1 2
// 获取范围内值
GETRANGE k1 0 2
// 设置键存活的时间
SETEX k1 20 v1
// 设置时判断是否存在key,如果存在就不做改变
SETNX k1 v1
// 同时设置多个值,如果键存在,修改值
MSET k1 v1 k2 v2
// 同时获取多个值
MGET k1 k2
// 同时设置多个值,如果存在键,执行失败
MSETNX k1 v1 k2 v2
// 设置并返回设置的value
getset k1 v1
是一个键值对集合,类似与 Java 里的 Map
KV 模式不变,但 V 是一个键值对。
// 设置一个user,user的id属性是11
HSET user id 1
// 获取 user 的 id 属性
HGET user id
// 设置一个user的多个值
HMSET user name kernel age 18
// 获取多个属性
HMGET user name age
// 获取所有属性
HGETALL
// 删除user中的name属性
HDEL user name
// 查看user有几个属性
HLEN user
// 查看user中是否存在name属性
HEXISTS user name
// 查看user的所有key
HKEYS user
// 查看user的所有value
HVALS user
// 自增2
HINCRBY user age 2
// 增加小数
HINCRBYFLOAT user score 0。5
// 不存在才能往里放
HSETNX user email kernel@163。com
简单的字符串列表,按照插入顺序排序,底层是一个链表。
单键多值。
它是一个字符串链表,可以从左右两边添加。
如果键不存在,创建链表,键存在,增加值,弹出所有值,键就失效了。
操作头尾效率高,中间元素效率低。
// 从左边向key中压入值
LPUSH list01 1 2 3 4 5
// 从右边向 key 中压入值
RPUSH list02 1 2 3 4 5
// 从左边查看范围内的值
Lrange list01 0 -1
// 从左边弹栈
LPOP
// 从右边弹栈
RPOP
// 查看指定索引的值
LINDEX list01 2
// 查看指定key的长度
LLEN list01
// 从key中从左边删除3个3
LREM list01 3 3
LTRIM list01 0 3 截取0-3赋值给key
// 从前面的key右边弹出一个值赋值给后面key
RPOPLPUSH list01 list02
String 类型的无序集合,采用 HashTable 实现的。
单键多值。
// 添加多个值,重复值不添加
SADD set01 1 2 3 4 5 1 2 3 4 5
// 查看set01的值
SMEMBERS set01
// 查看set01是否存在5
SISMEMBER set01 5
// 查看set01的元素个数
SCARD set01
// 删除set01中的3
SREM set01 3
// 从set01中随机取3个数
SRANDMEMBER set01 3
// 随机弹栈
SPOP set01
// 将 set01 中的5 移动到 set02 中
SMOVE set01 set02 5
// 差集,在set01不在set02中的值
SDIFF set01 set02
// 交集
SINTER set01 set02
// 并集
SUNION set01 set02
set 和 zset 的异同点:
相同点:String 类型元素的集合,不允许重复的成员。
不同点:每个元素都会关联一个 double 类型的分数。
// 设置值
ZADD zset01 60 v1 70 v2 80 v3 90 v4 100 v5
// 范围内取值,withscores表示包含score
ZRANGE zset01 0 -1
// 范围内取值包括score
// 区间内取值,(表示不包含,limit 开始下标,走多少步
ZRANGEBYSCORE zset01 60 80
// 删除某score对应的value
ZREM zset01 v4
// 查看zset01的个数
ZCARD zset01
// 查看区间内个数
ZCOUNT zset01 60 90
// 取下标
ZBANK zset01 60
// 取v4对应的值
ZSCORE zset01 v4
// 逆序获得v3对应的下标
ZREVRANK zset01 v3
// 逆序取范围内的值
ZREVRANGE zset01 0 -1
// 逆序区间内取值
ZREVRANGEBYSCORE zset01 90 60
1k => 1000 bytes
1kb => 1024 bytes
1m => 1000000 bytes
1mb => 10241024 bytes
1g => 1000000000 bytes
1gb => 10241024*1024 bytes
可以通过 includes 包含,redis.conf 可以作为总闸,包含其他配置。
daemonize:配置作为守护进程运行。
pidfile:如以后台进程运行,Redis 将会把 pid 写入到 /var/run/redis.pid 文件,可以指定文件。
bind 127.0.0.1:绑定的主机地址,如果想全部访问不做限制的话,全为0.0.0.0。
timeout 300:当客户端闲置多长时间后关闭连接,如果指定为0,表示关闭该功能。
loglevel verbose:日志等级,有 debug、verbose、notice、warning,默认是 verbose。
logfile "" :日志文件保存位置,如果配置Redis为守护进程方式运行,而这里又配置为日志记录方式为标准输出,则日志将会发送给 /dev/null。
tcp-keepalive:单位为秒,如果设置为0,则不会进行 Keepalive 检测,建议设置成60。
syslog-enabled:是否把日志输出到 syslog 中。
syslog-ident:指定 syslog 里的日志标志。
syslog-facility:指定 syslog 设备,值可以是USER或 LOCAL0-LOCAL7。
Save 默认出厂设置如果1分钟写了10000次、5分钟写了10次、15分钟写了1次都会触发备份,如果想禁用 RDB 持久化策略,将 save 设为 save ""。
stop-writes-on-bgsave-error:如果配置成 no,表示你不在乎数据不一致或者有其他的手段发现和控制。
rdbcompression:对于存储到磁盘中的快照,可以设置是否进行压缩存储。如果是的话,Redis 会采用 LZF 算法进行压缩。如果你不想消耗 CPU 来进行压缩的话,可以设置为关闭此功能。
rdbchecksum:在存储快照后,还可以让 Redis 使用 CRC64 算法来进行数据校验,但是这样做会增加大约10%的性能消耗,如果希望获取到最大的性能提升,可以关闭此功能。
dbfilename
dir
CONFIG SET requirepass 123456 将密码改为 123456,默认为空,设置后,执行任何命令之前必须执行 auth 123456。
maxclients 最大连接客户端数量
maxmemory 设置 Redis 可以使用的内存量。一旦到达内存使用上限,Redis 将会试图移除内部数据。
maxmemory-policy :
volatile-lru:使用 LRU 算法移除 key,只对设置了过期时间的键。
allkeys-lru:使用 LRU 算法移除 key。
volatile-random:在过期集合中移除随机的 key,只对设置了过期时间的键。
allkeys-random:移除随机的 key。
volatile-ttl:移除那些 TTL 值最小的 key,即那些最近要过期的 key。
noeviction:不进行移除。针对写操作,只是返回错误信息。
maxmemory-samples:设置样本数量,LRU 算法和最小 TTL 算法都并非是精确的算法,而是估算值,所以你可以设置样本的大小,Redis 默认会检查这么多个 key 并选择其中 LRU 的那个。
appendonly:配置 AOF 持久化。
appendfilename:持久化文件名。
appendfsync:同步
always:同步持久化 每次发生数据变更会被立即记录到磁盘 性能较差但数据完整性比较好
everysec:出厂默认推荐,异步操作,每秒记录 如果一秒内宕机,有数据丢失。
no:从不同步。
no-appendfsync-on-rewrite:重写时是否可以运用 Appendfsync,用默认 no 即可,保证数据安全性。
auto-aof-rewrite-min-size:设置重写的基准值。
参数说明
redis.conf 配置项说明如下:
Redis 会单独创建(fork)一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程是不进行任何 IO 操作的,这就确保了极高的性能,如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那 RDB 方式要比 AOF 方式更加的高效。RDB 的缺点是最后一次持久化后的数据可能丢失。
RDB 保存的是 dump.rdb 文件。
作用是复制一个与当前进程一样的进程,新进程的所有数据都和原进程一样,但是是一个新的进程,并作为原进程的子进程。
在 redis.conf 文件中的 SNAPSHOTTING 下面。
按照配置触发。
执行 save 或者 bgsave,save 只管保存,其他全部堵塞,bgsave 是后台异步进行快照,还可以接收用户请求。
执行 flushall 命令也会触发,但是由于执行了该命令清空所有库的数据,所有 dump.rdb 文件中是空的。
适合大规模的数据恢复。
对数据完整性和一致性要求不高。
在一定间隔时间做一次备份,所以如果 Redis 意外 down 掉的话,就会丢失最后一次快照后的所有修改。
fork 的时候,内存中的数据被克隆了一份,大致2倍的膨胀性需要考虑。
动态停止:所有停止 RDB 保存规则的方法:redis-cli config set save ""。
RDB 是一个非常紧凑的文件,在保存 RDB 文件的时候父进程会 fork 出一个子进程,接下来全部工作由子进程负责,父进程不需要做任何 IO 操作,所有 RDB 方式可以最大化 Redis 的性能,与 AOF 相比,它更适合做大规模的数据回复,对数据完整性、一致性要求不高,数据丢失风险高,因为该方式 fork 子进程来持久化,当数据量大的时候,fork 的过程非常耗时,可能会导致在毫秒级上 Redis 不能响应客户端请求。
以日志的形式来记录每个写操作,将 Redis 执行过的所有写指令记录下来(读操作不记录),只许追加文件但不可以改写文件,Redis 启动之初会读取该文件重新构建数据,换言之,Redis 重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。
AOF 保存的是 appendonly.aof 文件。
APPEND ONLY MODE,将 appendonly no 改为 yes。
正常恢复:
将有数据的 AOF 文件复制一份保存到对应目录 (config get dir)。
恢复:重启 Redis 然后重新加载。
异常恢复:
备份被写坏的 AOF 文件。
redis-check-aof --fix 文件名 进行修复。
恢复:重启 Redis 然后重新加载。
AOF 采用文件追加方式,随着时间的推移文件越来越大,为了避免出现这种情况,新增了重写机制,当 AOF 文件超过预先设定的阈值时,Redis 就会启动内容压缩,只保留可以恢复数据的最小指令集,可以使用命令 bgrewriteaof。
原理:
AOF 文件持续增长而过大时,会 fork 出一条新进程来将文件重写,遍历新进程的内存中数据,每条记录有一条的Set语句。重写 AOF 文件的操作,并没有读取旧的 AOF 文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的 AOF 文件,这点和快照有点类似。
触发机制:
Redis 会记录上次重写时的 AOF 大小,默认配置是当 AOF 文件大小是上次 rewrite 后大小的一倍且文件大于64M 时触发。
每秒同步:appendfsync always 同步持久化每次发生数据变更会被立即记录到磁盘,性能较差但数据完整性比较好。
每修改同步:appendfsync everysec 异步操作,每秒记录,如果一秒内宕机,有数据丢失。
不同步:appendfsync no 从不同步。
相同数据集的数据而言 AOF 文件要远大于 RDB 文件,恢复速度慢于 RDB。
AOF 文件是一个只进行追加的日志文件,Redis 可以在 AOF 文件体积过大时进行重写,AOF 文件有序的保存了对数据的写操作,这些写操作以 Redis 协议被很好的保存,因此,AOF 文件的内容非常容易被读懂,对文件的分析也很容易。对于相同的数据集来说,AOF 的体积通常要大于 RDB,根据所使用的 fsync 策略,AOF 的速度要慢于 RDB。
RDB 持久化方式能够在指定的时间间隔能对你的数据进行快照存储。
AOF 持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF 命令以 Redis 协议追加保存每次写的操作到文件末尾。Redis 还能对 AOF 文件进行后台重写,使得 AOF 文件的体积不至于过大。
如果你只希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化方式。
在这种情况下,当 Redis 重启的时候会优先载入 AOF 文件来恢复原始的数据,因为在通常情况下 AOF 文件保存的数据集要比 RDB 文件保存的数据集要完整。
RDB 的数据不实时,同时使用两者时服务器重启也只会找 AOF 文件。
那要不要只使用 AOF 呢?
建议不要,因为 RDB 更适合用于备份数据库 (AOF在不断变化不好备份),快速重启,而且不会有 AOF 可能潜在的 bug,留着作为一个万一的手段。
因为 RDB 文件只用作后备用途,建议只在 Slave 上持久化 RDB 文件,而且只要15分钟备份一次就够了,只保留 save 900 1 这条规则。
如果 Enalbe AOF,好处是在最恶劣情况下也只会丢失不超过两秒数据,启动脚本较简单只 load 自己的 AOF 文件就可以了。代价一是带来了持续的 IO,二是 AOF rewrite 的最后将 rewrite 过程中产生的新数据写到新文件造成的阻塞几乎是不可避免的。只要硬盘许可,应该尽量减少 AOF rewrite 的频率,AOF 重写的基础大小默认值 64M 太小了,可以设到 5G 以上。默认超过原大小100%大小时重写可以改到适当的数值。
如果不 Enable AOF ,仅靠 Master-Slave Replication 实现高可用性也可以。能省掉一大笔 IO 也减少了 rewrite 时带来的系统波动。代价是如果 Master/Slave 同时倒掉,会丢失十几分钟的数据,启动脚本也要比较两个 Master/Slave中的 RDB 文件,载入较新的那个。新浪微博就选用了这种架构。
悲观锁(Pessimistic Lock), 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会 block 直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。
乐观锁(Optimistic Lock), 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,乐观锁策略:提交版本必须大于记录当前版本才能执行更新。
DISCARD 取消事务,放弃执行事务块内的所有命令
EXEC 执行所有事务块内的命令
MULTI 标记一个事务块的开始
UNWATCH 取消 WATCH 命令对所有 key 的监视
WATCH key [key...] 监视一个或多个 key,如果在事务执行之前这个 key 被其他命令所改动,那么事务将被打断
正常执行:
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set k2 v22
QUEUED
127.0.0.1:6379> set k3 v33
QUEUED
127.0.0.1:6379> EXEC
1) OK
2) OK
放弃事务:
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> DISCARD
OK
127.0.0.1:6379> get k3
"v33"
全体连坐:
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> setget k2 v2
(error) ERR unknown command 'setget'
127.0.0.1:6379> EXEC
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get k3
"v33"
怨头债主:
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> INCR k2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> EXEC
1) OK
2) (error) ERR value is not an integer or out of range
3) OK
WATCH 监控:
对某个 key 监控之后,如果有别的客户端将这个 key 修改,则事务执行失败。
UNWATCH 的作用是放弃监控。
一旦执行 EXEC 无论成功与否所有监控所都被取消掉了。
WATCH 命令相当于乐观锁,事务提交时,如果 key 已经被修改,整个事务队列将执行失败,如果监视了多个 key,只要其中有一个 key 被修改,则整个队列执行失败,同时返回 Nullmulti-bulk 通知调用者事务执行失败。
开启:以MULTI开始一个事务
入队:将多个命令入队到事务中,接到这些命令并不会立即执行,而是放到等待执行的事务队列里面
执行:由EXEC命令触发事务
单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
没有隔离级别的概念:队列中的命令没有提交之前都不会实际的被执行,因为事务提交前任何指令都不会被实际执行,也就不存在事务内的查询要看到事务里的更新,在事务外查询不能看到”这个让人万分头痛的问题。
不保证原子性:Redis 同一个事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚。
进程间的一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。
首先需要订阅者订阅才可以收到消息,SUBSCRIBE c1 c2 c3。
然后发布者向频道发送消息,订阅者就可以收到了,比如 PUBLISH c3 word。
订阅者订阅频道时候还可以使用通配符,例如 SUBSCRIBE new*。
这样无论向 new1 频道还是 new2 频道发送消息订阅者都可以收到。
也就是我们所说的主从复制,主机数据更新后根据配置和策略,自动同步到备机的 Master/Slaver 机制,Master 以写为主,Slave 以读为主。
读写分离
容灾恢复
配从不配主。
从库配置:slaveof 主库 IP 主库端口,没次与主库断开连接后,都需要重新连接,除非写入到配置文件中。可以通过 info replication 查看当前数据库的配置。
常用三种方式:
一主二仆:
一个 Master 配置两个 Slave。Salve 只能读,不可以写,当 Master 挂掉,Salve 原地等待,Master 恢复后,自动连接,如果 Slave 挂掉,需要手动重新连接。
薪火相传:Master 配置一个 Slave,Slave 可以是下一个 Salve 的 Master,有效减轻 Master 的压力。
反客为主:通过 Salve no one 使当前数据库停止与其他数据库的同步,转成 Master。
Slave 启动成功连接到 Master 后会发送一个 sync 命令。
Master 接到命令启动后台的存盘进程,同时收集所有接收到的用于修改数据集命令,在后台进程执行完毕之后,Master 将传送整个数据文件到 Slave,以完成一次完全同步。
全量复制:而 Slave 服务在接收到数据库文件数据后,将其存盘并加载到内存中。
增量复制:Master 继续将新的所有收集到的修改命令依次传给 Slave,完成同步。
但是只要是重新连接 Master,一次完全同步(全量复制)将被自动执行。
反客为主的自动版,监控主库是否挂掉,如果挂掉自动选出一个从库作为主库。
新建 sentinel.conf 文件,配置哨兵, sentinel monitor 被监控数据库名字 127.0.0.1 6379 1,1表示主机挂掉 Salve 投票看谁能当主机,得票多的当主机。
启动哨兵,redis-sentinel sentinel.conf 。
如果原有的 Master 挂掉,会自动选举出一个 Master,当 Master 恢复后,原 Master 作为 新 Master 的 Salve。
缺点:由于所有的写操作都是先在 Master 上操作,然后同步更新到 Slave 上,所以从 Master 同步到 Slave 机器有一定的延迟,当系统很繁忙的时候,延迟问题会更加严重,Slave 机器数量的增加也会使这个问题更加严重。
开放端口
firewall-cmd --zone=public --add-port=6379/tcp --permanent
配置阿里云安全组规则
查看开放端口
firewall-cmd --zone=public --list-ports
commons-pool-1.6.jar
jedis-2.1.0.jar
获取 Jedis 实例需要从 JedisPool 中获取,用完要返还给 JedisPool,出错也要返还给 JedisPool。
自定义 JedisPoolUtil
public class JedisPoolUtil {
// 保证此变量对所有的线程的可见性,禁止指令重排序优化
private static volatile JedisPool jedisPool = null;
// 私有化构造方法
private JedisPoolUtil() {
}
// 提供池子,采用双重检验锁机制
public static JedisPool getJedisPool() {
if (null == jedisPool) {
synchronized (JedisPool.class) {
if (null == jedisPool) {
JedisPoolConfig poolConfig = new JedisPoolConfig();
// 最大连接数
poolConfig.maxActive = 1000;
// 最大等待连接数
poolConfig.maxIdle = 32;
// 最大等待时间
poolConfig.setMaxWait(100 * 1000);
// 获得实例时是否检测可用性
poolConfig.setTestOnBorrow(true);
jedisPool = new JedisPool(poolConfig, "127.0.0.1", 6379);
}
}
}
return jedisPool;
}
public static void release(JedisPool jedisPool, Jedis jedis) {
if (jedis != null) {
jedisPool.returnResourceObject(jedis);
}
}
}
双重检验锁是对同步块加锁的方法,为什么教双重检验呢,那是因为有两次判空操作。
为何在同步块外面判空?
为了提高性能,如果去掉同步块外面判空,只要调用此方法,都会拿到一个静态内部锁,降低了效率,所以在外面加判空,降低了同步块的执行次数。
为什么还在同步块内判空呢?
可能会有多个线程同时进入同步块外面的判空,但是有线程先一步拿到了对象,如果不再内部判空,可能会生成多个实例。
一定要使用 volatile 修饰静态变量,否则由于多线程,还是会创建多个实例。
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流