Redis笔记
入门概述
1.是什么
Redis:(REmote Dictionary Serve)远程字典服务器,是一个完全开源免费的,用C语言编写,遵守BSD协议;是一个高性能的(key/value)分布式内存数据库,基于内存运行并支持持久化的NoSQL数据库,且拥有丰富的数据类型(string,list,set,zset,hash)等,支持数据的备份(即master-slave模式的数据备份)
2.主要用途
- **内存存储和持久化:**redis支持异步将内存中的数据写到硬盘上,同事不影响继续服务
- **取最新N个数据的操作:**如可以将最新的10条评论的ID放在redis的list集合里
- 模拟类似于HttpSession这种需要设定过期时间的功能
- 发布、订阅消息系统
- 定时器、计数器
3.下载地址
4.redis启动后注意事项
- 默认16个数据库,类似数组,下标从0至15,初始默认使用0号库,且索引是0开始
- select 下标命令切换数据库
- Dbsize命令查看数据库的key数量
- **Flushdb:**清空当前库
- **Flushall:**清空所有库
- 同意密码管理,16个库都是同样密码,要么都OK要么一个也连接不上
- 默认使用端口6379
Redis数据结构
Redis的常见五大数据类型
- **String:**redis最基本的数据类型,一个redis中字符串value最多可以存512M,string类型是二进制安全的(即redis的string可以包含任何数据。比如JPG图片或者序列化对象)
- **Hash(哈希):**类似于java中的Map<String,Object>,Redis hash是一个键值对集合
- **List(列表):**类似于java中的List,按照插入顺序排序,底层实现是一个列表
- **Set(集合):**是一个string类型的无序无重复集合。底层由HashTable实现,
- **Zset(sorted set):**是一个string类型的有序无重复集合,redis通过分数(score)来为集合中的成员进行从小到大排序,zset的成员是惟一的,但分数(score)可以重复
常用操作命令
Redis键(key)
- **keys * :**查询出当前库的所有key
- **exists key:**判断某个key是否存在
- **move key db:**移动某个key到指定的数据库。如move name 3,表示把name从当前库移动到3号库
- **expire key time:**为给定的key设置过期时间。如expire name 10,设置name在10秒后过期
- **ttl key:**查看还有多少秒过期,-1表示永不过期。-2表示已过期
- **type key:**查看key是什么类型
Redis字符串(string)
-
set/get/del/append/strlen
127.0.0.1:6379> set name zhangan OK 127.0.0.1:6379> get name "zhangan" 127.0.0.1:6379> append name zhangsanveryshy (integer) 22 127.0.0.1:6379> get name "zhanganzhangsanveryshy" 127.0.0.1:6379> STRLEN name (integer) 22
-
**incr/decr/incrby/decrby:**一定要是数字才能进行加减
-
**setrang/getrange:**范围设置、取值
127.0.0.1:6379> set name zhangsan OK 127.0.0.1:6379> SETRANGE name 0 li (integer) 8 127.0.0.1:6379> get name "liangsan" 127.0.0.1:6379> get name "liangsan" 127.0.0.1:6379> GETRANGE name 0 4 "liang"
-
setex(相当于set with expire )键秒值/setnx(相当于set if not exist) :
127.0.0.1:6379> SETEX sex 10 girl OK 127.0.0.1:6379> SETNX age 13
-
**mset/mget/msetnx:**批量设置、获取,每次mset设置具有原子性,存在一个设置失败就不会设置成功
MSET name zhangsna age 13 height 180 OK
Redis列表(List)
-
**lpush/rpush/lrange:**添加与取值
-
**lpop/rpop:**弹出栈顶值
-
**lindex:**按照索引下标获取元素,从0开始,由上往下
-
**llen:**获取指定key的存储长度
-
**lrem key 【删除数量】:**删除指定数量的value
-
**ltrim key 【startIndex】【endIndex】:**截取指定范围的值后再赋值给key
-
**rpoplpush 源列表 目标列表:**将原列表的最右侧元素添加到目标列表的最左侧
-
linsert key (before|after) val1 val2
127.0.0.1:6379> LPUSH hobby 1 2 3 4 5 #从左开始添加 (integer) 5 127.0.0.1:6379> LRANGE hobby 0 -1 # 取值,-1表示最后一个 1) "5" 2) "4" 3) "3" 4) "2" 5) "1" 127.0.0.1:6379> RPUSH k1 1 2 3 4 5 # 从右开始添加 (integer) 5 127.0.0.1:6379> LRANGE k1 0 -1 1) "1" 2) "2" 3) "3" 4) "4" 5) "5" 127.0.0.1:6379> LPOP hobby #取出最左侧 "5" 127.0.0.1:6379> RPOP hobby #取出最右侧 "1" 127.0.0.1:6379> LRANGE hobby 0 -1 1) "4" 2) "3" 3) "2" 127.0.0.1:6379> LINDEX hobby 0 # 按照索引下标获取元素 "4" 127.0.0.1:6379> LLEN hobby # 获取指定key存储value的长度 (integer) 3 127.0.0.1:6379> LPUSH kk1 1 2 3 4 4 5 #演示lrem (integer) 6 127.0.0.1:6379> LREM kk1 2 4 # 删除kk1中的两个4 (integer) 2 127.0.0.1:6379> LRANGE kk1 0 -1 1) "5" 2) "3" 3) "2" 4) "1" 127.0.0.1:6379> LTRIM kk1 0 2 #截取kk1的0至2位数字并赋值给key OK 127.0.0.1:6379> LRANGE kk1 0 -1 1) "5" 2) "3" 3) "2"
Redis集合(set)
-
sadd/smembers/sismember
-
**scard:**获取集合里面的元素个数
-
**srem key value:**删除集合中的元素
-
**srandmember key count(可选):**随机获取集合中的成员,可以指定个数
-
**spop key:**随机出栈
-
**smove key1 key2 key1中的值:**将key1里的某个值赋给key2
-
**sdiff key1 key 2:**取key1和key2的差集
-
**sinter key1 key2:**取key1和key2的交集
-
**sunion key1 key2:**取key1和key2的并集
127.0.0.1:6379> sadd set01 1 2 2 3 3 4 #向set中添加成员 (integer) 4 127.0.0.1:6379> SMEMBERS set01 # 查看set中的所有成员 1) "1" 2) "2" 3) "3" 4) "4" 127.0.0.1:6379> SISMEMBER set01 2 #查看set集合中是否存在指定成员,存在返回1,不存在返回0 (integer) 1 127.0.0.1:6379> SISMEMBER set01 5 (integer) 0 127.0.0.1:6379>SREM set01 3 # 删除set01中的成员3 (integer) 1 127.0.0.1:6379> sadd user1 zhangsan lisi wangwu (integer) 3 127.0.0.1:6379> sadd user2 mayun mahuaten (integer) 2 127.0.0.1:6379> SMOVE user1 user2 lisi (integer) 1 127.0.0.1:6379> SMEMBERS user2 1) "mayun" 2) "mahuaten" 3) "lisi"
Redis哈希(hash)
-
hset/hget/hmset/hmget/hgetall/hdel
-
**hlen:**获取key的元素个数
-
**hexists key field:**查看key中的某个field是否存在,不存在返回0
-
**hkeys/hvals:**获取所有key或value
-
**hincrby/hincrbyfloat key field [int/float]:**将指定的key中field的值与指定值相加
127.0.0.1:6379> HSET user name zhangsan #向hash中加入user (integer) 3 127.0.0.1:6379> HGET user name # 获取user中的name "zhangsan" 127.0.0.1:6379> HMSET customer id 11 name lisi age 26 #批量添加多个field-value OK 127.0.0.1:6379> HMGET customer id name age # 获取customer中多个field 1) "11" 2) "lisi" 3) "26" 127.0.0.1:6379> HGETALL customer # 获取customer中的所有field-value 1) "id" 2) "11" 3) "name" 4) "lisi" 5) "age" 6) "26" 127.0.0.1:6379> HDEL customer age #删除customer中的age (integer) 1 127.0.0.1:6379> HGETALL customer 1) "id" 2) "11" 3) "name" 4) "lisi" 127.0.0.1:6379> HEXISTS customer age #查看customer中的age是否存在,存在返回1,不存在返回0 (integer) 0 127.0.0.1:6379> HEXISTS customer id (integer) 1 127.0.0.1:6379> HKEYS customer #获取customer中所有key 1) "id" 2) "name" 127.0.0.1:6379> HVALS customer #获取customer中所有value 1) "11" 2) "lisi"
Redis有序集合(sorted set)
- zadd/zrange
- **zrangebyscore key startScoree endScore:**获取指定分数范围内的成员
- **zrevrange/zrevarangebyscore:**与上相反取值
- zrem key val: 删除key中对应的val
- zcard/zcount key score区间、zrank key values值/zscore key 对应值
Redis配置文件(redis.conf)位置:位于安装目录的src
-
includes(包含):用于包含其他地方的配置文件,类似于jsp的includes
-
general:通用配置
- daemonize -> 设置reidis以后台进程运行,默认为false
- port -> 端口号
- pidfile -> 配置pid的写入文件地址,只有设置了守护进程有效
- tcp-backlog -> 连接队列,在高并发环境下,需要一个高backlog值来避免慢客户端连接问题。注意linux内核会将这个值家笑道/proc/sys/net/core/somaxconn的值,所以需要确认增大somaxconn和tcp_max_syn_backlog两个值来达到想要的效果
backlog队列总和=未完成三次握手队列+已完成三次握手队列
;默认值511 - bind ->绑定ip,可以设置允许指定的ip连接。注释时表示任意ip,
注意指定ip后将ip添加到防火墙
- timeout -> 客户端闲置超时时间,0表示不超时,不为0时表示连接隔多少秒关闭
- tcp-keepaliv -> 单位为秒,如果设置为0,则不会进行keeplive检测,建议设置成60
- loglevel -> 日志级别,信息有多到少debug->verbose->notice->warning
- logfile -> 指定日志的输出文件
- syslog-enable ->是否输出日志到syslog(系统日志)中,默认No
- database -> 设置库数量,默认16个
-
SECURITY:安全设置
- **requirepass:**登录密码
-
SNAPSHOTTING(快照)
- **save:**保存DB到磁盘上。用来持久化。方式
save 秒 写操作次数
,例如save 10 5 表示10秒内有五次改变就触发备份 - **stop-writes-on-bgsave-error:**后台备份出错时是否禁止写操作。默认yes
- **rdbcompression:**对于存储到磁盘中的快照,可以设置是否进行压缩存储。设置yes时,redis会采用LZF算法进行压缩。若不小消耗CPU来进行压缩,可以设置no
- **rdbchecksum:**在存储快照后,还可以让redis使用CRC64算法来进行数据校验,但是这样做会增大约10%的性能消耗,若想获取最大性能的提升可以关闭此功能
- **save:**保存DB到磁盘上。用来持久化。方式
-
APPEND ONLY MODE(AOF配置)
- **appendonly:**是否开启,默认关闭
- **appendfilename:**指定AOF的写入文件
- **appendfsync:**日志记录方式
- **always:**同步持久化,每次发生数据变更会被立即记录到磁盘,性能较差但数据完整性比较好
- **everysec:**出厂默认推荐,异步操作,每秒记录,如果一秒内宕机,会有数据丢失
- no
- **no-appendfsync-on-rewrite:**重写时是否可以御用appendfsync,用默认no即可,保证数据安全性
- **auto-aof-rewrite-percentage:**设置重写的基准值,默认100,表示前一次的1倍
- **auto-aof-rewrite-min-size:**设置重写的基准值,表示文件大小的最小值。默认64MB
redis持久化
RDB(Redis Database)
是什么
在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是行话将的snapshot(快照),它恢复时将快照文件直接读到内存里。Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程不进行任何io操作,这就确保了极高的性能,如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF更加高效。RDB的缺点是最后一次持久化后的数据可能丢失,保存的是dump.rdb文件。**优势:**适合大规模数据恢复且对数据的完整性和一致性要求不高。**劣势:**在一定间隔时间做一次备份,所以如果redis意外down掉的话,就会丢失最后一次快照后所有修改。fork时若内存中的数据被clone一份,会膨胀至两倍的内存空间
Fork
Fork的作用是复制一个与当前进程一样的进程。新进程的所有数据(变量、环境变量、程序计数器等)数值和原进程一直,但是是一个全新的进程,并作为原进程的子进程
如何触发RDB快照
- 配置文件的快照设置(即save配置)
- 通过命令save或者bgsave
- save时只管保存,其他不管,全部阻塞
- bgsave时Redis会在后台异步进行快照操作,快照同时还可以响应客户端请求。可以通过lastsave命令获取最后一次成功执行快照的时间
- 执行flushall命令,也会产生dump.rdb文件夹,但是里面是空无意义
如何恢复
将备份文件(dump.rdb)移动到redis安装目录并启动服务即可,可以通过config get dir获取目录
如何停止
动态停止RDB保存规则:config set save “”
AOF(Append Only File)
是什么
以日志的形式来记录每个写操作,将Redis执行过的所有写指令记录下来(读操作不记录),秩序追加文件但不可以改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis重启的话就根据日志文件的内容将写指令从前到后执行一次完成数据的恢复工作,AOF保存的是appendonly.aof文件
配置位置
安装目录下redis.conf中的Append Only Mode模块
AOF启动/修复/恢复
正常恢复
设置appendonly为yes,将有数据的aof文件复制一份保存到对饮目录,重启时redis后会优先加载aof文件。可以与dump.rdb同时存在,但优先级大于dump.rdb
异常恢复
备份被写坏的aof文件,使用redis-check-aof --fix修复aof文件,后重启redis服务。修复dump.rdb同理,修复命令为redis-check-dump --fix
Rewrite(重写)
是什么
由于AOF采用文件追加的方式,所以文件会越来愈大,为避免出现此种情况,新增了重写机制。当AOF文件的大小超过所设定的阙值时,redis就会启动AOF文件的内容压缩,只保留可以恢复数据的最小指令集,可以使用命令bgrewriteaof
重写原理
AOF文件持续增大至过大时,redis会fork一个新进程来讲文件重写(也是先写临时文件后再rename),遍历新进程的内存中数据,每条记录有一条set语句。重写AOF文件的操作,并没有读取旧的aof文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件
触发机制
redis会记录上一次重写时的aof文件大小,默认配置是当aof文件大小是上次rewrite后大小的一倍且文件大于64M时触发
Redis事务
是什么
本质是一组命令的集合,一个事务中的所有命令都会被序列化,按顺序的执行,在执行这一组命令时不会插入其他命令。
怎么玩
常用命令
- **multi:**标记一个事务块的开始
- **exec:**执行一个事务块内的所有命令
- **discard:**取消事务,放弃执行事务块内的所有命令
- **watch key [key…]:**监视一个或多个key,若在事务执行前key被其他命令改动,那么事务会被打断
- **unwatch:**取消watch命令对所有key的监视
示例
##正常执行
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> get k2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> EXEC
1) OK
2) OK
3) "v2"
4) OK
##放弃事务
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v3
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> DISCARD
OK
127.0.0.1:6379> get k2
"v2"
## 全体连坐(即一个失败则全部失败)
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v3
QUEUED
127.0.0.1:6379> set k3 v4
QUEUED
127.0.0.1:6379> getset v1 # 检查错误,会导致全部命令执行失败
(error) ERR wrong number of arguments for 'getset' command
127.0.0.1:6379> set k5 v5
QUEUED
127.0.0.1:6379> EXEC
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get k5
(nil)
## 冤头债主
127.0.0.1:6379> set a1 v1
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set a2 v2
QUEUED
127.0.0.1:6379> INCR a1 #运行时错误,不会导致其他命令执行失败
QUEUED
127.0.0.1:6379> set a3 v3
QUEUED
127.0.0.1:6379> get a3
QUEUED
127.0.0.1:6379> EXEC
1) OK
2) (error) ERR value is not an integer or out of range
3) OK
4) "v3"
watch监控
乐观锁
相对于悲观锁而存在的,即每次操作都不会上锁。但是在多人更新同一条数据是,会判断下在此期间是否有别人正在修改,可以使用版本号等机制
悲观锁
每次拿数据的时候都认为别人会修改,所以每次拿数据的时候都会上锁,这样别人想拿这个数据就会block直到他拿到锁。 传统的关系型数据库里边就用到很多这种锁机制,比如行锁、表锁等,都是在操作前先上锁
怎么用
watch指令,类似乐观锁,事务提交时,如果key的值被别的客户端改变,整个队列都不会被执行。通过watch命令在事务执行之前监控了多个key,倘若在watch之后有任何key的值发生了变化,exec命令执行的事务都将被放弃,同时返回Nullmulti-bulk应答以通知调用者事务执行失败
127.0.0.1:6379> set balance 100
OK
127.0.0.1:6379> set debt 0
OK
127.0.0.1:6379> WATCH balance # 开启监控
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> DECRBY balance 20
QUEUED
127.0.0.1:6379> INCRBY debt 20
QUEUED
127.0.0.1:6379> EXEC
1) (integer) 80
2) (integer) 20
127.0.0.1:6379> UNWATCH #取消监控所有key
Redis发布订阅
是什么
进程间的一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接受消息
命令
- **psubscribe pattern[…pattern]:**订阅一个或多个符合给定模式的频道
- **pubsub subcommand […arg]:**查看订阅与发布系统状态
- **publish channel message:**将消息发送给指定频道
- **punsubscribe […pattern]:**退订所有给定模式的频道
- **subscribe channel[…channel]:**订阅一个或多个指定的频道
- **unsubscribe […channel]:**退订指定的频道
案列
#client1
127.0.0.1:6379> SUBSCRIBE c1 # 订阅c1频道
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "c1"
3) (integer) 1
#client2 在c1平道发送消息'helloRedis'
127.0.0.1:6379> PUBLISH c1 helloredis
(integer) 1
#client1
127.0.0.1:6379> SUBSCRIBE c1
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "c1"
3) (integer) 1
1) "message"
2) "c1"
3) "helloredis" ##读取c1频道的消息
Redis主从复制
是什么
主机数据更新后根据配置和策略,自动同步到备机的master/slaver机制,Master以写为主,Slave以读为主
能干嘛
- 读写分离
- 容灾恢复
怎么玩
配置方式
-
配从库不配主库
可以使用info replication命令查看当前主机状态信息
-
从库配置:slaveof主库ip 主库端口
每次与master断开后,都需要重新连接,除非配置进redis.conf文件
-
修改配置文件细节操作
- 拷贝多个redis.conf文件(同一台服务器上开启多个redis服务时,多台服务器可省略此操作)
- 开启daemonize yes
- 设置pid文件名字
- 指定端口
- 指定log文件名字
- 设置dump.rdb名字
- 启动redis服务后使用slaveof ip地址 端口号开启复制
常用方式
-
一主多仆
-
薪火相传
-
反客为主
**slaveof no one命令:**使当前数据库停止与其他数据库的同步,转成主数据库
复制原理
- Slave启动成功连接到master后悔发送一个sync命令
- Master接到命令启动后台的存盘进程,同时收集所有接收到的用于修改数据集的命令,在后台进程执行完毕后,master将传送整个数据文件到slave,以完成一次完全同步
- 全量复制:而slave服务在接受到数据库文件数据后,将其存盘并加载到内存中
- 增量复制:Master继续讲新的所有收集到的修改命令一次管给slave,完成同步
- 但只要是重新连接master,一次完全同步(全量复制)将被执行
哨兵模式(反客为主的自动版)
- 在服务器上新建sentinel.conf文件,名字绝不能错
- 配置哨兵,填写内容,
- sentinel monitor 被监控数据库名字(自己起的名字) Master的IP地址 Master的端口号 ticket_num(slave得票值为ticket_num后成为主机)
- 启动哨兵
- redis-sentinel sentinel.conf文件目录+/sentinel.conf
- redis-sentinel命令在redis安装目录src下
一组sentinel能同时监控多个master
复制的缺点
由于所有的写操作都是现在master上操作,然后同步更新到slave上,所以从Master同步到slave机器有一定的延迟,当系统很繁忙的时候,延迟问题会更加严重,slave机器数量的增加也会使这个问题更加严重