1 redis的数据类型
1.1String 类型
redis 中没有使用 C 语言的字符串表示,而是自定义一个数据结构叫 SDS(simple dynamic string)即简单动态字符串。redis 的字符串是二进制安全的,存入什么数据取出的还是什么数据。
String 类型的的常用命令有:
1.1.1赋值命令
redis 中赋值操作使用 set 命令,语法 set key value。
如果希望一次性插入多条数据,可以使用mset命令,语法mset key1 value1 key2 value2 …
1.1.2取值命令
redis 中进行取值操作使用 get 命令,语法:get key。
需要注意的是,如果通过 get 命令获取数据时,给定的键不存在,则返回(nil),
表示查询结果为空。
如果希望一次性从 redis 中获取多个键对应的数据的值,可以使用 mget 命令,
语法:mget key1 key2 …
我们也可以使用 getset 命令,在获取对应键的数据的同时,为该键指定新的值。
语法:getset key newValue。
1.1.3删除命令
在 Redis 中删除数据使用 del 命令,语法:del key。
如果需要直接清空该库中所有数据,可以使用 flushdb 命令
如果要清空所有库中的所有数据使用 flushall 命令。
1.1.4数值递增
当存储的字符串是整数形式时,Redis 提供了一个实用的命令 incr,其作用是让当前键值递增,并返回递增后的值。语法:incr key。(类似于mysql中的auto_increment) 当数据库本身没有对应的key时会自动创建一个,并且赋值为1
当key存在时,会默认+1
非整数形式的key使用个incr会报错
以上的自增表示每次加 1,如果需要按照指定的数据来进行增长,可以使用incrby 命令,
语法:incrby key increment。
1.1.5数值递减
当需要对数据进行递减操作时,使用 decr 命令,
语法:decr key。
如果希望递减指定的数值,可以使用 decrby,
语法:decrby key decrement。
1.1.6追加数据
在 Redis 中可以使用 append 命令向指定数据的末尾追加内容,
语法:append key value。
注意:在插入数据时,字符串的双引号默认可以省略,但是如果字符串中包含空格的情况,需要加上双引号,否则空格会被忽略。
1.1.7获取长度
使用 strlen 命令可以返回指定键对应的值的长度,如果键不存在则返回 0。
语法:strlen key。
1.2 hash 类型
hash 叫散列类型,它提供了字段和字段值的映射。字段值只能是字符串类型,不支持散列类型、集合类型等其它类型。
Hash类型类似于java中的map类型,使用key(field)-value的形式组织数据。
1.2.1赋值命令
向 hash 中插入或设置数据没使用 hset 命令,
语法:hset key field value。
案例:创建一个名为stu的散列表,添加域name,值为suse
给散列表添加另外两个key
如果希望从 redis 中一次性设置多个字段的值,可以使用 hmset,
语法:hmset key field1 value1 field2 value2 …
范例:向 redis 中插入 user 的 id 和 age 属性。
12.2取值命令
从 hash 中获取数据使用 hget 命令,
语法:hget key field。
案例:取出stu的每个属性值
使用 hmget 可以一次性获取多个字段的值。语法:hmget key field1 field2
field3 …
案例:同时取出user的所有的属性值
可以使用 hgetall 命令,或者指定键的所有字段和字段的值。
语法:hgetall key。
案例:同时取出user的属性和值
使用 HKEYS 和 HVALS 命令可以分别用来获取指定键的所有键和所有值。
案例:分别取出stu的keys和values
1.2.3判断字段是否存在
判断字段是否存在使用 HEXISTS 命令,如果存在则返回 1,如果不存在则返回 0 ,
语法:HEXISTS key field
案例:分别判断stu是否存在字段name和addr
1.2.4获取字段数量
获取字段数量使用 HLEN 命令,如果键不存在则返回 0,语法 HLEN key。
案例:判断stu有多少个字段?
1.2.5删除命令
要删除一个或多个字段,使用 hdel 命令,语法:HDEL key field [field ...]
案例:
同时删除多个字段:
1.2.6增加数字
如果 hash 中的字段存储的值为数值类型,可以进行自增操作。
语法:HINCRBY key field increment。
案例:将stu的age增加10
1.3 list 类型
列表类型(list)可以存储一个有序的字符串列表,常用的操作是向列表两端添加元素,或者获得列表的某一个片段。
列表类型内部是使用双向链表(double linked list)实现的,所以向列表两端添加元素的时间复杂度为 0(1),获取越接近两端的元素速度就越快。这意味着即使是一个有几千万个元素的列表,获取头部或尾部的 10 条记录也是极快的。
1.3.1添加数据
Redis 列表采用双向链表实现,因此在插入数据时可以从两端分别操作,向
列表左边增加元素使用 lpush 命令,向列表右边增加元素使用 rpush 命令。
语法 : lpush key value [value…]
理论结果:
1.3.2查看列表
LRANGE 命令是列表类型最常用的命令之一,获取列表中的某一片段,将返回 start、stop 之间的所有元素(包含两端的元素),索引从 0 开始。索引可以是负数,如:“-1”代表最后边的一个元素。
语法:lrange key start stop
取出部分元素:
取出全部元素: 结束的索引使用“-1”
从以上获取的数据结构来看,我们发现从列表的左侧插入数据,在列表中数据以倒叙存放,从列表的右侧插入数据则是正序存放。
1.3.3获取数据
在列表中可以使用 lpop 和 rpop 分别列表的左侧和右侧弹出一个数据,该数据会首先返回,然后再从原列表中删除。
语法: lpop key
从右边弹出数据
1.3.4获取列表中元素的个数
使用 LLEN 命令可以获取列表中元素的个数。范例:LLEN key
1.3.5 删除列表中指定的值
LREM 命令会删除列表中前 count 个值为 value 的元素,返回实际删除的元素个数。根据 count 值的不同,该命令的执行方式会有所不同:
当 count>0 时, LREM 会从列表左边开始删除。
当 count<0 时, LREM 会从列表后边(右边)开始删除。
当 count=0 时, LREM 删除所有值为 value 的元素。
案例:
分别从左边和右边删除a
删除所有的a count为1
1.3.6 获得/设置指定索引的元素值
获得或者设置指定索引的元素值分别使用 lindex 和 lset 命令
案例:获取list1的索引为3的元素值
设置索引为3的元素的值
1.3.7只保留列表指定片段
List中使用ltrim命令对列表中的数据进行截取操作,只保留指定数据片段。
语法:ltrim key start stop。
1.3.8向列表中插入元素
向列表中指定位置插入元素可以使用 linsert 命令,语法:LINSERT key
BEFORE|AFTER pivot value。
1.3.9将元素从一个列表转移到另一个列表中
使用 RPOPLPUSH 命令可以将元素从一个列表转移到另一个列表中。
语法:
RPOPLPUSH source destination。
1.4set 类型
集合类型的常用操作是向集合中加入或删除元素、判断某个元素是否存在等,由于集合类型的 Redis 内部是使用值为空的散列表实现,所有这些操作的时间复杂度都为 0(1)。 Redis 还提供了多个集合之间的交集、并集、差集的运算。
1.4.1增加/删除元素
集合中添加数据使用 sadd 命令,语法 sadd key member [member…].
不能添加重复的元素:
1.4.2 获得集合中的所有元素
获取集合中所有元素使用 SMEMBERS 命令,
语法:SMEMBERS key
由于set没有顺序所有不能range,只能一次性取出所有的元素:
1.4.3集合的差集运算
属于 A 并且不属于 B 的元素构成的集合,使用命令 SDIFF,语法:SDIFF key
[key ...]
1.4.4集合的交集运算
属于 A 并且也属于 B 的元素构成的集合,使用命令 SINTER,语法:SINTER
key [key ...]
1.4.5 集合的并集运算
属于 A 并且或者属于 B 的元素构成的集合,使用命令 SUNION,语法:
SUNION key [key ...]
1.4.6获得集合中元素的个数
获得集合中元素的个数,使用 SCARD 命令,如果集合不存在则返回 0,
语法:SCARD key
1.4.7 从集合中弹出一个元素
从集合中弹出一个元素使用 spop 命令,语法:spop key
1.5sorted set 类型
在集合类型的基础上有序集合类型为集合中的每个元素都关联一个分数,这使得我们不仅可以完成插入、删除和判断元素是否存在在集合中,还能够获得分数最高或最低的前N个元素、获取指定分数范围内的元素等与分数有关的操作。
在某些方面有序集合和列表类型有些相似。
1、二者都是有序的。
2、二者都可以获得某一范围的元素
但是,二者有着很大区别:
1、列表类型是通过链表实现的,获取靠近两端的数据速度极快,而当元素增多后,访问中间数据的速度会变慢。
2、有序集合类型使用散列表实现,所有即使读取位于中间部分的数据也很快。
3、列表中不能简单的调整某个元素的位置,但是有序集合可以(通过更改分数实现)
4、有序集合要比列表类型更耗内存
1.5.1 增加元素
向有序集合中加入一个元素和该元素的分数,如果该元素已经存在则会用新的分数替换原有的分数。返回值是新加入到集合中的元素个数,不包含之前已经存在的元素。
语法:ZADD key score member [score member ...]
1.5.2获得排名在某个范围的元素列表
获得排名在某个范围的元素列表使用 zrange 命令,
语法:ZRANGE key start stop [WITHSCORES]
案例:获取 score 中的元素
Tips:可以通过添加withscores获取元素的分数,分数默认按照升序排序。
降序获取 score 中的元素
语法:zrevrange key start stop
降序排序获取前三名:
1.5.3删除元素
移除有序集 key 中的一个或多个成员使用命令 ZREM,不存在的成员将被忽略。
语法:ZREM key member [member ...]
删除green
1.5.4获得指定分数范围的元素
获 得 指 定 分 数 范 围 的 元 素 使 用 ZRANGEBYSCORE 命 令 ,
语 法 :ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
Limit offset count 和mysql中的limit start,seize 是一个意思
Offset:开始的位置
Count:获取的个数
1.5.5增加某个元素的分数,返回值是更改后的分数
增加某个元素的分数,返回值是更改后的分数,使用命令 ZINCRBY,
语法:ZINCRBY key increment member
1.5.6获得集合中元素的数量
获取集合中元素的数量使用 ZCARD 命令,语法:ZCARD key。
1.5.7获得指定分数范围内的元素个数
获得指定分数范围内的元素个数使用命令 zcount,语法:ZCOUNT key min max。
1.5.8按照排名范围删除元素
按 照 排 名 范 围 删 除 元 素 使 用 命 令 ZREMRANGEBYRANK ,
语 法 :ZREMRANGEBYRANK key start stop
1.5.9按照分数范围删除元素
按 照 分数 范围 删除 元素使用 命令 ZREMRANGEBYSCORE ,
语法:ZREMRANGEBYSCORE key start stop
1.5.10获取元素的排名
获取元素的排名使用 zrank 命令,语法:ZRANK key member
Tips:默认按照升序排名
如果要使用倒序排名使用命令 ZREVRANK。
1.6 redis 的其他命令
1.6.1设置 key 的生存时间
Redis 在实际使用过程中更多的用作缓存,然而缓存的数据一般都是需要设置生存时间的,即:到期后数据销毁。类似于session和cookie。
EXPIRE key seconds 设置 key 的生存时间(单位:秒)key 在多少秒后会自动删除 TTL key 查看 key 生于的生存时间 PERSIST key 清除生存时间 PEXPIRE key milliseconds 生存时间设置单位为:毫秒 |
案例:设置和查看key的生存时间
在生成时间逐渐减少的过程中可以通过设置修改其生存时间
如果一个key没有设置过生存时间,则默认是-1
如果时间已经过期,或者key不存在就是-2
取消一个key 的生存时间设置(让key成为永久存在)
设置一个key的生存时间,但是是以毫秒为单位的。
1.6.2返回满足给定 pattern 的所有 key
Redis 中可以使用 pattern 来获取键的名称。
语法:kesy pattern (*表示所有的key)
获取所有的key
获取以stu开头的key
1.6.3重命名 key
重命名 key 使用命令 rename,语法:rename name newName
1.6.4返回值的类型
使用 type 命令可以返回指定键的值的类型,语法:type key。
1.6.5返回当前数据库中 key 的数目
返回当前数据库中 key 的数目使用 dbsize 命令
1.6.6获取服务器的信息和统计
获取服务器的信息和统计使用 info 命令
范例:获取服务器信息
127.0.0.1:6379[5]> info # Server redis_version:3.0.7 redis_git_sha1:00000000 redis_git_dirty:0 redis_build_id:97bf5cc32376f4dd redis_mode:standalone os:Linux 2.6.32-431.el6.x86_64 x86_64 arch_bits:64 multiplexing_api:epoll gcc_version:4.4.7 process_id:4258 run_id:5138f58b23769e1310ed8e454958dfa66ff1e7d1 tcp_port:6379 uptime_in_seconds:45134 uptime_in_days:0 hz:10 lru_clock:129312 config_file:/usr/local/redis/redis.conf
# Clients connected_clients:1 client_longest_output_list:0 client_biggest_input_buf:0 blocked_clients:0
# Memory used_memory:819184 used_memory_human:799.98K used_memory_rss:2232320 used_memory_peak:835800 used_memory_peak_human:816.21K used_memory_lua:36864 mem_fragmentation_ratio:2.73 mem_allocator:jemalloc-3.6.0
# Persistence loading:0 rdb_changes_since_last_save:11 rdb_bgsave_in_progress:0 rdb_last_save_time:1543633045 rdb_last_bgsave_status:ok rdb_last_bgsave_time_sec:0 rdb_current_bgsave_time_sec:-1 aof_enabled:0 aof_rewrite_in_progress:0 aof_rewrite_scheduled:0 aof_last_rewrite_time_sec:-1 aof_current_rewrite_time_sec:-1 aof_last_bgrewrite_status:ok aof_last_write_status:ok
# Stats total_connections_received:6 total_commands_processed:287 instantaneous_ops_per_sec:0 total_net_input_bytes:10468 total_net_output_bytes:5227 instantaneous_input_kbps:0.00 instantaneous_output_kbps:0.00 rejected_connections:0 sync_full:0 sync_partial_ok:0 sync_partial_err:0 expired_keys:2 evicted_keys:0 keyspace_hits:131 keyspace_misses:12 pubsub_channels:0 pubsub_patterns:0 latest_fork_usec:801 migrate_cached_sockets:0
# Replication role:master connected_slaves:0 master_repl_offset:0 repl_backlog_active:0 repl_backlog_size:1048576 repl_backlog_first_byte_offset:0 repl_backlog_histlen:0
# CPU used_cpu_sys:51.15 used_cpu_user:23.93 used_cpu_sys_children:0.03 used_cpu_user_children:0.01
# Cluster cluster_enabled:0
# Keyspace db0:keys=5,expires=0,avg_ttl=0 db1:keys=2,expires=0,avg_ttl=0 db2:keys=3,expires=0,avg_ttl=0 db3:keys=3,expires=0,avg_ttl=0 db4:keys=1,expires=0,avg_ttl=0 db5:keys=8,expires=0,avg_ttl=0 |
1.6.7退出连接
退出客户端连接
语法:exit
2redis的持久化方案
Redis 的高性能是由于其将所有数据都存储在了内存中,为了使 Redis 在重启之后仍能保证数据不丢失,需要将数据从内存中同步到硬盘中,这一过程就是持久化。
Redis 支持两种方式的持久化,一种是 RDB 方式,一种是 AOF 方式。可以单独使用其中一种或将二者结合使用。
2.1 RDB 持久化
RDB 方式的持久化是通过快照(snapshotting)完成的,当符合一定条件时 Redis 会自动将内存中的数据进行快照并持久化到硬盘。
RDB 是 Redis 默认采用的持久化方式,在 redis.conf 配置文件中默认有此下配置:
900秒以内修改了1个key就会持久化一次。
300秒以内修改了10个key就会持久化一次。
60秒以内修改了10000个key就会持久化一次。
Redis 启动后会读取 RDB 快照文件,将数据从硬盘载入到内存。根据数据量大小与结构和服务器性能不同,这个时间也不同。通常将记录一千万个字符串类型键、大小为 1GB 的快照文件载入到内存中需要花费 20~30 秒钟。如果开启了 RDB,Redis 默认会在启动当前服务的目录自动创建数据文件。通过 RDB 方式实现持久化,一旦 Redis 异常退出,就会丢失最后一次快照以后更改的所有数据。这就需要开发者根据具体的应用场合,通过组合设置自动快照条件的方式来将可能发生的数据损失控制在能够接受的范围。如果数据很重要以至于无法承受任何损失,则可以考虑使用 AOF 方式进行持久化。
Redis持久化的数据文件是存储在和redis.conf所在的目录中的文件,默认是dump.rdb
可以在配置文件中修改数据的存储文件。
2.2 AOF 持久化
默认情况下 Redis 没有开启 AOF(append only file)方式的持久化,可以通过 appendonly 参数开启:appendonly yes
开启 AOF 持久化后每执行一条会更改 Redis 中的数据的命令,Redis 就会将该命令写入硬盘中的 AOF 文件。AOF 文件的保存位置和 RDB 文件的位置相同,都是通过 dir 参数设置的,默认的文件名是 appendonly.aof,可以通过appendfilename 参数修改:appendfilename appendonly.aof
测试:
添加数据:
杀死服务进程:
查看数据存储的文件:
查看aof文件:其中存储的是执行的命令
启动redis发现数据还在,。
AOP方式就是保存操作的命令,再次启动redis时,会将所有的命令再次执行一次恢复数据。
3 redis集群
3.1集群和分布式
三高:高可用,高并发,高性能
高可用:“高可用性”(High Availability)通常来描述一个系统经过专门的设计,从而减少停工时间,而保持其服务的高度可用性。
高并发:在计算机科学中,并发性(Concurrence)是指在一个系统中,拥有多个计算,这些计算有同时执行的特性,而且他们之间有着潜在的交互。因此系统可进行的运行路径会有相当多个,而且结果可能具有不确定性。
高性能:高性能计算(High performance computing, 缩写HPC) 指通常使用很多处理器(作为单个机器的一部分)或者某一集群中组织的几台计算机(作为单个计 算资源操作)的计算系统和环境。
质量不够,数量来凑!
使用多个服务器一起完成一样工作,称之为集群。
分布式是一个任务由多个服务器一起完成。
服务器数量最少超过两台才是集群。无论多少台服务器,对外都是一个整体。
3.2redis-cluster
3.2.1 redis-cluster 架构图
架构图中每个蓝色的圆就表示一个集群节点。所谓节点可以认为就是集群中的“一个服务器”。下面结构中,有5个节点,redis-cluster中,每个节点所存储的数据时不相同的。这样可以分解压力,提高服务器的性能。(人多力量大)。
任务调度是集群系统中的核心部分。
什么是任务调度:当需要服务时,由哪个节点来完成具体的服务(工作)。在很多的集群中都存在一个“老大”。但是在redis的集群中是没有老大的,所有的节点都是平等的。对于客户来说,任何时候访问任何一个节点都是一样。
如果没有“老大”,如果有节点“挂了”,应该如何协调?
redis-cluster 通过投票容错机制判断节点是否还在工作:
Redis集群中每个节点都会按时向其他节点发送”心跳包”。当有一个节点不响应时,则判断该节点故障。要求是超过半数的投票不响应。
Redis的主从复制:解决高可用的问题
Tips:A,B,C三个节点中存储的数据时不一致的,三个节点集成在一起才是完成数据。但是主服务器A 和从服务器a中存储的数据时一直的。
架构细节:
(1)所有的 redis 节点彼此互联(PING-PONG 机制),内部使用二进制协议优化传输速度和带宽.
(2)节点的 fail 是通过集群中超过半数的节点检测失效时才生效.
(3)客户端与 redis 节点直连,不需要中间 proxy 层.客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可
3.2.2redis任务调度
Redis通过16384个槽分配任务
redis-cluster 把所有的物理节点映射到[0-16383]slot 上,cluster 负责维护
node<->slot<->value
Redis 集群中内置了 16384 个哈希槽,当需要在 Redis 集群中放置一个 key-value 时,
redis 先对 key 使用 crc16 算法算出一个结果,然后把结果对 16384 求余数,这样每个
key 都会对应一个编号在 0-16383 之间的哈希槽,redis 会根据节点数量大致均等的将哈
希槽映射到不同的节点
并不是说有16384个槽,就只能存储16384条数据。
例如:uname---(算法)---1356 upass ---(算法)---1356
Redis-cluster可以有多少节点?
每个节点1个槽,最多就是16384个节点。
3.3 Redis 集群的搭建
Redis 集群中至少应该有三个节点。要保证集群的高可用,需要每个节点有一个备份机。
Redis 集群至少需要 6 台服务器。
搭建伪分布式。可以使用一台虚拟机运行 6 个 redis 实例。需要修改 redis 的端口号 7001-7006
3.3.1集群搭建环境
1、使用 ruby 脚本搭建集群。需要 ruby 的运行环境。
安装 ruby
yum install ruby
yum install rubygems
2、安装 ruby 脚本运行使用的包。
将 redis-3.0.0.gem.tar.gz 上传到根目录
安装:
3.3.2搭建步骤
在urs/local下创建一个redis-cluster目录
拷贝 redis-3.0.0/src 下的 ruby 脚本到 redis-cluster 下
在redis-cluster目录下创建6个文件夹用来存放6个服务器的配置文件
将redis的配置文件分别赋值到6个文件夹中:
逐个修改配置文件
修改端口为对应的端口(7001~7006)
修改cluster-enabled为yes
逐个修改完成之后,启动每个 redis 实例
编写脚本启动每个实例:
脚本没有运行权限:
添加运行权限
使用脚本启动
使用 ruby 脚本搭建集群
./redis-trib.rb create --replicas 1 192.168.30.150:7001 192.168.30.150:7002 192.168.30.150:7003 192.168.30.150:7004 192.168.30.150:7005 192.168.30.150:7006 |
3.4集群的使用方法
连接redis:指定端口7001
发现无法从7001自动移动到7002。是因为使用的是单机版的连接方式。
使用-c指明连接的是集群:
Redis-clir –p 7001 –c
Redis-cluster连接任何一个节点都可以,无论是主节点还是从节点都行:
使用客户端工具查看redis
双击安装
连接redis
通过客户端查看数据:
3.5使用jedis连接集群
public class JedisClusterTest { @Test public void testCluster() { //创建节点集合 Set<HostAndPort> nodes = new HashSet<>(); nodes.add(new HostAndPort("192.168.30.150", 7001)); nodes.add(new HostAndPort("192.168.30.150", 7002)); nodes.add(new HostAndPort("192.168.30.150", 7003)); nodes.add(new HostAndPort("192.168.30.150", 7004)); nodes.add(new HostAndPort("192.168.30.150", 7005)); nodes.add(new HostAndPort("192.168.30.150", 7006)); //创建一个jedisCluster对象 JedisCluster cluster = new JedisCluster(nodes); //所有的redis中的命令在jedis对象和jedisCluster对象中都有对应的方法。 cluster.set("stuName", "wangwu"); } } |