标签:还需要 多线程 工作过程 集中 publish 日志记录 cut 原子操作 comm
@font-face { font-family: "Arial"; }@font-face { font-family: "Courier New"; }@font-face { font-family: "Times"; }@font-face { font-family: "Geneva"; }@font-face { font-family: "Tms Rmn"; }@font-face { font-family: "Helv"; }@font-face { font-family: "MS Serif"; }@font-face { font-family: "MS Sans Serif"; }@font-face { font-family: "New York"; }@font-face { font-family: "System"; }@font-face { font-family: "Wingdings"; }@font-face { font-family: "MS 明朝"; }@font-face { font-family: "??"; }@font-face { font-family: "宋体"; }@font-face { font-family: "新細明體"; }@font-face { font-family: "MS ゴシック"; }@font-face { font-family: "Century"; }@font-face { font-family: "Verdana"; }@font-face { font-family: "Angsana New"; }@font-face { font-family: "Cordia New"; }@font-face { font-family: "Mangal"; }@font-face { font-family: "Latha"; }@font-face { font-family: "Sylfaen"; }@font-face { font-family: "Vrinda"; }@font-face { font-family: "Raavi"; }@font-face { font-family: "Shruti"; }@font-face { font-family: "Sendnya"; }@font-face { font-family: "Gautami"; }@font-face { font-family: "Tunga"; }@font-face { font-family: "Estrangelo Edessa"; }@font-face { font-family: "Cambria Math"; }@font-face { font-family: "@宋体"; }@font-face { font-family: "Calibri Light"; }@font-face { font-family: "Calibri"; }@font-face { font-family: "@MS 明朝"; }@font-face { font-family: "@MS ゴシック"; }@font-face { font-family: "Microsoft YaHei"; }@font-face { font-family: "Consolas"; }@font-face { font-family: "Helvetica Neue"; }@font-face { font-family: "SimSun"; }@font-face { font-family: "@SimSun"; }@font-face { font-family: "@Microsoft YaHei"; }p.MsoNormal, li.MsoNormal, div.MsoNormal { margin: 0cm 0cm 0.0001pt; text-align: justify; font-size: 12pt; font-family: Calibri; }h1 { margin: 17pt 0cm 16.5pt; text-align: justify; line-height: 240%; page-break-after: avoid; font-size: 22pt; font-family: Calibri; }h2 { margin: 13pt 0cm; text-align: justify; line-height: 173%; page-break-after: avoid; font-size: 16pt; font-family: "Calibri Light"; }h3 { margin: 13pt 0cm; text-align: justify; line-height: 173%; page-break-after: avoid; font-size: 16pt; font-family: Calibri; }p.MsoDocumentMap, li.MsoDocumentMap, div.MsoDocumentMap { margin: 0cm 0cm 0.0001pt; text-align: justify; font-size: 12pt; font-family: "Times New Roman"; }span.a { font-family: "Times New Roman"; }span.msoIns { text-decoration: underline; color: teal; }span.msoDel { text-decoration: line-through; color: red; }.MsoChpDefault { font-family: Calibri; }div.WordSection1 { }ol { margin-bottom: 0cm; }ul { margin-bottom: 0cm; }
Redis 是完全开源免费的,遵守BSD协议,是一个高性能的key-value数据库。
Redis 与其他 key - value 缓存产品有以下三个特点:
Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用。
Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储。
Redis支持数据的备份,即master-slave模式的数据备份。
性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。
丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。
原子 – Redis的所有操作都是原子性的,同时Redis还支持对几个操作全并后的原子性执行。
丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性。
与其他key-value存储的不同
Redis有着更为复杂的数据结构并且提供对他们的原子性操作,这是一个不同于其他数据库的进化路径。Redis的数据类型都是基于基本数据结构的同时对程序员透明,无需进行额外的抽象。
Redis运行在内存中但是可以持久化到磁盘,所以在对不同数据集进行高速读写时需要权衡内存,应为数据量不能大于硬件内存。在内存数据库方面的另一个优点是,相比在磁盘上相同的复杂的数据结构,在内存中操作起来非常简单,这样Redis可以做很多内部复杂性很强的事情。同时,在磁盘格式方面他们是紧凑的以追加的方式产生的,因为他们并不需要进行随机访问。
1、下载安装包
2、解压
3、安装
make && make install
make是编译,make install是安装,可以指定安装路径
make PREFIX=/use/local/redis install
PREFIX用于指定安装路径
4、验证
除了执行PATH路径下的命令外,也可以去 src 目录下(推荐这个)
在:/usr/local/bin 下,会有 redis-server 一般此目录已经加入PATH了
redis-server 使用默认配置启动refis服务
指定配置文件的方式启动:
redis-server redis.conf
配置文件默认在安装目录下
新开一个窗口,redis-cli 使用默认配置连接Redis服务
执行ping,显示PONG,说明成功了
4、关闭服务
在进入 redis-cli 的情况下,直接shutdown命令,
或者使用kill命令
登陆服务器
redis-cli 或者 redis-cli -h 127.0.0.1 -p 6379
设置配置信息
1、去修改配置文件
2、通过config set命令修改
config set key value
3、获取配信息
config get key
详细配置项
redis.conf 配置项说明如下:
1. Redis默认不是以守护进程的方式运行,可以通过该配置项修改,使用yes启用守护进程
daemonize no
2. 当Redis以守护进程方式运行时,Redis默认会把pid写入/var/run/redis.pid文件,可以通过pidfile指定
pidfile /var/run/redis.pid
3. 指定Redis监听端口,默认端口为6379,作者在自己的一篇博文中解释了为什么选用6379作为默认端口,因为6379在手机按键上MERZ对应的号码,而MERZ取自意大利歌女Alessia Merz的名字
port 6379
4. 绑定的主机地址
bind 127.0.0.1
5.当 客户端闲置多长时间后关闭连接,如果指定为0,表示关闭该功能
timeout 300
6. 指定日志记录级别,Redis总共支持四个级别:debug、verbose、notice、warning,默认为verbose
loglevel verbose
7. 日志记录方式,默认为标准输出,如果配置Redis为守护进程方式运行,而这里又配置为日志记录方式为标准输出,则日志将会发送给/dev/null
logfile stdout
8. 设置数据库的数量,默认数据库为0,可以使用SELECT <dbid>命令在连接上指定数据库id
databases 16
9. 指定在多长时间内,有多少次更新操作,就将数据同步到数据文件,可以多个条件配合
save <seconds> <changes>
Redis默认配置文件中提供了三个条件:
save 900 1
save 300 10
save 60 10000
分别表示900秒(15分钟)内有1个更改,300秒(5分钟)内有10个更改以及60秒内有10000个更改。
10. 指定存储至本地数据库时是否压缩数据,默认为yes,Redis采用LZF压缩,如果为了节省CPU时间,可以关闭该选项,但会导致数据库文件变的巨大
rdbcompression yes
11. 指定本地数据库文件名,默认值为dump.rdb
dbfilename dump.rdb
12. 指定本地数据库存放目录
dir ./
13. 设置当本机为slav服务时,设置master服务的IP地址及端口,在Redis启动时,它会自动从master进行数据同步
slaveof <masterip> <masterport>
14. 当master服务设置了密码保护时,slav服务连接master的密码
masterauth <master-password>
15. 设置Redis连接密码,如果配置了连接密码,客户端在连接Redis时需要通过AUTH <password>命令提供密码,默认关闭
requirepass foobared
16. 设置同一时间最大客户端连接数,默认无限制,Redis可以同时打开的客户端连接数为Redis进程可以打开的最大文件描述符数,如果设置 maxclients 0,表示不作限制。当客户端连接数到达限制时,Redis会关闭新的连接并向客户端返回max number of clients reached错误信息
maxclients 128
17. 指定Redis最大内存限制,Redis在启动时会把数据加载到内存中,达到最大内存后,Redis会先尝试清除已到期或即将到期的Key,当此方法处理 后,仍然到达最大内存设置,将无法再进行写入操作,但仍然可以进行读取操作。Redis新的vm机制,会把Key存放内存,Value会存放在swap区
maxmemory <bytes>
18. 指定是否在每次更新操作后进行日志记录,Redis在默认情况下是异步的把数据写入磁盘,如果不开启,可能会在断电时导致一段时间内的数据丢失。因为 redis本身同步数据文件是按上面save条件来同步的,所以有的数据会在一段时间内只存在于内存中。默认为no
appendonly no
19. 指定更新日志文件名,默认为appendonly.aof
appendfilename appendonly.aof
20. 指定更新日志条件,共有3个可选值:
no:表示等操作系统进行数据缓存同步到磁盘(快)
always:表示每次更新操作后手动调用fsync()将数据写到磁盘(慢,安全)
everysec:表示每秒同步一次(折衷,默认值)
appendfsync everysec
21. 指定是否启用虚拟内存机制,默认值为no,简单的介绍一下,VM机制将数据分页存放,由Redis将访问量较少的页即冷数据swap到磁盘上,访问多的页面由磁盘自动换出到内存中(在后面的文章我会仔细分析Redis的VM机制)
vm-enabled no
22. 虚拟内存文件路径,默认值为/tmp/redis.swap,不可多个Redis实例共享
vm-swap-file /tmp/redis.swap
23. 将所有大于vm-max-memory的数据存入虚拟内存,无论vm-max-memory设置多小,所有索引数据都是内存存储的(Redis的索引数据 就是keys),也就是说,当vm-max-memory设置为0的时候,其实是所有value都存在于磁盘。默认值为0
vm-max-memory 0
24. Redis swap文件分成了很多的page,一个对象可以保存在多个page上面,但一个page上不能被多个对象共享,vm-page-size是要根据存储的 数据大小来设定的,作者建议如果存储很多小对象,page大小最好设置为32或者64bytes;如果存储很大大对象,则可以使用更大的page,如果不 确定,就使用默认值
vm-page-size 32
25. 设置swap文件中的page数量,由于页表(一种表示页面空闲或使用的bitmap)是在放在内存中的,,在磁盘上每8个pages将消耗1byte的内存。
vm-pages 134217728
26. 设置访问swap文件的线程数,最好不要超过机器的核数,如果设置为0,那么所有对swap文件的操作都是串行的,可能会造成比较长时间的延迟。默认值为4
vm-max-threads 4
27. 设置在向客户端应答时,是否把较小的包合并为一个包发送,默认为开启
glueoutputbuf yes
28. 指定在超过一定的数量或者最大的元素超过某一临界值时,采用一种特殊的哈希算法
hash-max-zipmap-entries 64
hash-max-zipmap-value 512
29. 指定是否激活重置哈希,默认为开启(后面在介绍Redis的哈希算法时具体介绍)
activerehashing yes
30. 指定包含其它的配置文件,可以在同一主机上多个Redis实例之间使用同一份配置文件,而同时各个实例又拥有自己的特定配置文件
include /path/to/local.conf
Redis数据类型
Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。
String(字符串)
string是redis最基本的类型,你可以理解成与Memcached一模一样的类型,一个key对应一个value。
string类型是二进制安全的。意思是redis的string可以包含任何数据。比如jpg图片或者序列化的对象 。
string类型是Redis最基本的数据类型,一个键最大能存储512MB。
redis 127.0.0.1:6379> SET name "w3cschool.cc"
OK
redis 127.0.0.1:6379> GET name
"w3cschool.cc"
Hash(哈希)
Redis hash 是一个键值对集合。
Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。
redis 127.0.0.1:6379> HMSET user:1 username w3cschool.cc password w3cschool.cc points 200
OK
redis 127.0.0.1:6379> HGETALL user:1
1) "username"
2) "w3cschool.cc"
3) "password"
4) "w3cschool.cc"
5) "points"
6) "200"
redis 127.0.0.1:6379>
以上实例中 hash 数据类型存储了包含用户脚本信息的用户对象。 实例中我们使用了 Redis HMSET, HGETALL 命令,user:1 为键值。
每个 hash 可以存储 232 - 1 键值对(40多亿)。
List(列表)
Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。
redis 127.0.0.1:6379> lpush w3cschool.cc redis
(integer) 1
redis 127.0.0.1:6379> lpush w3cschool.cc mongodb
(integer) 2
redis 127.0.0.1:6379> lpush w3cschool.cc rabitmq
(integer) 3
redis 127.0.0.1:6379> lrange w3cschool.cc 0 10
1) "rabitmq"
2) "mongodb"
3) "redis"
redis 127.0.0.1:6379>
列表最多可存储 232 - 1 元素 (4294967295, 每个列表可存储40多亿)。
Set(集合)
Redis的Set是string类型的无序集合。
集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。
sadd 命令
添加一个string元素到,key对应的set集合中,成功返回1,如果元素已经在集合中返回0,key对应的set不存在返回错误。
redis 127.0.0.1:6379> sadd w3cschool.cc redis
(integer) 1
redis 127.0.0.1:6379> sadd w3cschool.cc mongodb
(integer) 1
redis 127.0.0.1:6379> sadd w3cschool.cc rabitmq
(integer) 1
redis 127.0.0.1:6379> sadd w3cschool.cc rabitmq
(integer) 0
redis 127.0.0.1:6379> smembers w3cschool.cc
1) "rabitmq"
2) "mongodb"
3) "redis"
注意:以上实例中 rabitmq 添加了两次,但根据集合内元素的唯一性,第二次插入的元素将被忽略。
集合中最大的成员数为 232 - 1 (4294967295, 每个集合可存储40多亿个成员)。
zset(sorted set:有序集合)
Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。
不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。
zset的成员是唯一的,但分数(score)却可以重复。
zadd 命令
添加元素到集合,元素在集合中存在则更新对应score
redis 127.0.0.1:6379> zadd w3cschool.cc 0 redis
(integer) 1
redis 127.0.0.1:6379> zadd w3cschool.cc 0 mongodb
(integer) 1
redis 127.0.0.1:6379> zadd w3cschool.cc 0 rabitmq
(integer) 1
redis 127.0.0.1:6379> zadd w3cschool.cc 0 rabitmq
(integer) 0
redis 127.0.0.1:6379> ZRANGEBYSCORE w3cschool.cc 0 1000
1) "redis"
2) "mongodb"
3) "rabitmq"
Redis客户端命令
Redis 命令用于在 redis 服务上执行操作。
要在 redis 服务上执行命令需要一个 redis 客户端。Redis 客户端在我们之前下载的的 redis 的安装包中。
语法
Redis 客户端的基本语法为:
$ redis-cli
启动 redis 客户端,打开终端并输入命令 redis-cli。该命令会连接本地的 redis 服务。
$redis-cli
redis 127.0.0.1:6379>
redis 127.0.0.1:6379> PING
PONG
在以上实例中我们连接到本地的 redis 服务并执行 PING 命令,该命令用于检测 redis 服务是否启动。
在远程服务上执行命令
$ redis-cli -h host -p port -a password
以下实例演示了如何连接到主机为 127.0.0.1,端口为 6379 ,密码为 mypass 的 redis 服务上。
$redis-cli -h 127.0.0.1 -p 6379 -a "mypass"
redis 127.0.0.1:6379>
redis 127.0.0.1:6379> PING
PONG
见
http://www.runoob.com/redis/redis-tutorial.html
这里就不累述了
所有的 Redis 节点在集群中互联,内部使用二进制协议优化速度和带宽
节点的 fail,是通过集群中超过半数的节点检测失效时才生效。也就是说,集群中有半数以上的节点无法连接上某个节点,那么这个节点就被判定 fail
客户端连接集群中的任意一台机器,都可以访问整个集群的数据,因为节点之间会跳转
新的数据,怎么确定添加到集群中的哪台机器呢?
答:Redis 内置了16384个哈希槽,当需要在集群中添加数据的时候,对 Key使用 CRC16算法计算出一个结果,然后将结果对16384取余。
这样,每个 Key 都对应到了0~16383中的一个数字,Redis根据节点数量,大致均等的将哈希槽映射到节点上。
可以理解成集群中的每台 Redis 服务器,都均匀分配了一些槽,Key 的计算结果属于哪个槽,那个槽属于哪台服务器,就将数据存储在哪台服务器上
安装集群需要 ruby 支持
需要 redis 第三方接口支持:Mac 下执行一遍 sudo gem install redis
下面通过一个实例演示Redis 集群的安装
这里在同一台服务器用不同的端口表示不同的redis服务器,如下:
主节点:
192.168.101.3:7001
192.168.101.3:7002
192.168.101.3:7003
从节点:
192.168.101.3:7004
192.168.101.3:7005
192.168.101.3:7006
我这里在一台机器上,通过端口,模拟不同的 Redis 服务器,实际环境中,一般是通过不同机器,也就是 ip 来确定的
1、安装单机版 Redis
2、创建一个文件夹 - redis-cluster
这个文件夹保存所有通过端口区分的集群中的 Redis服务器
创建redis-cluster目录,其下创建7001、7002......7006目录,
将redis安装目录bin下的文件拷贝到每个700X目录内,同时将redis源码目录src下的redis-trib.rb拷贝到redis-cluster目录下。
修改每个700X目录下的redis.conf配置文件:
port XXXX
#bind 192.168.101.3
cluster-enabled yes
3、如果是真实的集群环境,则不需要步骤二。直接启动每个节点
分别进入7001、7002、...7006目录,执行:
./redis-server ./redis.conf
查看redis进程:
4、启动集群
./redis-trib.rb create --replicas 1 192.168.101.3:7001 192.168.101.3:7002 192.168.101.3:7003 192.168.101.3:7004 192.168.101.3:7005 192.168.101.3:7006
说明:
redis集群至少需要3个主节点,每个主节点有一个从节点总共6个节点
replicas指定为1表示每个主节点有一个从节点
注意:
如果执行时报如下错误:
[ERR] Node XXXXXX is not empty. Either the node already knows other nodes (check with CLUSTER NODES) or contains some key in database 0
解决方法是删除生成的配置文件nodes.conf,如果不行则说明现在创建的结点包括了旧集群的结点信息,需要删除redis的持久化文件后再重启redis,比如:appendonly.aof、dump.rdb
5、查询集群信息
./redis-cli -c -h 192.168.101.3 -p 7001 ,其中-c表示以集群方式连接redis,-h指定ip地址,-p指定端口号
cluster nodes 查询集群结点信息
cluster info 查询集群状态信息
其他集群中的操作
1、添加主节点
集群创建成功后可以向集群中添加节点,下面是添加一个master主节点
添加7007结点,参考集群结点规划章节添加一个“7007”目录作为新节点。
./redis-trib.rb add-node 192.168.101.3:7007 192.168.101.3:7001
查看集群结点发现7007已添加到集群中:
cluster nodes
2、Hash槽重新分配
添加完主节点需要对主节点进行hash槽分配这样该主节才可以存储数据。
redis集群有16384个槽,集群中的每个结点分配自已槽,通过查看集群结点可以看到槽占用情况。
给刚添加的7007结点分配槽:
第一步:连接上集群
./redis-trib.rb reshard 192.168.101.3:7001(连接集群中任意一个可用结点都行)
第二步:输入要分配的槽数量
输入 500表示要分配500个槽
第三步:输入接收槽的结点id
这里准备给7007分配槽,通过cluster nodes查看7007结点id为15b809eadae88955e36bcdbb8144f61bbbaf38fb
输入:15b809eadae88955e36bcdbb8144f61bbbaf38fb
第四步:输入源结点id
这里输入all
第五步:输入yes开始移动槽到目标结点id
3、添加从节点
集群创建成功后可以向集群中添加节点,下面是添加一个slave从节点。
添加7008从结点,将7008作为7007的从结点。
./redis-trib.rb add-node --slave --master-id 主节点id 添加节点的ip和端口集群中已存在节点ip和端口
执行如下命令:
./redis-trib.rb add-node --slave --master-id cad9f7413ec6842c971dbcc2c48b4ca959eb5db4 192.168.101.3:7008 192.168.101.3:7001
cad9f7413ec6842c971dbcc2c48b4ca959eb5db4 是7007结点的id,可通过cluster nodes查看。
注意:如果原来该结点在集群中的配置信息已经生成cluster-config-file指定的配置文件中(如果cluster-config-file没有指定则默认为nodes.conf),这时可能会报错:
[ERR] Node XXXXXX is not empty. Either the node already knows other nodes (check with CLUSTER NODES) or contains some key in database 0
解决方法是删除生成的配置文件nodes.conf,删除后再执行./redis-trib.rb add-node指令
查看集群中的结点,刚添加的7008为7007的从节点:
4、删除节点
./redis-trib.rb del-node 127.0.0.1:7005 4b45eb75c8b428fbd77ab979b85080146a9bc017
删除已经占有hash槽的结点会失败,报错如下:
[ERR] Node 127.0.0.1:7005 is not empty! Reshard data away and try again.
需要将该结点占用的hash槽分配出去(参考hash槽重新分配章节)。
// 连接redis集群
@Test
public void testJedisCluster() {
JedisPoolConfig config = new JedisPoolConfig();
// 最大连接数
config.setMaxTotal(30);
// 最大连接空闲数
config.setMaxIdle(2);
//集群结点
Set<HostAndPort> jedisClusterNode = new HashSet<HostAndPort>();
jedisClusterNode.add(new HostAndPort("192.168.101.3", 7001));
jedisClusterNode.add(new HostAndPort("192.168.101.3", 7002));
jedisClusterNode.add(new HostAndPort("192.168.101.3", 7003));
jedisClusterNode.add(new HostAndPort("192.168.101.3", 7004));
jedisClusterNode.add(new HostAndPort("192.168.101.3", 7005));
jedisClusterNode.add(new HostAndPort("192.168.101.3", 7006));
JedisCluster jc = new JedisCluster(jedisClusterNode, config);
JedisCluster jcd = new JedisCluster(jedisClusterNode);
jcd.set("name", "zhangsan");
String value = jcd.get("name");
System.out.println(value);
}
/Users/sherry/WorkPath/Git/Web/redisDemo/src/main/java/org/zln/utils/JedisUtils.java
package org.zln.utils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
/**
* Created by sherry on 16/9/13.
*/
public class JedisUtils {
private static Logger logger = LogManager.getLogger();
private static JedisPool pool = null;
private static final String URL = "127.0.0.1";
private static final int PORT = 6379;
private static Jedis jedis = null;
static {
pool = new JedisPool(new JedisPoolConfig(), URL,PORT);
}
public static Jedis getRedisCon(String password){
jedis = pool.getResource();
if (StringUtils.isNotEmpty(password)){
logger.info("以密码:"+password+" 登陆 redis");
jedis.auth(password);
}
logger.info(jedis.ping());
return jedis;
}
public static void closeRedisCon(Jedis jedis){
if (jedis!=null){
jedis.close();
}
}
public static void closeApp(){
//关闭应用程序的时候执行
pool.destroy();
}
}
/Users/sherry/WorkPath/Git/Web/redisDemo/src/main/java/org/zln/main/JedisMain.java
package org.zln.main;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.zln.utils.JedisUtils;
import redis.clients.jedis.Jedis;
import java.io.*;
import java.util.*;
/**
* Created by sherry on 16/9/13.
*/
public class JedisMain {
private static Logger logger = LogManager.getLogger();
public static void main(String[] args) {
Jedis jedis = JedisUtils.getRedisCon("");
// stringTest(jedis);
//
// listTest(jedis);
//
// mapTest(jedis);
//
// objTest(jedis);
objListTest(jedis);
// 将这个Jedis实例归还给JedisPool。
JedisUtils.closeRedisCon(jedis);
}
/**
* 对象列表
* @param jedis
*/
private static void objListTest(Jedis jedis) {
Person person1 = new Person("name1",1);
Person person2 = new Person("name2",2);
Person person3 = new Person("name3",3);
Person person4 = new Person("name4",4);
List<Person> persons = new ArrayList<>();
persons.add(person1);
persons.add(person2);
persons.add(person3);
persons.add(person4);
try {
for (Person person:persons){
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(person);
byte [] byteArray = bos.toByteArray();
oos.close();
bos.close();
logger.info("写入对象:"+person);
jedis.lpush("persons".getBytes(),byteArray);
}
List<byte[]> personsBytes = jedis.lrange("persons".getBytes(),0,10);
for (byte[] bs:personsBytes){
ByteArrayInputStream bis = new ByteArrayInputStream(bs);
ObjectInputStream inputStream = new ObjectInputStream(bis);
Person readObject = (Person) inputStream.readObject();
logger.info( " 读取对象 \t" + readObject.toString());
inputStream.close();
bis.close();
}
} catch (IOException|ClassNotFoundException e) {
e.printStackTrace();
}
jedis.del("persons".getBytes());
}
/**
* 存储对象
* @param jedis
*/
private static void objTest(Jedis jedis) {
Person person = new Person();
person.setAge(27);
person.setName("卡卡卡");
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(person);
byte [] byteArray = bos.toByteArray();
oos.close();
bos.close();
String setObjectRet = jedis.set(person.getName().getBytes(), byteArray);
logger.info( " set object return \t" + setObjectRet);
byte [] bs = jedis.get( person.getName().getBytes());
ByteArrayInputStream bis = new ByteArrayInputStream(bs);
ObjectInputStream inputStream = new ObjectInputStream(bis);
Person readObject = (Person) inputStream.readObject();
logger.info( " read object \t" + readObject.toString());
inputStream.close();
bis.close();
} catch (IOException|ClassNotFoundException e) {
e.printStackTrace();
}
jedis.del(person.getName());
}
/**
* 存储 Map
* @param jedis
*/
private static void mapTest(Jedis jedis) {
Map<String,String> user = new HashMap<String,String>();
user.put( "name" , "cd" );
user.put( "password" , "123456" );
jedis.hmset("user",user);
// mapkey 个数
System.out.println(String.format ( "len:%d" , jedis.hlen( "user" )));
//map中的所有键值
System.out.println(String.format ( "keys: %s" , jedis.hkeys( "user" ) ));
//map中的所有value
System.out.println(String.format ( "values: %s" , jedis.hvals( "user" ) ));
//取出map中的name字段值
List<String> rsmap = jedis.hmget( "user" , "name" , "password" );
System.out.println(rsmap);
//删除map中的某一个键值 password
jedis.hdel( "user" , "password" );
System.out.println(jedis.hmget( "user" , "name" , "password" ));
jedis.del("user");
}
/**
* 存储列表
* @param jedis
*/
private static void listTest(Jedis jedis) {
jedis.lpush("tutorial-list", "Redis");
jedis.lpush("tutorial-list", "Mongodb");
jedis.lpush("tutorial-list", "Mysql");
// 获取存储的数据并输出
List<String> list = jedis.lrange("tutorial-list", 0 ,5);
for(int i=0; i<list.size(); i++) {
System.out.println("Stored string in redis:: "+list.get(i));
}
jedis.del("tutorial-list");
}
/**
* 存储字符串
* @param jedis
*/
private static void stringTest(Jedis jedis) {
//一次次添加
jedis.set("string1","一号字符串");
jedis.set("string2","二号字符串");
jedis.set("string3","三号字符串");
jedis.set("string4","四号字符串");
logger.info("获取string1:"+jedis.get("string1"));
//在已有 key 上,对 value 进行新增
jedis.append("string1","添加新字符串");
logger.info("获取string1:"+jedis.get("string1"));
//一次性添加多个键值对
jedis.mset("s1","v1","s2","v2","s3","v3");
logger.info("获取s1:"+jedis.get("s1"));
for (Iterator<String> iterator = jedis.keys("*").iterator();iterator.hasNext();){
String key = iterator.next();
logger.info(key+":"+jedis.get(key));
jedis.del(key);
}
}
}
pom构建:
[html] view plain copy
print?
1 <modelVersion>4.0.0</modelVersion>
2 <groupId>com.x.redis</groupId>
3 <artifactId>springredis</artifactId>
4 <version>0.0.1-SNAPSHOT</version>
5
6 <dependencies>
7 <dependency>
8 <groupId>org.springframework.data</groupId>
9 <artifactId>spring-data-redis</artifactId>
10 <version>1.0.2.RELEASE</version>
11 </dependency>
12 <dependency>
13 <groupId>org.springframework</groupId>
14 <artifactId>spring-test</artifactId>
15 <version>3.1.2.RELEASE</version>
16 <scope>test</scope>
17 </dependency>
18
19 <dependency>
20 <groupId>redis.clients</groupId>
21 <artifactId>jedis</artifactId>
22 <version>2.1.0</version>
23 </dependency>
24
25 <dependency>
26 <groupId>junit</groupId>
27 <artifactId>junit</artifactId>
28 <version>4.8.2</version>
29 <scope>test</scope>
30 </dependency>
31 </dependencies>
spring配置文件(applicationContext.xml):
[html] view plain copy
print?
1 <?xml version="1.0" encoding="UTF-8"?>
2 <beans xmlns="http://www.springframework.org/schema/beans"
3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
4 xmlns:context="http://www.springframework.org/schema/context"
5 xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
6 xmlns:aop="http://www.springframework.org/schema/aop"
7 xsi:schemaLocation="
8 http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
9 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
10
11 <context:property-placeholder location="classpath:redis.properties" />
12
13 <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
14 <property name="maxIdle" value="${redis.maxIdle}" />
15 <property name="maxActive" value="${redis.maxActive}" />
16 <property name="maxWait" value="${redis.maxWait}" />
17 <property name="testOnBorrow" value="${redis.testOnBorrow}" />
18 </bean>
19
20 <bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
21 p:host-name="${redis.host}" p:port="${redis.port}" p:password="${redis.pass}" p:pool-config-ref="poolConfig"/>
22
23 <bean id="redisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate">
24 <property name="connectionFactory" ref="connectionFactory" />
25 </bean>
26
27 <bean id="userDao" class="com.x.dao.impl.UserDao" />
28 </beans>
redis.properties
[html] view plain copy
print?
1 # Redis settings
2 redis.host=localhost
3 redis.port=6379
4 redis.pass=java2000_wl
5
6
7 redis.maxIdle=300
8 redis.maxActive=600
9 redis.maxWait=1000
10 redis.testOnBorrow=true
java代码:
[java] view plain copy
print?
1 package com.x.entity;
2
3 import java.io.Serializable;
4
5 /**
6 * @author http://blog.csdn.net/java2000_wl
7 * @version <b>1.0</b>
8 */
9 public class User implements Serializable {
10
11 private static final long serialVersionUID = -6011241820070393952L;
12
13 private String id;
14
15 private String name;
16
17 private String password;
18
19 /**
20 * <br>------------------------------<br>
21 */
22 public User() {
23
24 }
25
26 /**
27 * <br>------------------------------<br>
28 */
29 public User(String id, String name, String password) {
30 super();
31 this.id = id;
32 this.name = name;
33 this.password = password;
34 }
35
36 /**
37 * 获得id
38 * @return the id
39 */
40 public String getId() {
41 return id;
42 }
43
44 /**
45 * 设置id
46 * @param id the id to set
47 */
48 public void setId(String id) {
49 this.id = id;
50 }
51
52 /**
53 * 获得name
54 * @return the name
55 */
56 public String getName() {
57 return name;
58 }
59
60 /**
61 * 设置name
62 * @param name the name to set
63 */
64 public void setName(String name) {
65 this.name = name;
66 }
67
68 /**
69 * 获得password
70 * @return the password
71 */
72 public String getPassword() {
73 return password;
74 }
75
76 /**
77 * 设置password
78 * @param password the password to set
79 */
80 public void setPassword(String password) {
81 this.password = password;
82 }
83 }
[java] view plain copy
print?
1 package com.x.dao;
2
3 import org.springframework.beans.factory.annotation.Autowired;
4 import org.springframework.data.redis.core.RedisTemplate;
5 import org.springframework.data.redis.serializer.RedisSerializer;
6
7 /**
8 * AbstractBaseRedisDao
9 * @author http://blog.csdn.net/java2000_wl
10 * @version <b>1.0</b>
11 */
12 public abstract class AbstractBaseRedisDao<K, V> {
13
14 @Autowired
15 protected RedisTemplate<K, V> redisTemplate;
16
17 /**
18 * 设置redisTemplate
19 * @param redisTemplate the redisTemplate to set
20 */
21 public void setRedisTemplate(RedisTemplate<K, V> redisTemplate) {
22 this.redisTemplate = redisTemplate;
23 }
24
25 /**
26 * 获取 RedisSerializer
27 * <br>------------------------------<br>
28 */
29 protected RedisSerializer<String> getRedisSerializer() {
30 return redisTemplate.getStringSerializer();
31 }
32 }
[java] view plain copy
print?
1 package com.x.dao;
2
3 import java.util.List;
4
5 import com.x.entity.User;
6
7 /**
8 * @author http://blog.csdn.net/java2000_wl
9 * @version <b>1.0</b>
10 */
11 public interface IUserDao {
12
13 /**
14 * 新增
15 * <br>------------------------------<br>
16 * @param user
17 * @return
18 */
19 boolean add(User user);
20
21 /**
22 * 批量新增 使用pipeline方式
23 * <br>------------------------------<br>
24 * @param list
25 * @return
26 */
27 boolean add(List<User> list);
28
29 /**
30 * 删除
31 * <br>------------------------------<br>
32 * @param key
33 */
34 void delete(String key);
35
36 /**
37 * 删除多个
38 * <br>------------------------------<br>
39 * @param keys
40 */
41 void delete(List<String> keys);
42
43 /**
44 * 修改
45 * <br>------------------------------<br>
46 * @param user
47 * @return
48 */
49 boolean update(User user);
50
51 /**
52 * 通过key获取
53 * <br>------------------------------<br>
54 * @param keyId
55 * @return
56 */
57 User get(String keyId);
58 }
[java] view plain copy
print?
1 package com.x.dao.impl;
2
3 import java.util.ArrayList;
4 import java.util.List;
5
6 import org.springframework.dao.DataAccessException;
7 import org.springframework.data.redis.connection.RedisConnection;
8 import org.springframework.data.redis.core.RedisCallback;
9 import org.springframework.data.redis.serializer.RedisSerializer;
10 import org.springframework.util.Assert;
11
12 import com.x.dao.AbstractBaseRedisDao;
13 import com.x.dao.IUserDao;
14 import com.x.entity.User;
15
16 /**
17 * Dao
18 * @author http://blog.csdn.net/java2000_wl
19 * @version <b>1.0</b>
20 */
21 public class UserDao extends AbstractBaseRedisDao<String, User> implements IUserDao {
22
23 /**
24 * 新增
25 *<br>------------------------------<br>
26 * @param user
27 * @return
28 */
29 public boolean add(final User user) {
30 boolean result = redisTemplate.execute(new RedisCallback<Boolean>() {
31 public Boolean doInRedis(RedisConnection connection)
32 throws DataAccessException {
33 RedisSerializer<String> serializer = getRedisSerializer();
34 byte[] key = serializer.serialize(user.getId());
35 byte[] name = serializer.serialize(user.getName());
36 return connection.setNX(key, name);
37 }
38 });
39 return result;
40 }
41
42 /**
43 * 批量新增 使用pipeline方式
44 *<br>------------------------------<br>
45 *@param list
46 *@return
47 */
48 public boolean add(final List<User> list) {
49 Assert.notEmpty(list);
50 boolean result = redisTemplate.execute(new RedisCallback<Boolean>() {
51 public Boolean doInRedis(RedisConnection connection)
52 throws DataAccessException {
53 RedisSerializer<String> serializer = getRedisSerializer();
54 for (User user : list) {
55 byte[] key = serializer.serialize(user.getId());
56 byte[] name = serializer.serialize(user.getName());
57 connection.setNX(key, name);
58 }
59 return true;
60 }
61 }, false, true);
62 return result;
63 }
64
65 /**
66 * 删除
67 * <br>------------------------------<br>
68 * @param key
69 */
70 public void delete(String key) {
71 List<String> list = new ArrayList<String>();
72 list.add(key);
73 delete(list);
74 }
75
76 /**
77 * 删除多个
78 * <br>------------------------------<br>
79 * @param keys
80 */
81 public void delete(List<String> keys) {
82 redisTemplate.delete(keys);
83 }
84
85 /**
86 * 修改
87 * <br>------------------------------<br>
88 * @param user
89 * @return
90 */
91 public boolean update(final User user) {
92 String key = user.getId();
93 if (get(key) == null) {
94 throw new NullPointerException("数据行不存在, key = " + key);
95 }
96 boolean result = redisTemplate.execute(new RedisCallback<Boolean>() {
97 public Boolean doInRedis(RedisConnection connection)
98 throws DataAccessException {
99 RedisSerializer<String> serializer = getRedisSerializer();
100 byte[] key = serializer.serialize(user.getId());
101 byte[] name = serializer.serialize(user.getName());
102 connection.set(key, name);
103 return true;
104 }
105 });
106 return result;
107 }
108
109 /**
110 * 通过key获取
111 * <br>------------------------------<br>
112 * @param keyId
113 * @return
114 */
115 public User get(final String keyId) {
116 User result = redisTemplate.execute(new RedisCallback<User>() {
117 public User doInRedis(RedisConnection connection)
118 throws DataAccessException {
119 RedisSerializer<String> serializer = getRedisSerializer();
120 byte[] key = serializer.serialize(keyId);
121 byte[] value = connection.get(key);
122 if (value == null) {
123 return null;
124 }
125 String name = serializer.deserialize(value);
126 return new User(keyId, name, null);
127 }
128 });
129 return result;
130 }
131 }
[java] view plain copy
print?
1 import java.util.ArrayList;
2 import java.util.List;
3
4 import junit.framework.Assert;
5
6 import org.junit.Test;
7 import org.springframework.beans.factory.annotation.Autowired;
8 import org.springframework.test.context.ContextConfiguration;
9 import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;
10
11 import com.x.dao.IUserDao;
12 import com.x.entity.User;
13
14 /**
15 * 测试
16 * @author http://blog.csdn.net/java2000_wl
17 * @version <b>1.0</b>
18 */
19 @ContextConfiguration(locations = {"classpath*:applicationContext.xml"})
20 public class RedisTest extends AbstractJUnit4SpringContextTests {
21
22 @Autowired
23 private IUserDao userDao;
24
25 /**
26 * 新增
27 * <br>------------------------------<br>
28 */
29 @Test
30 public void testAddUser() {
31 User user = new User();
32 user.setId("user1");
33 user.setName("java2000_wl");
34 boolean result = userDao.add(user);
35 Assert.assertTrue(result);
36 }
37
38 /**
39 * 批量新增 普通方式
40 * <br>------------------------------<br>
41 */
42 @Test
43 public void testAddUsers1() {
44 List<User> list = new ArrayList<User>();
45 for (int i = 10; i < 50000; i++) {
46 User user = new User();
47 user.setId("user" + i);
48 user.setName("java2000_wl" + i);
49 list.add(user);
50 }
51 long begin = System.currentTimeMillis();
52 for (User user : list) {
53 userDao.add(user);
54 }
55 System.out.println(System.currentTimeMillis() - begin);
56 }
57
58 /**
59 * 批量新增 pipeline方式
60 * <br>------------------------------<br>
61 */
62 @Test
63 public void testAddUsers2() {
64 List<User> list = new ArrayList<User>();
65 for (int i = 10; i < 1500000; i++) {
66 User user = new User();
67 user.setId("user" + i);
68 user.setName("java2000_wl" + i);
69 list.add(user);
70 }
71 long begin = System.currentTimeMillis();
72 boolean result = userDao.add(list);
73 System.out.println(System.currentTimeMillis() - begin);
74 Assert.assertTrue(result);
75 }
76
77 /**
78 * 修改
79 * <br>------------------------------<br>
80 */
81 @Test
82 public void testUpdate() {
83 User user = new User();
84 user.setId("user1");
85 user.setName("new_password");
86 boolean result = userDao.update(user);
87 Assert.assertTrue(result);
88 }
89
90 /**
91 * 通过key删除单个
92 * <br>------------------------------<br>
93 */
94 @Test
95 public void testDelete() {
96 String key = "user1";
97 userDao.delete(key);
98 }
99
100 /**
101 * 批量删除
102 * <br>------------------------------<br>
103 */
104 @Test
105 public void testDeletes() {
106 List<String> list = new ArrayList<String>();
107 for (int i = 0; i < 10; i++) {
108 list.add("user" + i);
109 }
110 userDao.delete(list);
111 }
112
113 /**
114 * 获取
115 * <br>------------------------------<br>
116 */
117 @Test
118 public void testGetUser() {
119 String id = "user1";
120 User user = userDao.get(id);
121 Assert.assertNotNull(user);
122 Assert.assertEquals(user.getName(), "java2000_wl");
123 }
124
125 /**
126 * 设置userDao
127 * @param userDao the userDao to set
128 */
129 public void setUserDao(IUserDao userDao) {
130 this.userDao = userDao;
131 }
132 }
Redis是数据库,不是简单的缓存
见上
默认情况下安装完毕后,在/usr/local/bin/目录下和安装目录的src目录下,会产生几个命令文件,分别是
redis-server:Redis的服务器操作命令,用于Redis服务器的启停
redis-cli:Redis的客户端命令,用于连接Redis服务器,并进行各项操作
redis-benchmark:Redis的性能测试工具
redis-check-aof:检查aof日志的工具
redis-check-dump:检查rdb日志的工具
使用redis-server命令,指定一个redis.conf配置文件进行启动即可
如:redis-server redis.conf
使用redis-cli命令
例如:redis-cli -h ip -p port
如何直接 redis-cli,则默认连接127.0.0.1的6379端口
能够操作的数据类型包括 字符串、列表、集合、有序集合、哈希、位图、超重对数
Redis的键是二进制安全的,可以使用任何的二进制序列作为键
键的最大值是512MB
String(字符串)
string是redis最基本的类型,你可以理解成与Memcached一模一样的类型,一个key对应一个value。
string类型是二进制安全的。意思是redis的string可以包含任何数据。比如jpg图片或者序列化的对象 。
string类型是Redis最基本的数据类型,一个键最大能存储512MB。
实例
redis 127.0.0.1:6379> SET name "runoob"
OK
redis 127.0.0.1:6379> GET name
"runoob"
在以上实例中我们使用了 Redis 的 SET 和 GET 命令。键为 name,对应的值为 runoob。
注意:一个键最大能存储512MB。
Hash(哈希)
Redis hash 是一个键值对集合。
Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。
实例
127.0.0.1:6379> HMSET user:1 username runoob password runoob points 200
OK
127.0.0.1:6379> HGETALL user:1
1) "username"
2) "runoob"
3) "password"
4) "runoob"
5) "points"
6) "200"
以上实例中 hash 数据类型存储了包含用户脚本信息的用户对象。 实例中我们使用了 Redis HMSET, HGETALL 命令,user:1 为键值。
每个 hash 可以存储 232 -1 键值对(40多亿)。
List(列表)
Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。
实例
redis 127.0.0.1:6379> lpush runoob redis
(integer) 1
redis 127.0.0.1:6379> lpush runoob mongodb
(integer) 2
redis 127.0.0.1:6379> lpush runoob rabitmq
(integer) 3
redis 127.0.0.1:6379> lrange runoob 0 10
1) "rabitmq"
2) "mongodb"
3) "redis"
redis 127.0.0.1:6379>
列表最多可存储 232 - 1 元素 (4294967295, 每个列表可存储40多亿)。
Set(集合)
Redis的Set是string类型的无序集合。
集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。
sadd 命令
添加一个string元素到,key对应的set集合中,成功返回1,如果元素已经在集合中返回0,key对应的set不存在返回错误。
sadd key member
实例
redis 127.0.0.1:6379> sadd runoob redis
(integer) 1
redis 127.0.0.1:6379> sadd runoob mongodb
(integer) 1
redis 127.0.0.1:6379> sadd runoob rabitmq
(integer) 1
redis 127.0.0.1:6379> sadd runoob rabitmq
(integer) 0
redis 127.0.0.1:6379> smembers runoob
1) "rabitmq"
2) "mongodb"
3) "redis"
注意:以上实例中 rabitmq 添加了两次,但根据集合内元素的唯一性,第二次插入的元素将被忽略。
集合中最大的成员数为 232 - 1(4294967295, 每个集合可存储40多亿个成员)。
zset(sorted set:有序集合)
Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。
不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。
zset的成员是唯一的,但分数(score)却可以重复。
zadd 命令
添加元素到集合,元素在集合中存在则更新对应score
zadd key score member
实例
redis 127.0.0.1:6379> zadd runoob 0 redis
(integer) 1
redis 127.0.0.1:6379> zadd runoob 0 mongodb
(integer) 1
redis 127.0.0.1:6379> zadd runoob 0 rabitmq
(integer) 1
redis 127.0.0.1:6379> zadd runoob 0 rabitmq
(integer) 0
redis 127.0.0.1:6379> ZRANGEBYSCORE runoob 0 1000
1) "redis"
2) "mongodb"
3) "rabitmq"
序号 |
命令及描述 |
1 |
该命令用于在 key 存在时删除 key。 |
2 |
序列化给定 key ,并返回被序列化的值。 |
3 |
检查给定 key 是否存在。 |
4 |
EXPIRE key seconds 为给定 key 设置过期时间。 |
5 |
EXPIREAT 的作用和 EXPIRE 类似,都用于为 key 设置过期时间。 不同在于 EXPIREAT 命令接受的时间参数是 UNIX 时间戳(unix timestamp)。 |
6 |
设置 key 的过期时间以毫秒计。 |
7 |
PEXPIREAT key milliseconds-timestamp 设置 key 过期时间的时间戳(unix timestamp) 以毫秒计 |
8 |
查找所有符合给定模式( pattern)的 key 。 |
9 |
将当前数据库的 key 移动到给定的数据库 db 当中。 |
10 |
移除 key 的过期时间,key 将持久保持。 |
11 |
以毫秒为单位返回 key 的剩余的过期时间。 |
12 |
以秒为单位,返回给定 key 的剩余生存时间(TTL, time to live)。 |
13 |
从当前数据库中随机返回一个 key 。 |
14 |
修改 key 的名称 |
15 |
仅当 newkey 不存在时,将 key 改名为 newkey 。 |
16 |
返回 key 所储存的值的类型。 |
检查执行的key是否存在
127.0.0.1:6379> exists k1
(integer) 1
127.0.0.1:6379> exists k5
(integer) 0
删除指定的key
127.0.0.1:6379> del name
(integer) 1
返回值为删除的键的个数。如果被删除的键本身就不存在,就返回0
查询键的类型
127.0.0.1:6379> type k1
string
设置键的生存时间。单位是秒
超过我们的设置时间后,key会失效,等同于执行了del操作
127.0.0.1:6379> expire mk 5
(integer) 1
127.0.0.1:6379> get mk
"wahaha"
127.0.0.1:6379> get mk
(nil)
设置键的生存时间。单位是毫秒
超过我们的设置时间后,key会失效,等同于执行了del操作
移除key的过期时间设置。相当于此key会永久保持
127.0.0.1:6379> persist mk
(integer) 1
key的剩余生存时间,单位是秒
127.0.0.1:6379> ttl k4
(integer) 3498
key的剩余生存时间,单位是毫秒
127.0.0.1:6379> pttl k4
(integer) 3558506
有两种方法
1、找到对应的进程号,直接kill
2、使用redis-cli登陆后,执行shutdown命令
默认情况下启动的redis-server,并不是后台运行的
修改redis.conf文件的 daemonize yes
有两种方式
1、直接修改redis.conf 文件
2、使用CONFIG命令
CONFIG GET * ;获取当前设置的全部参数
CONFIG GET KEY ;获取指定key设置的值
CONFIG SET KEY VALUE ;设置某个参数
redis.conf 配置项说明如下:
1. Redis默认不是以守护进程的方式运行,可以通过该配置项修改,使用yes启用守护进程
daemonize no
2. 当Redis以守护进程方式运行时,Redis默认会把pid写入/var/run/redis.pid文件,可以通过pidfile指定
pidfile /var/run/redis.pid
3. 指定Redis监听端口,默认端口为6379,作者在自己的一篇博文中解释了为什么选用6379作为默认端口,因为6379在手机按键上MERZ对应的号码,而MERZ取自意大利歌女Alessia Merz的名字
port 6379
4. 绑定的主机地址
bind 127.0.0.1
5.当 客户端闲置多长时间后关闭连接,如果指定为0,表示关闭该功能
timeout 300
6. 指定日志记录级别,Redis总共支持四个级别:debug、verbose、notice、warning,默认为verbose
loglevel verbose
7. 日志记录方式,默认为标准输出,如果配置Redis为守护进程方式运行,而这里又配置为日志记录方式为标准输出,则日志将会发送给/dev/null
logfile stdout
8. 设置数据库的数量,默认数据库为0,可以使用SELECT <dbid>命令在连接上指定数据库id
databases 16
9. 指定在多长时间内,有多少次更新操作,就将数据同步到数据文件,可以多个条件配合
save <seconds> <changes>
Redis默认配置文件中提供了三个条件:
save 900 1
save 300 10
save 60 10000
分别表示900秒(15分钟)内有1个更改,300秒(5分钟)内有10个更改以及60秒内有10000个更改。
10. 指定存储至本地数据库时是否压缩数据,默认为yes,Redis采用LZF压缩,如果为了节省CPU时间,可以关闭该选项,但会导致数据库文件变的巨大
rdbcompression yes
11. 指定本地数据库文件名,默认值为dump.rdb
dbfilename dump.rdb
12. 指定本地数据库存放目录
dir ./
13. 设置当本机为slav服务时,设置master服务的IP地址及端口,在Redis启动时,它会自动从master进行数据同步
slaveof <masterip> <masterport>
14. 当master服务设置了密码保护时,slav服务连接master的密码
masterauth <master-password>
15. 设置Redis连接密码,如果配置了连接密码,客户端在连接Redis时需要通过AUTH <password>命令提供密码,默认关闭
requirepass foobared
16. 设置同一时间最大客户端连接数,默认无限制,Redis可以同时打开的客户端连接数为Redis进程可以打开的最大文件描述符数,如果设置 maxclients 0,表示不作限制。当客户端连接数到达限制时,Redis会关闭新的连接并向客户端返回max number of clients reached错误信息
maxclients 128
17. 指定Redis最大内存限制,Redis在启动时会把数据加载到内存中,达到最大内存后,Redis会先尝试清除已到期或即将到期的Key,当此方法处理 后,仍然到达最大内存设置,将无法再进行写入操作,但仍然可以进行读取操作。Redis新的vm机制,会把Key存放内存,Value会存放在swap区
maxmemory <bytes>
18. 指定是否在每次更新操作后进行日志记录,Redis在默认情况下是异步的把数据写入磁盘,如果不开启,可能会在断电时导致一段时间内的数据丢失。因为 redis本身同步数据文件是按上面save条件来同步的,所以有的数据会在一段时间内只存在于内存中。默认为no
appendonly no
19. 指定更新日志文件名,默认为appendonly.aof
appendfilename appendonly.aof
20. 指定更新日志条件,共有3个可选值:
no:表示等操作系统进行数据缓存同步到磁盘(快)
always:表示每次更新操作后手动调用fsync()将数据写到磁盘(慢,安全)
everysec:表示每秒同步一次(折衷,默认值)
appendfsync everysec
21. 指定是否启用虚拟内存机制,默认值为no,简单的介绍一下,VM机制将数据分页存放,由Redis将访问量较少的页即冷数据swap到磁盘上,访问多的页面由磁盘自动换出到内存中(在后面的文章我会仔细分析Redis的VM机制)
vm-enabled no
22. 虚拟内存文件路径,默认值为/tmp/redis.swap,不可多个Redis实例共享
vm-swap-file /tmp/redis.swap
23. 将所有大于vm-max-memory的数据存入虚拟内存,无论vm-max-memory设置多小,所有索引数据都是内存存储的(Redis的索引数据 就是keys),也就是说,当vm-max-memory设置为0的时候,其实是所有value都存在于磁盘。默认值为0
vm-max-memory 0
24. Redis swap文件分成了很多的page,一个对象可以保存在多个page上面,但一个page上不能被多个对象共享,vm-page-size是要根据存储的 数据大小来设定的,作者建议如果存储很多小对象,page大小最好设置为32或者64bytes;如果存储很大大对象,则可以使用更大的page,如果不 确定,就使用默认值
vm-page-size 32
25. 设置swap文件中的page数量,由于页表(一种表示页面空闲或使用的bitmap)是在放在内存中的,,在磁盘上每8个pages将消耗1byte的内存。
vm-pages 134217728
26. 设置访问swap文件的线程数,最好不要超过机器的核数,如果设置为0,那么所有对swap文件的操作都是串行的,可能会造成比较长时间的延迟。默认值为4
vm-max-threads 4
27. 设置在向客户端应答时,是否把较小的包合并为一个包发送,默认为开启
glueoutputbuf yes
28. 指定在超过一定的数量或者最大的元素超过某一临界值时,采用一种特殊的哈希算法
hash-max-zipmap-entries 64
hash-max-zipmap-value 512
29. 指定是否激活重置哈希,默认为开启(后面在介绍Redis的哈希算法时具体介绍)
activerehashing yes
30. 指定包含其它的配置文件,可以在同一主机上多个Redis实例之间使用同一份配置文件,而同时各个实例又拥有自己的特定配置文件
include /path/to/local.conf
序号 |
命令及描述 |
1 |
设置指定 key 的值 |
2 |
获取指定 key 的值。 |
3 |
返回 key 中字符串值的子字符 |
4 |
将给定 key 的值设为 value ,并返回 key 的旧值(old value)。 |
5 |
对 key 所储存的字符串值,获取指定偏移量上的位(bit)。 |
6 |
获取所有(一个或多个)给定 key 的值。 |
7 |
对 key 所储存的字符串值,设置或清除指定偏移量上的位(bit)。 |
8 |
将值 value 关联到 key ,并将 key 的过期时间设为 seconds (以秒为单位)。 |
9 |
只有在 key 不存在时设置 key 的值。 |
10 |
用 value 参数覆写给定 key 所储存的字符串值,从偏移量 offset 开始。 |
11 |
返回 key 所储存的字符串值的长度。 |
12 |
MSET key value [key value ...] 同时设置一个或多个 key-value 对。 |
13 |
MSETNX key value [key value ...] 同时设置一个或多个 key-value 对,当且仅当所有给定 key 都不存在。 |
14 |
这个命令和 SETEX 命令相似,但它以毫秒为单位设置 key 的生存时间,而不是像 SETEX 命令那样,以秒为单位。 |
15 |
将 key 中储存的数字值增一。 |
16 |
将 key 所储存的值加上给定的增量值(increment) 。 |
17 |
将 key 所储存的值加上给定的浮点增量值(increment) 。 |
18 |
将 key 中储存的数字值减一。 |
19 |
key 所储存的值减去给定的减量值(decrement) 。 |
20 |
如果 key 已经存在并且是一个字符串, APPEND 命令将 value 追加到 key 原来的值的末尾。 |
Redis中的字符串是二进制安全的
127.0.0.1:6379> set mk wahaha
OK
如果key不存在,会创建,如果key存在,会覆盖
127.0.0.1:6379> set mk mkv nx
(nil)
使用 nx 参数,如果key已经存在,就会set失败
127.0.0.1:6379> set mnk kk xx
(nil)
使用 xx 参数,如果key不存在,就会set失败
127.0.0.1:6379> set k4 v4 ex 3600
OK
可以在设置的时候指定key的生存时间,ex 单位是秒
127.0.0.1:6379> get mk
"wahaha"
对于value是整型数据的值进行自增
127.0.0.1:6379> set seq 1
OK
127.0.0.1:6379> incr seq
(integer) 2
127.0.0.1:6379> get seq
"2"
127.0.0.1:6379> incr seq
(integer) 3
127.0.0.1:6379> get seq
"3"
incr命令是原子的,也就是说,是多线程安全的。这个和Oracle中的序列的概念有点像
同时设置多个k-v
127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3
OK
同时获取多个key的value
127.0.0.1:6379> mget k1 k2 k3
1) "v1"
2) "v2"
3) "v3"
设置一个值,同时返回原先的值
127.0.0.1:6379> get k1
"v1"
127.0.0.1:6379> getset k1 v2
"v1"
127.0.0.1:6379> get k1
"v2"
序号 |
命令及描述 |
1 |
删除一个或多个哈希表字段 |
2 |
查看哈希表 key 中,指定的字段是否存在。 |
3 |
获取存储在哈希表中指定字段的值。 |
4 |
获取在哈希表中指定 key 的所有字段和值 |
5 |
为哈希表 key 中的指定字段的整数值加上增量 increment 。 |
6 |
HINCRBYFLOAT key field increment 为哈希表 key 中的指定字段的浮点数值加上增量 increment 。 |
7 |
获取所有哈希表中的字段 |
8 |
获取哈希表中字段的数量 |
9 |
获取所有给定字段的值 |
10 |
HMSET key field1 value1 [field2 value2 ] 同时将多个 field-value (域-值)对设置到哈希表 key 中。 |
11 |
将哈希表 key 中的字段 field 的值设为 value 。 |
12 |
只有在字段 field 不存在时,设置哈希表字段的值。 |
13 |
获取哈希表中所有值 |
14 |
HSCAN key cursor [MATCH pattern] [COUNT count] 迭代哈希表中的键值对。 |
添加/创建一个map类型的数据
127.0.0.1:6379> hmset person:zln name zln age 26
OK
其中 person:zln 是key。关于Map类型的Key,一般会使用:或-连接表达含义
获取哈希对象中某个字段的值
127.0.0.1:6379> hget person:zln name
"zln"
获取哈希对象中多个字段的值
127.0.0.1:6379> hmget person:zln name age
1) "zln"
2) "26"
获取哈希对象全部字段的值-包括字段名
127.0.0.1:6379> hgetall person:zln
1) "name"
2) "zln"
3) "age"
4) "26"
序号 |
命令及描述 |
1 |
移出并获取列表的第一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。 |
2 |
移出并获取列表的最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。 |
3 |
BRPOPLPUSH source destination timeout 从列表中弹出一个值,将弹出的元素插入到另外一个列表中并返回它; 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。 |
4 |
通过索引获取列表中的元素 |
5 |
LINSERT key BEFORE|AFTER pivot value 在列表的元素前或者后插入元素 |
6 |
获取列表长度 |
7 |
移出并获取列表的第一个元素 |
8 |
将一个或多个值插入到列表头部 |
9 |
将一个或多个值插入到已存在的列表头部 |
10 |
获取列表指定范围内的元素 |
11 |
移除列表元素 |
12 |
通过索引设置列表元素的值 |
13 |
对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。 |
14 |
移除并获取列表最后一个元素 |
15 |
移除列表的最后一个元素,并将该元素添加到另一个列表并返回 |
16 |
在列表中添加一个或多个值 |
17 |
为已存在的列表添加值 |
往列表的左侧添加数据
127.0.0.1:6379> lpush list1 lv1
(integer) 1
可以一次添加多个数据
lpush listKey v1 v2 v3 ...
如果列表不存在,会自动创建
往列表的右侧添加数据
127.0.0.1:6379> rpush list1 lv3
(integer) 3
可以一次添加多个数据
rpush listKey v1 v2 v3 ...
返回列表索引范围内的子集
127.0.0.1:6379> lrange list1 0 -1
1) "lv2"
2) "lv1"
3) "lv3"
-1表示倒数第一个数据
返回最右侧的元素,并将此元素从列表中删除
127.0.0.1:6379> lrange list1 0 -1
1) "lv2"
2) "lv1"
3) "lv3"
127.0.0.1:6379> rpop list1
"lv3"
127.0.0.1:6379> lrange list1 0 -1
1) "lv2"
2) "lv1"
当弹出最后一个元素后,列表也就不存在了
返回最左侧的元素,并将次元素从列表中删除
127.0.0.1:6379> lrange list1 0 -1
1) "lv2"
2) "lv1"
127.0.0.1:6379> lpop list1
"lv2"
127.0.0.1:6379> lpop list1
"lv1"
截取指定范围内的列表
127.0.0.1:6379> ltrim list1 4 5
OK
1、记录社交网络中用户最近提交的更新
2、设计生产者消费者模式,比如生产者使用rpush,消费者使用lpop。不过我觉得,消费者一般使用blpop key timeout比较好,当获取不到数据的时候,等待直到超时。
序号 |
命令及描述 |
1 |
向集合添加一个或多个成员 |
2 |
获取集合的成员数 |
3 |
返回给定所有集合的差集 |
4 |
SDIFFSTORE destination key1 [key2] 返回给定所有集合的差集并存储在 destination 中 |
5 |
返回给定所有集合的交集 |
6 |
SINTERSTORE destination key1 [key2] 返回给定所有集合的交集并存储在 destination 中 |
7 |
判断 member 元素是否是集合 key 的成员 |
8 |
返回集合中的所有成员 |
9 |
SMOVE source destination member 将 member 元素从 source 集合移动到 destination 集合 |
10 |
移除并返回集合中的一个随机元素 |
11 |
返回集合中一个或多个随机数 |
12 |
移除集合中一个或多个成员 |
13 |
返回所有给定集合的并集 |
14 |
SUNIONSTORE destination key1 [key2] 所有给定集合的并集存储在 destination 集合中 |
15 |
SSCAN key cursor [MATCH pattern] [COUNT count] 迭代集合中的元素 |
元素添加
127.0.0.1:6379> sadd sk 1
(integer) 1
查看当前Set的全部元素
127.0.0.1:6379> smembers sk
1) "1"
测试某个元素是否真实存在
127.0.0.1:6379> sismember sk 1
(integer) 1
127.0.0.1:6379> sismember sk 2
(integer) 0
其它命令注意是用来进行集合间操作的,详见上面的命令列表
序号 |
命令及描述 |
1 |
ZADD key score1 member1 [score2 member2] 向有序集合添加一个或多个成员,或者更新已存在成员的分数 |
2 |
获取有序集合的成员数 |
3 |
计算在有序集合中指定区间分数的成员数 |
4 |
有序集合中对指定成员的分数加上增量 increment |
5 |
ZINTERSTORE destination numkeys key [key ...] 计算给定的一个或多个有序集的交集并将结果集存储在新的有序集合 key 中 |
6 |
在有序集合中计算指定字典区间内成员数量 |
7 |
ZRANGE key start stop [WITHSCORES] 通过索引区间返回有序集合成指定区间内的成员 |
8 |
ZRANGEBYLEX key min max [LIMIT offset count] 通过字典区间返回有序集合的成员 |
9 |
ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT] 通过分数返回有序集合指定区间内的成员 |
10 |
返回有序集合中指定成员的索引 |
11 |
移除有序集合中的一个或多个成员 |
12 |
移除有序集合中给定的字典区间的所有成员 |
13 |
ZREMRANGEBYRANK key start stop 移除有序集合中给定的排名区间的所有成员 |
14 |
移除有序集合中给定的分数区间的所有成员 |
15 |
ZREVRANGE key start stop [WITHSCORES] 返回有序集中指定区间内的成员,通过索引,分数从高到底 |
16 |
ZREVRANGEBYSCORE key max min [WITHSCORES] 返回有序集中指定分数区间内的成员,分数从高到低排序 |
17 |
返回有序集合中指定成员的排名,有序集成员按分数值递减(从大到小)排序 |
18 |
返回有序集中,成员的分数值 |
19 |
ZUNIONSTORE destination numkeys key [key ...] 计算给定的一个或多个有序集的并集,并存储在新的 key 中 |
20 |
ZSCAN key cursor [MATCH pattern] [COUNT count] 迭代有序集合中的元素(包括元素成员和元素分值) |
Redis 在 2.8.9 版本添加了 HyperLogLog 结构。
Redis HyperLogLog 是用来做基数统计的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定的、并且是很小的。
在 Redis 里面,每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基 数。这和计算基数时,元素越多耗费内存就越多的集合形成鲜明对比。
但是,因为 HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素本身,所以 HyperLogLog 不能像集合那样,返回输入的各个元素。
什么是基数?
比如数据集 {1, 3, 5, 7, 5, 7, 8}, 那么这个数据集的基数集为 {1, 3, 5 ,7, 8}, 基数(不重复元素)为5。 基数估计就是在误差可接受的范围内,快速计算基数。
实例
以下实例演示了 HyperLogLog 的工作过程:
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 命令
下表列出了 redis HyperLogLog 的基本命令:
序号 |
命令及描述 |
1 |
PFADD key element [element ...] 添加指定元素到 HyperLogLog 中。 |
2 |
返回给定 HyperLogLog 的基数估算值。 |
3 |
PFMERGE destkey sourcekey [sourcekey ...] 将多个 HyperLogLog 合并为一个 HyperLogLog |
Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。
Redis 客户端可以订阅任意数量的频道。
下图展示了频道 channel1 , 以及订阅这个频道的三个客户端 —— client2 、 client5 和 client1 之间的关系:
当有新消息通过 PUBLISH 命令发送给频道 channel1 时, 这个消息就会被发送给订阅它的三个客户端:
以下实例演示了发布订阅是如何工作的。在我们实例中我们创建了订阅频道名为 redisChat:
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
# 订阅者的客户端会显示如下消息
1) "message"
2) "redisChat"
3) "Redis is a great caching technique"
1) "message"
2) "redisChat"
3) "Learn redis by runoob.com"
序号 |
命令及描述 |
1 |
PSUBSCRIBE pattern [pattern ...] 订阅一个或多个符合给定模式的频道。 |
2 |
PUBSUB subcommand [argument [argument ...]] 查看订阅与发布系统状态。 |
3 |
将信息发送到指定的频道。 |
4 |
PUNSUBSCRIBE [pattern [pattern ...]] 退订所有给定模式的频道。 |
5 |
SUBSCRIBE channel [channel ...] 订阅给定的一个或多个频道的信息。 |
6 |
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"
序号 |
命令及描述 |
1 |
取消事务,放弃执行事务块内的所有命令。 |
2 |
执行所有事务块内的命令。 |
3 |
标记一个事务块的开始。 |
4 |
取消 WATCH 命令对所有 key 的监视。 |
5 |
监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。 |
Redis 脚本使用 Lua 解释器来执行脚本。 Reids 2.6 版本通过内嵌支持 Lua 环境。执行脚本的常用命令为 EVAL。
语法
Eval 命令的基本语法如下:
redis 127.0.0.1:6379> EVAL script numkeys key [key ...] arg [arg ...]
实例
以下实例演示了 redis 脚本工作过程:
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 脚本命令
下表列出了 redis 脚本常用命令:
序号 |
命令及描述 |
1 |
EVAL script numkeys key [key ...] arg [arg ...] 执行 Lua 脚本。 |
2 |
EVALSHA sha1 numkeys key [key ...] arg [arg ...] 执行 Lua 脚本。 |
3 |
SCRIPT EXISTS script [script ...] 查看指定的脚本是否已经被保存在缓存当中。 |
4 |
从脚本缓存中移除所有脚本。 |
5 |
杀死当前正在运行的 Lua 脚本。 |
6 |
将脚本 script 添加到脚本缓存中,但并不立即执行这个脚本。 |
序号 |
命令及描述 |
1 |
异步执行一个 AOF(AppendOnly File) 文件重写操作 |
2 |
在后台异步保存当前数据库的数据到磁盘 |
3 |
CLIENT KILL [ip:port] [ID client-id] 关闭客户端连接 |
4 |
获取连接到服务器的客户端连接列表 |
5 |
获取连接的名称 |
6 |
在指定时间内终止运行来自客户端的命令 |
7 |
CLIENT SETNAME connection-name 设置当前连接的名称 |
8 |
获取集群节点的映射数组 |
9 |
获取 Redis 命令详情数组 |
10 |
获取 Redis 命令总数 |
11 |
获取给定命令的所有键 |
12 |
返回当前服务器时间 |
13 |
COMMAND INFO command-name [command-name ...] 获取指定 Redis 命令描述的数组 |
14 |
获取指定配置参数的值 |
15 |
对启动 Redis 服务器时所指定的 redis.conf 配置文件进行改写 |
16 |
修改 redis 配置参数,无需重启 |
17 |
重置 INFO 命令中的某些统计数据 |
18 |
返回当前数据库的 key 的数量 |
19 |
获取 key 的调试信息 |
20 |
让 Redis 服务崩溃 |
21 |
删除所有数据库的所有key |
22 |
删除当前数据库的所有key |
23 |
获取 Redis 服务器的各种信息和统计数值 |
24 |
返回最近一次 Redis 成功将数据保存到磁盘上的时间,以 UNIX 时间戳格式表示 |
25 |
实时打印出 Redis 服务器接收到的命令,调试用 |
26 |
返回主从实例所属的角色 |
27 |
异步保存数据到硬盘 |
28 |
异步保存数据到硬盘,并关闭服务器 |
29 |
将当前服务器转变为指定服务器的从属服务器(slave server) |
30 |
管理 redis 的慢日志 |
31 |
用于复制功能(replication)的内部命令 |
Redis SAVE 命令用于创建当前数据库的备份。
语法
redis Save 命令基本语法如下:
redis 127.0.0.1:6379> SAVE
实例
redis 127.0.0.1:6379> SAVE
OK
该命令将在 redis 安装目录中创建dump.rdb文件。
恢复数据
如果需要恢复数据,只需将备份文件 (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 服务更安全。
实例
我们可以通过以下命令查看是否设置了密码验证:
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 服务就需要密码验证,否则无法执行命令。
语法
AUTH 命令基本语法格式如下:
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]
实例
以下实例同时执行 10000 个请求来检测性能:
redis-benchmark -n 10000
PING_INLINE: 141043.72 requests per second
PING_BULK: 142857.14 requests per second
SET: 141442.72 requests per second
GET: 145348.83 requests per second
INCR: 137362.64 requests per second
LPUSH: 145348.83 requests per second
LPOP: 146198.83 requests per second
SADD: 146198.83 requests per second
SPOP: 149253.73 requests per second
LPUSH (needed to benchmark LRANGE): 148588.42 requests per second
LRANGE_100 (first 100 elements): 58411.21 requests per second
LRANGE_300 (first 300 elements): 21195.42 requests per second
LRANGE_500 (first 450 elements): 14539.11 requests per second
LRANGE_600 (first 600 elements): 10504.20 requests per second
MSET (10 keys): 93283.58 requests per second
redis 性能测试工具可选参数如下所示:
序号 |
选项 |
描述 |
默认值 |
1 |
-h |
指定服务器主机名 |
127.0.0.1 |
2 |
-p |
指定服务器端口 |
6379 |
3 |
-s |
指定服务器 socket |
|
4 |
-c |
指定并发连接数 |
50 |
5 |
-n |
指定请求数 |
10000 |
6 |
-d |
以字节的形式指定 SET/GET 值的数据大小 |
2 |
7 |
-k |
1=keep alive 0=reconnect |
1 |
8 |
-r |
SET/GET/INCR 使用随机 key, SADD 使用随机值 |
|
9 |
-P |
通过管道传输 <numreq> 请求 |
1 |
10 |
-q |
强制退出 redis。仅显示 query/sec 值 |
|
11 |
--csv |
以 CSV 格式输出 |
|
12 |
-l |
生成循环,永久执行测试 |
|
13 |
-t |
仅运行以逗号分隔的测试命令列表。 |
|
14 |
-I |
Idle 模式。仅打开 N 个 idle 连接并等待。 |
|
实例
以下实例我们使用了多个参数来测试 redis 性能:
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 通过监听一个 TCP 端口或者 Unix socket 的方式来接收来自客户端的连接,当一个连接建立后,Redis 内部会进行以下一些操作:
32 首先,客户端 socket 会被设置为非阻塞模式,因为 Redis 在网络事件处理上采用的是非阻塞多路复用模型。
33 然后为这个 socket 设置 TCP_NODELAY 属性,禁用 Nagle 算法
34 然后创建一个可读的文件事件用于监听这个客户端 socket 的数据发送
最大连接数
在 Redis2.4 中,最大连接数是被直接硬编码在代码里面的,而在2.6版本中这个值变成可配置的。
maxclients 的默认值是 10000,你也可以在 redis.conf 中对这个值进行修改。
config get maxclients
1) "maxclients"
2) "10000"
实例
以下实例我们在服务启动时设置最大连接数为 100000:
redis-server --maxclients 100000
客户端命令
S.N. |
命令 |
描述 |
1 |
CLIENT LIST |
返回连接到 redis 服务的客户端列表 |
2 |
CLIENT SETNAME |
设置当前连接的名称 |
3 |
CLIENT GETNAME |
获取通过 CLIENT SETNAME 命令设置的服务名称 |
4 |
CLIENT PAUSE |
挂起客户端连接,指定挂起的时间以毫秒计 |
5 |
CLIENT KILL |
关闭客户端连接 |
Redis是一种基于客户端-服务端模型以及请求/响应协议的TCP服务。这意味着通常情况下一个请求会遵循以下步骤:
35 客户端向服务端发送一个查询请求,并监听Socket返回,通常是以阻塞模式,等待服务端响应。
36 服务端处理命令,并将结果返回给客户端。
Redis 管道技术
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系统上执行上面这个简单脚本的数据表明,开启了管道操作后,往返时延已经被改善得相当低了。
without pipelining 1.185238 seconds
with pipelining 0.250783 seconds
如你所见,开启管道后,我们的速度效率提升了5倍。
分区是分割数据到多个Redis实例的处理过程,因此每个实例只保存key的一个子集。
分区的优势
通过利用多台计算机内存的和值,允许我们构造更大的数据库。
通过多核和多台计算机,允许我们扩展计算能力;通过多台计算机和网络适配器,允许我们扩展网络带宽。
分区的不足
redis的一些特性在分区方面表现的不是很好:
涉及多个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实例中的一个了。93024922 % 4 = 2,就是说key foobar应该被存到R2实例中。注意:取模操作是取除的余数,通常在多种编程语言中用%操作符实现。
开始在 Java 中使用 Redis 前, 我们需要确保已经安装了 redis 服务及 Java redis 驱动,且你的机器上能正常使用 Java。 Java的安装配置可以参考我们的 Java开发环境配置 接下来让我们安装 Java redis 驱动:
首先你需要下载驱动包,下载 jedis.jar,确保下载最新驱动包。
在你的classpath中包含该驱动包。
连接到 redis 服务
import redis.clients.jedis.Jedis;
public class RedisJava {
public static void main(String[] args) {
//连接本地的 Redis 服务
Jedis jedis = new Jedis("localhost");
System.out.println("Connection to server sucessfully");
//查看服务是否运行
System.out.println("Server is running: "+jedis.ping());
}
}
编译以上 Java 程序,确保驱动包的路径是正确的。
$javac RedisJava.java
$java RedisJava
Connection to server sucessfully
Server is running: PONG
Redis Java String Example
Redis Java String(字符串) 实例
import redis.clients.jedis.Jedis;
public class RedisStringJava {
public static void main(String[] args) {
//连接本地的 Redis 服务
Jedis jedis = new Jedis("localhost");
System.out.println("Connection to server sucessfully");
//设置 redis 字符串数据
jedis.set("runoobkey", "Redis tutorial");
// 获取存储的数据并输出
System.out.println("Stored string in redis:: "+ jedis.get("runoobkey"));
}
}
编译以上程序。
$javac RedisStringJava.java
$java RedisStringJava
Connection to server sucessfully
Stored string in redis:: Redis tutorial
Redis Java List(列表) 实例
import redis.clients.jedis.Jedis;
public class RedisListJava {
public static void main(String[] args) {
//连接本地的 Redis 服务
Jedis jedis = new Jedis("localhost");
System.out.println("Connection to server sucessfully");
//存储数据到列表中
jedis.lpush("tutorial-list", "Redis");
jedis.lpush("tutorial-list", "Mongodb");
jedis.lpush("tutorial-list", "Mysql");
// 获取存储的数据并输出
List<String> list = jedis.lrange("tutorial-list", 0 ,5);
for(int i=0; i<list.size(); i++) {
System.out.println("Stored string in redis:: "+list.get(i));
}
}
}
编译以上程序。
$javac RedisListJava.java
$java RedisListJava
Connection to server sucessfully
Stored string in redis:: Redis
Stored string in redis:: Mongodb
Stored string in redis:: Mysql
Redis Java Keys 实例
import redis.clients.jedis.Jedis;
public class RedisKeyJava {
public static void main(String[] args) {
//连接本地的 Redis 服务
Jedis jedis = new Jedis("localhost");
System.out.println("Connection to server sucessfully");
// 获取数据并输出
List<String> list = jedis.keys("*");
for(int i=0; i<list.size(); i++) {
System.out.println("List of stored keys:: "+list.get(i));
}
}
}
编译以上程序。
$javac RedisKeyJava.java
$java RedisKeyJava
Connection to server sucessfully
List of stored keys:: tutorial-name
List of stored keys:: tutorial-list
配置缓存大大小
在redis.conf 中 maxmemory 100mb
默认情况下,64位操作系统为0,即没有限制,32位操作系统使用3G作为隐含的内存大小限制
当内存到达指定大小后,需要选择不同的行为--->策略
由 maxmemory-policy 参数配置
以下策略可用:
noeviction:当到达内存限制时返回错误。当客户端尝试执行命令时会导致更多内存占用(大多数写命令,除了 DEL 和一些例外)。
allkeys-lru:回收最近最少使用(LRU)的键,为新数据腾出空间。
volatile-lru:回收最近最少使用(LRU)的键,但是只回收有设置过期的键,为新数据腾出空间。
allkeys-random:回收随机的键,为新数据腾出空间。
volatile-random:回收随机的键,但是只回收有设置过期的键,为新数据腾出空间。
volatile-ttl:回收有设置过期的键,尝试先回收离 TTL 最短时间的键,为新数据腾出空间。
当没有满足前提条件的话,volatile-lru,volatile-random 和 volatile-ttl 策略就表现得和 noeviction 一样了。
127.0.0.1:6379> config get maxmemory-samples
1) "maxmemory-samples"
2) "5"
配置Redis 的 LRU算法的精度,1~10
Redis的分片技术,允许在分布式环境中,一个键的所有值分布在不同的机器上。
分片技术突破了Redis能够承受的数据量,否则数据量受限于单机内存大小
计算能力也大大提升
标签:还需要 多线程 工作过程 集中 publish 日志记录 cut 原子操作 comm
原文地址:http://www.cnblogs.com/sherrykid/p/6224581.html