NoSQL读写速度很快,会更适合高并发,redis是一个开源的高性能(nosql)的key-value型数据库。
redis应用场景:
企业级开发中:可以用作数据库、缓存、热点数据(经常会被查询,但是不经常被修改或者删除的数据)和消息中间件等大部分功能。
Redis最适合所有数据in-momory的场景,虽然Redis也提供持久化功能,但实际更多的是一个disk-backed的功能,跟传统意义上的持久化有比较大的差别,那么可能大家就会有疑问,似乎Redis更像一个加强版的Memcached,那么何时使用Memcached,何时使用Redis呢?
如果简单地比较Redis与Memcached的区别,大多数都会得到以下观点:
1 、Redis不仅仅支持简单的k/v类型的数据,同时还提供list,set,zset,hash等数据结构的存储。
2 、Redis支持数据的备份,即master-slave模式的数据备份。
3 、Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用。
(1)会话缓存(Session Cache)
最常用的一种使用Redis的情景是会话缓存(session cache)。用Redis缓存会话比其他存储(如Memcached)的优势在于:Redis提供持久化。当维护一个不是严格要求一致性的缓存时,如果用户的购物车信息全部丢失,大部分人都会不高兴的,现在,他们还会这样吗?
幸运的是,随着 Redis 这些年的改进,很容易找到怎么恰当的使用Redis来缓存会话的文档。甚至广为人知的商业平台Magento也提供Redis的插件。
(2)全页缓存(FPC)
除基本的会话token之外,Redis还提供很简便的FPC平台。回到一致性问题,即使重启了Redis实例,因为有磁盘的持久化,用户也不会看到页面加载速度的下降,这是一个极大改进,类似PHP本地FPC。
再次以Magento为例,Magento提供一个插件来使用Redis作为全页缓存后端。
此外,对WordPress的用户来说,Pantheon有一个非常好的插件 wp-redis,这个插件能帮助你以最快速度加载你曾浏览过的页面。
(3)队列
Reids在内存存储引擎领域的一大优点是提供 list 和 set 操作,这使得Redis能作为一个很好的消息队列平台来使用。Redis作为队列使用的操作,就类似于本地程序语言(如Python)对 list 的 push/pop 操作。
(4)排行榜/计数器
Redis在内存中对数字进行递增或递减的操作实现的非常好。集合(Set)和有序集合(Sorted Set)也使得我们在执行这些操作的时候变的非常简单,Redis只是正好提供了这两种数据结构。所以,我们要从排序集合中获取到排名最靠前的10个用户–我们称之为“user_scores”,我们只需要像下面一样执行即可:
当然,这是假定你是根据你用户的分数做递增的排序。如果你想返回用户及用户的分数,你需要这样执行:
ZRANGE user_scores 0 10 WITHSCORES
Agora Games就是一个很好的例子,用Ruby实现的,它的排行榜就是使用Redis来存储数据的。
·redis优势:
性能极高-redis能读的速度是110000次/s,写的次数是81000次/s。
丰富的数据类型-redis支持二进制案例的Strings,Lists,Hashes,Sets及Ordered Sets数据类型的操作。
原子-redis的所有操作都是原子性的,要么成功执行要么失败完全不执行。单个操作是原子性的,多个操作也支持事务,即原子性,通过MULTI和EXEC指令包起来。
丰富的特性-Redis还支持publish/subscribe,通知,key过期等特性。
·redis缺点:
由于 Redis 是内存数据库,短时间内大量增加数据,可能导致内存不够用。
redis是单线程的,单台服务器无法充分利用多核服务器的CPU。
redis关闭
常用命令key
Key的命名建议
redis单个key 存入512M大小
key不要太长,尽量不要超过1024字节,这不仅消耗内存,而且会降低查找的效率;
key也不要太短,太短的话,key的可读性会降低;
在一个项目中,key最好使用统一的命名模式,例如user:123:password; 避免数据库表名和key的难以区分:例表名为user_name,如果以user_123_password命名 则无法区分哪部分
key名称区分大小写
redis键(key)
redis键命令用于管理redis的键
语法
redis 127.0.0.1:6379>COMMAND KEY_NAME
实例
redis 127.0.0.1:6379>SET runoobkey redis
OK
redis 127.0.0.1:6379>DEL runoobkey
(integer) 1
DEL是一个命令,runoobkey是一个键,如果键被删除成功,命令执行后输出(integer) 1,否则将输出(integer) 0.
redis keys命令
命令 描述
- DEL key 该命令用于在key存在时删除key DUMP key 序列化给定key,并返回被序列化的值
- EXISTS key 检查给定key是否存在 EXPIRE key seconds 为给定的可以设置过期时间,以秒计
- EXPIREAT key timestamp EXPIREAT的作用和EXPIRE类似,都是用于为key设置过去时间,不同在于EXPIREAT命令接受的时间参数是UNIX时间戳(unix timestamp)
- PEXPIRE key millseconds 设置key的过期时间以毫秒计
- PEXPIREAT key millseconds-timestamp 设置key过期时间的时间戳(unix timestamp)以毫秒计
- KEYS pattern 查找所有符合给定模式(pattern)的key. MOVE key db 将当前数据库的key移动到给定的数据库db中
- PERSIST key 移除key的过期时间,key将持久保持 PTTL key 以毫秒单位返回key的剩余的过期时间。
- TTL key 以秒为单位,返回给定key的剩余生存时间(TTL,time to live)
- RANDOMKEY 从当前数据库中随机返回一个key.
- RENAME key newkey 修改key的名称
- RENAMENX key newkey 仅当newkey不存在时,将key改名为newkey。
- TYPE key 返回key所存储的值的类型
redis字符串(string)
redis字符串数据类型的相关命令用于管理redis字符串值,基本语法如下:
redis 127.0.0.1:6379>COMMAND KEY_NAME
实例
redis 127.0.0.1:6379>SET runoobkey redis
OK
redis 127.0.0.1:6379>GET runoobkey
"redis"
redis 字符串命令
命令 描述
SET key value 设置指定key的值
GET key 获取指定key的值
GETRANGE key value 返回key中字符串值得子字符
GETSET key value 将给定key的值设为value,并返回key的旧值(old value)
GETBIT key offset 对可以所存储的字符串值,获取指定偏移量上的位(bit)
MGET key1[key2…] 获取所有(一个或多个)给定key的值
SETBIT key offset value 对key所储存的字符串值,设置或清除指定偏移量上的位(bit)
SETEX key seconds value 将值value关联到key,并将key的过期时间设置为seconds(以秒为单位)
SETNX key value 只有在key不存在时设置key的值
SETRANGE key offset value 用value参数覆写给定key所储存的字符串值,从偏移量offset开始
STRLEN key 返回key所储存的字符串值的长度
MSET key value[key value…] 同时设置一个或多个key-value对
MSETNX key value[key value…] 同时设置一个或多个key-value对,当且仅当所有给定key都不存在
PSETEX key millseconds value 这个命令和SETEX命令相似,但它以毫秒为单位设置key的生存时间,而不是像SETEX命令那样,以秒为单位。
INCR key 将key中储存的数字值增1
INCRBY key increment 将key所储存的值加上给定的增量值(increment)
INCRBYFLOAT key increment 将key所储存的值加上给定的浮点增量值(increment)
DECR key 将key中储存的数字值减一。
DECRBY key decrement key所储存的值减去给定的减量值(decrement)
APPEND key value 如果key已经存在并且是一个字符串,APPEND命令将指定的value追加到该key原来值(value)的末尾。
应用场景:
1、String通常用于保存单个字符串或JSON字符串数据
2、因String是二进制安全的,所以你完全可以把一个图片文件的内容作为字符串来存储
3、计数器(常规key-value缓存应用。常规计数: 微博数, 粉丝数)
INCR等指令本身就具有原子操作的特性,所以我们完全可以利用redis的INCR、INCRBY、DECR、DECRBY等指令来实现原子计数的效果。假如,在某种场景下有3个客户端同时读取了mynum的值(值为2),然后对其同时进行了加1的操作,那么,最后mynum的值一定是5。
不少网站都利用redis的这个特性来实现业务上的统计计数需求。
redis哈希(hash)
redis hash是一个string类型的field和value的映射表,hash特别适合用于储存对象。每个hash可以存储2^32 -1个键值对。
实例
127.0.0.1:6379>HMSET runoobkey name "redis tutorial" description "redis basic commands for caching" likes 20 visitors 23000
OK
127.0.0.1:6379>HGETALL runoobkey
1)"name"
2)"redis tutorial"
3)"description"
4)"redis basic commands for caching"
5)"likes"
6)"20"
7)"visitors"
8)"23000"
redis hash命令
命令 描述
HDEL key field1 [field2] 删除一个或多个哈希表字段
HEXISTS key field 查看哈希表key中,指定的字段是否存在
HGET key field 获取储存在哈希表中指定字段的值
HGETALL key 获取在哈希表中指定key的所有字段和值
HINCRBY key field increment 为哈希表key中的指定字段的整数值加上增量increment
HINCRBYFLOAT key field increment 为哈希表key中的指定字段的浮点数值加上增量increment
HKEYS key 获取所有哈希表中的字段
HLEN key 获取哈希表中字段的数量
HMGET key field1[field2] 获取所有给定字段的值
HMSET key field1 value1[field2 value2] 同时将多个field-value(域-值)对设置到哈希表key中
HSET key field value 将哈希表key中的字段field的值设置为value
HSETNX key field value 只有在字段field不存在时,设置哈希表字段的值
HVALS key 获取哈希表中所有值
HSCAN key cursor [MATCH patther] [COUNT count] 迭代哈希表中的键值对
Hash应用场景
Hash的应用场景:(存储一个用户信息对象数据)
常用于存储一个对象
为什么不用string存储一个对象?
hash是最接近关系数据库结构的数据类型,可以将数据库一条记录或程序中一个对象转换成hashmap存放在redis中。
用户ID为查找的key,存储的value用户对象包含姓名,年龄,生日等信息,如果用普通的key/value结构来存储,主要有以下2种存储方式:
第一种方式将用户ID作为查找key,把其他信息封装成一个对象以序列化的方式存储,这种方式的缺点是,增加了序列化/反序列化的开销,并且在需要修改其中一项信息时,需要把整个对象取回,并且修改操作需要对并发进行保护,引入CAS等复杂问题。
第二种方法是这个用户信息对象有多少成员就存成多少个key-value对儿,用用户ID+对应属性的名称作为唯一标识来取得对应属性的值,虽然省去了序列化开销和并发问题,但是用户ID为重复存储,如果存在大量这样的数据,内存浪费还是非常可观的。
总结:
Redis提供的Hash很好的解决了这个问题,Redis的Hash实际是内部存储的Value为一个HashMap,并提供了直接存取这个Map成员的接口
redis列表(list)
redis列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)一个列表最多可以包含2^32 -1个元素。
实例
redis 127.0.0.1:6379>LPUSH runoobkey redis
(integer) 1
redis 127.0.0.1:6379>LPUSH runoobkey mongodb
(integer) 1
redis 127.0.0.1:6379>LPUSH runoobkey mysql
(integer) 1
redis 127.0.0.1:6379>LRANGE runoobkey 0 10
1)"mysql"
2)"mongodb"
3)"redis"
redis列表命令
命令 描述
BLPOP key1 [key2] timeout 移出并获取列表的第一个元素,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
BRPOP key1 [key2] timeout 移出并获取列表的最后一个元素,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
BRPOPLPUSH source destination timeout 从列表中弹出一个值,将弹出的元素插入到另外一个列表中并返回它;如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
LINDEX key index 通过索引获取列表中的元素
LINSRT key BEFORE|AFTER pivot value 在列表的元素前或者后插入元素
LLEN key 获取列表长度
LPOP key 移出并获取列表的第一个元素
LPUSH key value1 [value2] 将一个或多个值插入到列表头部
LPUSHX key value 将一个值插入到已存在的列表头部
LRANGE key start stop 获取列表指定范围内的元素
LREM key count value 移出列表元素
LSET key index value 通过索引设置列表元素的值
LTRIM key start stop 对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。
RPOP key 移出列表的最后一个元素,返回值为移出的元素
RPOPLPUSH source destination 移出列表的最后一个元素,并将该元素添加到另外一个列表并返回
RPUSH key value1 [value2] 在列表中添加一个或多个值
RPUSHX key value 为已存在的列表添加值
redis集合(set)
redis的set是string类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。redis中集合是通过哈希表实现的,所以添加、删除、查找的复杂度都是O(1)。集合中最大的成员数为2^32 -1。
实例
redis 127.0.0.1:6379>SADD runoobkey redis
(integer) 1
redis 127.0.0.1:6379>SMEMBERS runoobkey
1)"redis"
redis集合命令
命令 描述
SADD key member1 [member2] 向集合添加一个或多个成员
SCARD key 获取集合的成员数
SDIFF key1 [key2] 返回给定所有集合的差集
SDIFFSTORE destination key1 [key2] 返回给定所有集合的差集并存储在destination中
SINTER key1 [key2] 返回给定所有集合的交集
SINTERSTORE destination key1 [key2] 返回给定所有集合的交集并存储在destination中
SISMEMBER key member 判断member元素是否是集合key的成员
SMEMBERS key 返回集合中的所有成员
SMOVE source destination member 将member元素从source集合移动到destination集合
SPOP key 移除并返回集合中的一个随机元素
SRANDMEMBER key [cout] 返回集合中一个或多个随机数
SREM key member1 [member2] 移除集合中一个或多个成员
SUNION key1 [key2] 返回所有给定集合的并集
SUNIONSTORE destination key1 [key2] 所有给定集合的并集存储在destination集合中
SSCAN key cursor [MATCH pattern] [COUNT count] 迭代集合中的元素
redis有序集合(sorted set,ZSet)
redis有序集合和集合一样也是string类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个double类型的分数。redis在正式通过分数来为集合中的成员进行从小到大的排序。有序集合的成员是唯一的,但分数(score)却可以重复。集合是通过哈希表实现的,所以添加、删除、查找的复杂度都是O(1)。集合中最大的成员数是2^32 -1.
实例
redis 127.0.0.1:6379> ZADD runoobkey 1 redis
(integer) 1
redis 127.0.0.1:6379> ZADD runoobkey 2 mongodb
(integer) 1
redis 127.0.0.1:6379> ZADD runoobkey 3 mysql
(integer) 1
redis 127.0.0.1:6379> ZADD runoobkey 3 mysql
(integer) 0
redis 127.0.0.1:6379> ZADD runoobkey 4 mysql
(integer) 0
redis 127.0.0.1:6379> ZRANGE runoobkey 0 10 WITHSCORES
1) "redis"
2) "1"
3) "mongodb"
4) "2"
5) "mysql"
6) "4"
redis有序集合命令
命令 描述
ZADD key score1 member1 [score2 member2] 向有序集合添加一个或多个成员,或者更新已存在成员的分数
ZCARD key 获取有序集合的成员数
ZCOUNT key min max 计算在有序集合中指定区间分数的成员数
ZINCRBY key increment member 有序集合中对指定成员的分数加上增量increment
ZINTERSTORE destination numkeys key [key…] 计算给定的一个或多个有序集合的交集并将结果存储在新的有序集合key中
ZLEXCOUNT key min max 在有序集合中计算指定字典区间内成员数量
ZRANGE key start stop [WITHSCORES] 通过索引区间返回有序集合成指定区间内额成员
ZRANGEBYLEX key min max [LIMIT offset count] 通过字典区间返回有序集合的成员
ZRANGEBYSCORE key min max [WITHSCORES]…[LIMIT] 通过分数返回有序集合指定区间内的成员
ZRANK key member 返回有序集合中指定成员的索引
ZREM key memer [member…] 移除有序集合中的一个或多个成员
ZREMRANGEBYLEX key min max 移除有序集合中给定的字典区间的所有成员
ZREMRANGEBYRANK key start stop 移除有序集合中给定的排名区间的所有成员
ZREMRANGEBYSCORE key min max 移除有序集合中给定的分数区间的所有成员
ZREVRANGE key start stop [WITHSCORES] 返回有序集合中指定分数区间内的成员,通过索引,分数从高到低
ZREVRANGEBYSCORE key max min [WITHSCORES] 返回有序集合中指定分数区间内的成员,分数从高到低排序
ZREVRANK key member 返回有序集合中指定成员的排名,有序集合成员按分数值递减(从大到小)排序
ZSCORE key member 返回有序集合中,成员的分数值
ZUNIONSTORE destination numkeys key [key…] 计算给定的一个多个有序集合的并集,并存储在新的key中
ZSCAN key cursor [MATCH pattern] [COUNT count] 迭代有序集合中的元素(包括元素成员和元素分值)
redis HyperLogLog
在2.8.9版本中添加了HyperLogLog结构。HyperLogLog是用来做基数统计的算法,优点是在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定的、并且是很小的。在redis里面,每个HyperLogLog键只需要花费12kb内存,就可以计算奖金2^64个不同元素的基数。这和计算基数时,元素越多耗费内存就越多的集合形成鲜明的对比。
但是,因为HyperLogLog只会根据输入元素来计算基数,而不会储存输入元素本身,所以HyperLogLog不能像集合那样,返回输入的各个元素。
什么是基数?
比如数据集{1,3,5,7,5,7,8},那么这个数据集的基数集为{1,3,5,7,8},基数(不重复元素)为5。基数估计就是在误差可接受的范围内,快速计算基数。
实例
redis 127.0.0.1:6379> PFADD runoobkey "redis"
1) (integer) 1
redis 127.0.0.1:6379> PFADD runoobkey "mongodb"
1) (integer) 1
redis 127.0.0.1:6379> PFADD runoobkey "mysql"
1) (integer) 1
redis 127.0.0.1:6379> PFCOUNT runoobkey
(integer) 3
redis HyperLogLog 命令
命令 描述
PFADD key element [element…] 添加指定元素到HyperLogLog中
PFCOUNT key [key…] 返回给定HyperLogLog的基数估算值
PFMERGE destkey sourcekey [sourcekey…] 将多个HyperLogLog合并为一个HyperLogLog
redis 发布订阅
redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接受消息。redis客户端可以订阅任意数量的频道。
实例
redis 127.0.0.1:6379>SUBSCRIBE redisChat
Reading messages...(press Ctrl-C to quit)
1)"subscribe"
2)"redisChat"
3)(integer) 1
重新开启一个redis客户端,然后在同一个频道redisChat发布两次消息,订阅者就能接受到消息
redis 127.0.0.1:6379> PUBLISH redisChat "Redis is a great caching technique"
(integer) 1
redis 127.0.0.1:6379> PUBLISH redisChat "Learn redis by runoob.com"
(integer) 1
订阅者的客户端会显示如下消息
- “message”
- “redisChat”
- “Redis is a great caching technique”
- “message”
- “redisChat”
- “Learn redis by runoob.com”
redis发布订阅命令
命令 描述
PSUBSCRIBE pattern [pattern…] 订阅一个或多个符合给定模式的频道
PUBSUB subcommand [argument [argument…]] 查看订阅与发布系统状态
PUBLISH channel message 将信息发送到指定的频道
PUNSUBSCRIBE [pattern [pattern …]] 退订所有给定模式的频道
SUBSCRIBE channel [channel …] 订阅给定的一个或多个频道的信息
UNSUBSCRIBE [channel [channel…]] 指退订给定的频道
redis事务
一个事务从开始到执行会经历以下三个阶段:
开始事务
命令入队
执行事务
实例
以下是一个事务的例子,先以MULTI开始一个事务,然后将多个命令入队到事务中,最后由EXEC命令触发事务,一并执行事务中的所有命令。
redis 127.0.0.1:6379> MULTI
OK
redis 127.0.0.1:6379> SET book-name "Mastering C++ in 21 days"
QUEUED
redis 127.0.0.1:6379> GET book-name
QUEUED
redis 127.0.0.1:6379> SADD tag "C++" "Programming" "Mastering Series"
QUEUED
redis 127.0.0.1:6379> SMEMBERS tag
QUEUED
redis 127.0.0.1:6379> EXEC
1) OK
2) "Mastering C++ in 21 days"
3) (integer) 3
4) 1) "Mastering Series"
2) "C++"
3) "Programming"
单个redis命令的执行时原子性的,但redis没有在事务上增加任何维持原子性的机制,所以redis事务的执行并不是原子性的。事务可以理解为一个打包的批量执行脚本,但批量指令并非原子化的操作,如果是语法问题出现错误(例如给字符串做+),中间某条指令的失败不会导致前面一座指令的回滚,也不会造成后续的指令不执行;如果是队列中的某个命令出现报告错误,执行时整个所有队列都会被取消。
redis 127.0.0.1:7000> multi
OK
redis 127.0.0.1:7000> set a aaa
QUEUED
redis 127.0.0.1:7000> set b bbb
QUEUED
redis 127.0.0.1:7000> set c ccc
QUEUED
redis 127.0.0.1:7000> exec
1) OK
2) OK
3) OK
redis事务命令
命令 描述
MULTI
标记一个事务块的开始
DISCARD
取消事务,放弃执行事务块内的所有命令
EXEC
执行所有事务块内的命令
WATCH key[key...]
监视一个(或多个)key,如果在事务执行之前这个(或这些)key被其他命令所改动,那么事务将被打断
UNWATCH
取消WATCH命令对所有key的监视
redis脚本
redis脚本使用lua解释器来执行脚本,redis2.6版本通过内嵌支持lua环境。执行脚本的常用命令为EVAL
语法
redis 127.0.0.1:6379>EVAL script numkeys key [key ...] arg[arg ...]
实例
redis 127.0.0.1:6379> EVAL "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first second
1) "key1"
2) "key2"
3) "first"
4) "second"
redis脚本命令
命令 描述
EVAL script numkeys key [key …] arg [arg …] 执行Lua脚本
EVALSHA sha1 numkeys key [key …] arg [arg …] 执行Lua脚本
SCRIPT EXISTS script [script …] 查看指定的脚本是否已经被保存在缓存当中
SCRIPT FLUSH 从脚本缓存中移除所有脚本
SCRIPT KILL 杀死当前这在运行的Lua脚本
SCRIPT LOAD script 将脚本script添加到脚本缓存中,但并不立即执行这个脚本
redis连接
redis连接命令主要是用于连接redis服务。
实例
redis 127.0.0.1:6379> AUTH "password"
OK
redis 127.0.0.1:6379> PING
PONG
redis连接命令
命令 描述
AUTH password 验证密码是否正确
ECHO message 打印字符串
PING 查看服务是否运行
QUIT 关闭当前连接
SELECT index 切换到指定的数据库
redis服务器
redis服务器命令主要是用于管理redis服务
redis服务命令
命令 描述
BGREWRITEAOF 异步执行一个AOF(AppendOnly File)文件重写操作
BGSAVE 在后台异步保存当前数据库的数据到磁盘
CLIENT KILL [ip:port] [ID client-id] 关闭客户端连接
CLIENT LIST 获取连接到服务器的客户端连接列表
CLIENT GETNAME 获取连接的名称
CLIENT PAUSE timeout 在指定时间内终止运行来自客户端的命令
CLIENT SETNAME connection-name 设置当前连接的名称
CLUSTER SLOTS 获取集群节点的映射数组
COMMAND 获取redis命令详情数组
COMMAND COUNT 获取redis命令总数
COMMAND GETKEYS 获取给定命令的所有键
TIME 返回当前服务器时间
COMMAND INFO command-name [command-name …] 获取指定redis命令描述的数组
CONFIG GET parameter 获取指定配置参数的值
CONFIG REWRITE 对启动redis服务区时所指定的redis.conf配置文件进行改写
CONFIG SET parameter value 修改redis配置参数,无需重启
CONFIG RESETSTAT 重置INFO命令中的某些统计数据
DBSIZE 返回当前数据库的key的数量
DEBUG OBJECT key 获取key的调试信息
DEBUG SEGFAULT 让redis服务崩溃
FLUSHALL 删除所有数据库的key
FLUSHDB 删除当前数据库的所有key
INFO [section] 获取redis服务器的各种信息和统计数值
LASTSAVE 返回最近一次redis成功将数据保存到磁盘上的时间,以UNIX时间戳格式表示
MONITOR 实时打印出redis服务器接收到的命令,调试用
ROLE 返回主从实例所属的角色
SAVE 同步保存数据到硬盘
SHUNTDOWN [NOSAVE] [SAVE] 异步保存数据到硬盘,并关闭服务器
SLAVEOF host port 将当前服务器转变为指定服务器的从属服务器(slave server)
SLOWLOG subcommand [argument] 管理redis的慢日志
SYNC 用于复制功能(replication)的内部命令
redis高级教程
redis数据备份与恢复
redis是支持持久化的内存数据库,也就是说redis需要经常将内存中的数据同步到硬盘来保证持久化。持久化方式:RDB和AOF
RDB是redis database的缩写,功能核心函数rdbsave(生成rdb文件)和rdbload(从文件中加载到内存)两个函数
redis SAVE 命令用于创建当前数据库的备份
语法
redis 127.0.0.1:6379> SAVE
恢复数据
如果需要恢复数据,只需将备份文件(dump.rdb)移动到redis安装目录并启动服务即可。获取redis目录可以使用CONFIG命令
redis 127.0.0.1:6379> CONFIG GET dir
1) "dir"
2) "/usr/local/redis/bin"
以上命令CONFIG GET dir输出的redis安装目录为/usr/local/redis/bin
Bgsave
创建redis备份文件也可以使用命令BGSAVE,该命令在后台执行。
实例
127.0.0.1:6379> BGSAVE
Background saving started
redis安全
通过redis的配置文件设置密码参数,客户端连接到redis服务就需要密码验证,这样就可以让redis服务更安全。
实例
127.0.0.1:6379> CONFIG get requirepass
1)"requirepass"
2)""
默认情况下requirepass参数是空的,意味着无需通过密码验证就可以连接到redis服务。
127.0.0.1:6379> CONFIG set requirepass "runoob"
OK
127.0.0.1:6379> CONFIG get requirepass
1)"requirepass"
2)"runoob"
设置密码后,客户端连接redis服务就需要密码验证,否则无法执行命令。
语法
127.0.0.1:6379> AUTH password
实例
127.0.0.1:6379> AUTH "runoob"
OK
127.0.0.1:6379> SET mykey "test value"
OK
127.0.0.1:6379> GET mykey
"test value"
redis性能测试
redis性能测试时通过同时执行多个命令实现的
语法
redis-benchmark [option] [option value]
注意:该命令是在redis的目录下执行的,而不是redis客户端的内部指令
实例
以下实例同时执行10000个请求来检测性能:
$ redis-benchmark -n 10000 -q
redis性能检测工具可选参数
选项 描述 默认值
-h 指定服务器主机名 127.0.0.1
-p 指定服务器端口 6379
-s 指定服务器socket
-c 指定并发连接数 50
-n 指定请求数 10000
-d 以字节的形式指定SET/GET值得数据大小 2
-k 1=keep alive 0=reconnection 1
-r SET/GET/INCR 使用随机 key,SADD使用随机值
-p 通过管道传输请求 1
-q 强制退出redis,仅显示query/sec值
-csv 以csv格式输出
-l 生成循环,永久执行测试
-t 仅运行以逗号跟个的测试命令列表
-I Idle模式,仅打开了N个idle连接并等待
实例
$ redis-benchmark -h 127.0.0.1 -p 6379 -t set,lpush -n 10000 -q
SET: 146198.83 requests per second
LPUSH: 145560.41 requests per second
实例中主机为127.0.0.1,端口为6379,执行的命令为set,lpush,请求数为10000,通过-q参数让结果只显示每秒执行的请求数。
redis客户端连接
redis通过监听一个TCP端口或者Unix socket的方式来接受来自客户端的连接,当一个连接建立后,redis内部会进行一下一些操作:
首先,客户端socket会被设置为非阻塞式,因为redis在网络事件处理上采用的是非阻塞多路复用模型
然后为这个socket设置TCP_NODELAY属性,禁用Nagle算法
然后创建一个可读的文件事件用于监听这个客户端socket的数据发送
最大连接数
在redis 2.4中,最大连接数是被直接硬编码在代码里面的,而在2.6版本中这个值变成可配置的。maxclients的默认值是10000,也可以在redis.conf中对这个值进行修改。
config get maxclients
1)"maxclients"
2)"10000"
实例
redis-server --maxclients 100000
客户端命令
命令 描述
CLIENT LIST 返回连接到redis服务的客户端列表
CLIENT SETNAME 设置当前连接的名称
CLIENT GETNAME 获取通过CLIENT SETNAME命令设置的服务名称
CLIENT PAUSE 挂起客户端连接,指定挂起的时间以毫秒计
CLIENT KILL 关闭客户端连接
redis管道技术
redis是一种基于客户端-服务端模型以及请求/相应协议的TCP服务,意味着通常情况下一个请求会遵循以下步骤:
客户端像服务端发送一个查询请求,并监听socket返回,通常是以阻塞模式,等待服务端响应
服务端处理命令,并将结果返回给客户端
redis管道技术可以在服务端未响应时,客户端可以继续向服务端发送请求,并最终一次性读取所有服务端的响应。
实例
查看redis管道,只需要启动redis实例并输入以下命令:
$(echo -en "PING\r\n SET runoobkey redis\r\nGET runoobkey\r\nINCR visitor\r\nINCR visitor\r\nINCR visitor\r\n"; sleep 10) | nc localhost 6379
+PONG
+OK
redis
:1
:2
:3
实例中通过使用PING命令查看redis服务是否可用,之后我们设置了runoobkey的值为redis,然后获取runoobkey的值并使得visitor自增3次。
在返回的结果中可以看到这些命令一次性向redis服务提交,并最终一次性读取所有服务端的响应
管道技术的优势
管道技术最显著的优势是提高了redis服务的性能
一些测试数据,使用redis的ruby客户端,支持管道技术特性,测试管道技术对速度的提升效果
require 'rubygems'
require 'redis'
def bench(descr)
start = Time.now
yield
puts "#{descr} #{Time.now-start} seconds"
end
def without_pipelining
r = Redis.new
10000.times {
r.ping
}
end
def with_pipelining
r = Redis.new
r.pipelined {
10000.times {
r.ping
}
}
end
bench("without pipelining") {
without_pipelining
}
bench("with pipelining") {
with_pipelining
}
从处于局域网中的mac OS X系统上执行这个简单的数据表明,开启了管道操作后,往返延时已经被改善的相当低了
reids分区
分区是分割数据到多个redis实例的处理过程,因此每个实例只保存key的一个子集
分区的优势:
通过利用多台计算机的内存和值,允许我们构造更大的数据库
通过多核和多台计算机,允许我们扩展计算能力;通过多台计算机和网络适配器,允许我们扩展网络带宽
分区的不足
涉及多个key的操作通常是不被支持的(当两个set映射到不同的redis实例上时,就不能对这个两个set执行交集操作)
涉及多个key的redis事务不能使用
当使用分区是,数据处理比较复杂,比如需要处理多个rdb/aof文件,并且从多个实例和主机备份持久化文件
增加或删除容量也比较复杂,redis集群大多数支持在运行时增加、删除节点的透明数据平衡的能力,但是类似于客户端的分区、代理等其他系统则不支持这项特性。然而,一种叫做presharding的技术对此是有帮助的。
分区类型
redis有两种类型分区,假设有4个redis实例R0,R1,R2,R3和类似user:1 , user:2 这样的表示用户的多个key,对既定的key有多种不同的方式来选择这个key存放在哪个实例中,也就是说有不同的系统来映射某个key到某个redis服务
范围分区
最简单的分区方式是按范围分区,就是映射一定范围的对象到特定的redis实例。比如,ID从0到10000的用户会保存到实例R0,ID从10001到20000的用户会保存到R1,以此类推。这种方式是可行的,并且在实际中使用,不足就是要有一个区间范围到实例的映射表,这个表要被管理,同时还需要各种对象的映射表,通常对redis来说并非是最好的方法。
哈希分区
另外一种分区方法是hash分区,这对任何key都适用,也无需是object_name:这种形式,像下面描述的一样简单:
用一个hash函数将key转化为一个数字,比如适用crc32 hash函数,对key foobar执行crc32(foobar)会输出蕾西93024922的整数
对这个整数取模,将其转化为0-3之间的数字,就可以将这个整数映射到4个redis实例中的一个了。9302422%4=2,就是说key foobar应该被存放到R2实例中。注意:取模操作是取除的余数,通常在多种编程语言中用%操作符实现。
缓存与数据库一致性
流量削峰:分流
数据同步除了实时同步和异步队列之外还有:1.阿里同步工具canal和2.采用udf自定义函数的方式
穿透和雪崩
穿透发生的情况:查询的数据遭到篡改或者请求数据错误:例如:网站查询时ur进行部分修改 此时缓存中就没有此数据,解决方案进行加密然后解密去查询,此时不是正确的url根本无法正确解密。
手动解决方案:
使用Redis的各种方式
在官方网站列一些Java客户端访问,有:Jedis/Redisson/Jredis/JDBC-Redis等,其中官方推荐使用Jedis和Redisson。常用Jedis。
开始在 Java 中使用 Redis 前, 我们需要确保已经安装了 redis 服务及 Java redis 驱动,且你的机器上能正常使用 Java。 Java的安装配置可以参考我们的 Java开发环境配置 接下来让我们安装 Java redis 驱动
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.4.2</version>
</dependency>
yml文件中配置为:
spring:
redis:
port:6397
password:12345
host:192.168.20.135 //主机ip
jedis:
pool:
max-idle:6 #最大空闲数
max-active:10 #最大连接数
min-idle:2 #最小空闲数
timeout:2000 #连接超时
测试是否连接成功
public class Redis {
public static void main(String[] args) {
String host = "127.0.0.1";
Jedis jedis = new Jedis(host);
System.out.println(jedis.ping());
}
}
开放端口(如下命令只针对Centos7以上)
查看已经开放的端口:firewall-cmd --list-ports
开启端口:
firewall-cmd --zone=public --add-port=6379/tcp --permanent
重启防火墙
firewall-cmd --reload
#重启
Java操作Redis 设置密码
这个问题是由于Redis没有配置密码的原因导致的,只需要为redis设置密码即可
config get requirepass:
这是查询redis是否配置密码,如果返回为空,则表明未配置密码
config set requirepass “guoweixin”
这是将redis的密码设置为“guoweixin”
客户端登录:用redis-cli 密码登陆(redis-cli -a password)
4.1 通过Jedis连接池
4.1.1 普通连接使用
public static void main(String[] args) {
String host = "127.0.0.1";
Jedis jedis = new Jedis(host);
System.out.println(jedis.ping());
}
4.1.2 通过连接池获得redis
public class JedisUtils {
private static JedisPool jedisPool;
private static String host = "localhost";
static{
//设置连接池的参数
JedisPoolConfig config = new JedisPoolConfig();
//设置空闲连接数
config.setMaxTotal(5);
//设置最大连接数
config.setMaxIdle(100);
//设置连接池
//。。。还有很多设置
jedisPool = new JedisPool(config, host);
}
public static Jedis getJedis(){
Jedis jedis = jedisPool.getResource();
return jedis;
}
public static void close(Jedis jedis){
jedis.close();
}
}
@Test
public void test_02(){
Jedis jedis = JedisUtils.getJedis();
System.out.println(jedis.ping());
}
4.1.3 redis 存储hash值
/**
* jedis 存储hash 值
*/
@Test
public void test_03(){
Jedis jedis = JedisUtils.getJedis();
//现在redis中查询
if(jedis.exists("user")){
//返回值类型为map
Map<String, String> map = jedis.hgetAll("user");
System.out.println("redis查询到的对象是:"+map);
}else {
//redis 没有的话在数据库中查找(假装)
User user = new User();
user.setId("1");
user.setAge(20);
user.setUname("zhangsan");
jedis.hset("user", "id", "1");
jedis.hset("user", "age", "20");
jedis.hset("user", "uname", "zhangsan");
System.out.println("mysql查询到的对象是:"+user);
}
//关闭连接
jedis.close();
}
4.2 RedisTemplate (springboot整合redis)
Spring data 提供了RedisTemplate模版
它封装了redis连接池管理的逻辑,业务代码无须关心获取,释放连接逻辑;spring redis同时支持了Jedis,Jredis,rjc 客户端操作;
在RedisTemplate中提供了几个常用的接口方法的使用,分别是
4.2.1 环境配置
jar:Redis和SpringBoot整合
首先添加POM文件
<!-- redis 和SpringBoot整合 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
由于我们使用SpringBoot 进行测试所以还需要添加一个POM文件
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
编写SpringBoot 运行主类
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
编写测试类
@RunWith(SpringRunner.class)
@SpringBootTest(classes={App.class})// 指定启动类
public class Redis {
@Autowired
RedisTemplate redisTemplate;
@Test
public void test_04(){
ValueOperations<String, String> result = redisTemplate.opsForValue();
if(redisTemplate.hasKey("abc")){
String value = result.get("abc");
System.out.println("--redis中--"+value);
}else{
//到数据库中查
String b = "hahahaah";
result.set("abc", b);
System.out.println("--mysql中--"+ b);
}
}
}
4.2.2 redis-springboot-RedisTemplate JDK序列化方式更改
默认的是JdkSerializationRedisSerializer 我们需要修改一下
if (this.defaultSerializer == null) {
this.defaultSerializer = new JdkSerializationRedisSerializer(this.classLoader != null ? this.classLoader : this.getClass().getClassLoader());
}
修改步骤:1.加入maven依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.29</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
</dependency>
2.编写RedisConfig 配置类
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){
RedisTemplate redisTemplate = new RedisTemplate();
redisTemplate.setConnectionFactory(redisConnectionFactory);
//1, 解决key的序列化方式
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(stringRedisSerializer);
//2.解决value 的序列化方式
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
ObjectMapper objectMapper = new ObjectMapper();
//针对时间类型的自定义序列化方式
// SimpleModule simpleModule = new SimpleModule();
// simpleModule.addSerializer(DateTime.class, new JodaDateTimeJsonSerializer());
// simpleModule.addDeserializer(DateTime.class, new JodaDateTimeJsonDeserializer());
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
// objectMapper.registerModule(simpleModule);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
return redisTemplate;
}
}
我们可以与上面的结果进行对比发现序列化方式的结果没有乱码了。
4.2.3 RedisTemplate 操作hash数据
redisTemplate 与用客户端插入hash数据的对比
@Test
public void test_05(){
User user = new User();
user.setId("1");
user.setAge(12);
user.setUname("张三");
redisTemplate.opsForHash().put("user", user.getId(), user);
System.out.println("存储完毕");
}
在存储对象的时候记得要把要存储的对象实现序列化接口public class User implements Serializable 否则的话会报下面的异常。
org.springframework.data.redis.serializer.SerializationException: Cannot serialize; nested exception is org.springframework.core.serializer.support.SerializationFailedException: Failed to serialize object using DefaultSerializer; nested exception is java.lang.IllegalArgumentException: DefaultSerializer requires a Serializable payload but received an object of type [com.redis.User]
4.2.4 redis-springboot-RedisTemplate hash序列化方式更改
从源码中我们可以看到默认的hash序列化方式是jdk提供的,这里我们要更改为String
if (this.hashKeySerializer == null) {
this.hashKeySerializer = this.defaultSerializer;
defaultUsed = true;
}
我们在redisConfig 中设置hash的key value 的序列化方式
//设置hash存储的Ksy value 的序列化方式
redisTemplate.setHashKeySerializer(stringRedisSerializer);
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
更改序列化方式前后结果对比:
redis 多数据库
Redis下,数据库是由一个整数索引标识,而不是由一个数据库名称。默认情况下,一个客户端连接到数据库0。
redis配置文件中下面的参数来控制数据库总数:
database 16
//(从0开始 1 2 3 …15 默认16个数据库)
select 数据库
//数据库的切换
移动数据(将当前key移动另个库)
move key名称 数据库
数据库清空:
flushdb //清除当前数据库的所有key
flushall //清除整个Redis的数据库所有key
redis使用之集群
单个redis问题:
1.内存容量有限 2.处理能力有限 3.无法高可用(一直持续被使用)
主从复置之后还存在问题不满足高可用性,如果主服务器宕机,那么整个主从服务全部宕机了,所以产生了哨兵模式
缺点:需要不停的对redis做监视,带来网络占用 cpu占用
redis集群
redis cluster容错:
容错指的是软件监测应用程序所运行的软件或者硬件中发生错误并从错误中进行恢复的能力,通常可以从系统的可靠性、可用性、可测性等几个方面衡量。
1、什么时候判断master不可用?
投票机制,投票过程是集群中所有master参与,如果半数以上的master节点与master节点通信超时,认为当前master节点挂掉。
2、什么时候判断整个集群不可用?
如果集群任意master挂掉,且当前master没有slave,集群进入fail状态,也可以理解成集群的slot映射[0-16383]不完整时进入fail状态,如果集群超过半数以上master挂掉。无论是否有slave,集群进入fail状态。
集群:redis cluster在设计的时候,就考虑到了去中心化,去中间件,也就是说,集群中的每个节点都是平等的关系,都是对等的,每个节点都保存各自的数据和整个集群的状态。每个节点都和其他所有节点连接,而且这些连接保持活跃,这样就保证了我们只需要连接集群中的任意一个节点就能获取到其他节点的数据。
集群的开启与关闭
参考网站:
http://www.redis.cn/
https://blog.csdn.net/javaxiaibai0414/article/details/88666453
https://www.jianshu.com/p/0431ae3ef98f