Redis系列之(一)——开发基础

一、安装与配置

#将Redis的相关运行文件放到/usr/local/bin/下,这样就可以在任意目录下执行Redis的命令
#启动
redis-server /opt/redis/redis.conf
#命令行客户端
redis-cli -h 127.0.0.1 -p 6379
#停止服务,nosave|save参数表示是否关闭前生成持久化文件
redis-cli shutdown nosave|save
image.png

在配置文件redis.conf中,默认的bind 接口是127.0.0.1,也就是本地回环地址。
这样的话,访问redis服务只能通过本机的客户端连接,而无法通过远程连接,
这样可以避免将redis服务暴露于危险的网络环境中,防止一些不安全的人随随便便通过远程
连接到redis服务。如果bind选项为空的话,那会接受所有来自于可用网络接口的连接。


#bind
#bind 127.0.0.1
#protected-mode
protected-mode no

处理异常
Just disable protected mode sending the command 'CONFIG SET protected-mode no' from the loopback interface by connecting to Redis from the same host the server is running, however MAKE SURE Redis is not publicly accessible from internet if you do so. Use CONFIG REWRITE to make this change permanent. 2) Alternatively you can just disable the protected mode by editing the Redis configuration file, and setting the protected mode option to 'no', and then restarting the server. 
3) If you started the server manually just for testing, restart it with the '--protected-mode no' option. 
4) Setup a bind address or an authentication password. NOTE: You only need to do one of the above things in order for the server to start accepting connections from the outside.

二、理解redis

1、redis数据结构

redis数据结构有:string(字符串)、hash(哈希)、list(列表)、set(集合)、zset(有序集合)。
每种数据结构内部都有多种实现编码,这样好处是:
不同使用场景下redis可以采用不同内部实现编码,达到节省内存和提高性能的目的;
另外,对外的数据结构屏蔽了内部编码的不同实现,引入新的内部实现编码不影响开发人员的使用。


redis的5种数据结构

内部编码

redis3.2关于list的内部编码增加了quicklist

127.0.0.1:6379> object encoding mylist
"quicklist"

2、redis架构:内存-单线程-I/O多路复用

reids性能之道:
第一,纯内存访问
第二,非阻塞I/O
第三,单线程避免了线程切换和竞态产生的消耗
注意:如果某个命令执行过长,会造成其他命令的阻塞,对于Redis这种高性能的服务来说是致命的,所以Redis是面向快速执行场景的数据库。
另外,为了性能(对key做hash)和节省空间,redis的key设计尽量简短。

I/O多路复用详解:


blocking I/O

blocking I/O:如果当前文件不可读或不可写,整个服务处于阻塞状态,不会对其它的操作作出响应。


IO多路复用

I/O 多路复用模型中,最重要的函数调用就是 select,该方法的能够同时监控多个文件描述符的可读可写情况,当其中的某些文件描述符可读或者可写时,select 方法就会返回可读以及可写的文件描述符个数。
Reactor 设计模式

Reactor 设计模式是事件驱动的,I/O 多路复用模块同时监听多个文件描述FD,当 accept、read、write 、close 文件事件产生时,文件事件处理器就会回调文件描述FD绑定的事件处理器handler。
Reactor设计模式实现了代码的解耦、模块化、提供复用性、控制并发粒度等,在性能上减少每个client创建线程查询select返回,提高性能。


image.png

三、数据结构和内部编码

1、字符串String

字符串命令

(1)、set:
ex seconds:为键设置秒级过期时间。
px milliseconds:为键设置毫秒级过期时间。
nx:键必须不存在,才可以设置成功,用于添加。使用场景有分布式锁
xx:与nx相反,键必须存在,才可以设置成功,用于更新。
setex和setnx两个命令等同于ex、nx选项

127.0.0.1:6379> set test1 100 ex 300
"ok"
127.0.0.1:6379> set test1 200 nx
"nil"

mset、mget用于批量操作。
(2)、incr key:
incr命令用于对值做自增操作,返回结果分为三种情况:
•值不是整数,返回错误。
•值是整数,返回自增后的结果。
•键不存在,按照值为0自增,返回结果为1。
可用于分布式全局ID,如时间戳+redis自增ID。
redis单线程架构,完全避免使用CAS、同步解决并发问题。

Long id = jedis.incr(key);
 if (id > max) {
      jedis.set(key, "0");
}
return System.currentTimeMillis() +  String.format("%0" + length + "d", id);

其他计数命令
decr key
incrby key increment
decrby key decrement
incrbyfloat key increment
(3)、getset key value命令:设置并返回原值
getset和set一样会设置值,但是不同的是,它同时会返回键原来的值。
(4)、内部编码
•int:8个字节的长整型。
•embstr:小于等于39个字节的字符串。
•raw:大于39个字节的字符串。
(5)
字符串最大大小为512M。

2、哈希hash

hash相关命令
set user:1:name   tom
set user:1:age   23
set user:1:city   beijing

set user:1   serialize(userInfo)

hmset user:1   name tom age 23 city beijing

一般使用哈希存储对象,比使用多个key(占用过多的键,内存占用量较大,同时用户信息内聚性比较差)或者存储对象序列化后字符串要更好(序列化和反序列化有一定的开销,同时每次更新属性都需要把全部数据取出进行反序列化,更新后再序列化到Redis中)
注意:
在使用hgetall时,如果哈希元素个数比较多,会存在阻塞Redis的可能。如果开发人员只需要获取部分field,可以使用hmget,如果一定要获取全部field-value,可以使用hscan命令,该命令会渐进式遍历哈希类型。

//HSCAN 命令用于迭代哈希键中的键值对。  
        Map<String,String> data = new HashMap<>();  
        for(int i=0;i<1000;i++){  
            data.put("key"+i,String.valueOf(i));  
        }  
        jedis.hmset("hash",data);  
        ScanResult<Map.Entry<String, String>> result;// =  jedis.hscan("hash",DATASOURCE_SELECT);  
        int count = 0;  
        int cursor = 0;  
        do {  
            result = jedis.hscan("hash",cursor);  
            cursor = Integer.valueOf(result.getStringCursor());  
            for (Map.Entry<String, String> map : result.getResult()) {  
                System.out.println(map.getKey() + ":" + map.getValue());  
                count++;  
            }  
        }  
        while(cursor!=0);  

Redis为解决诸如keys、hgetall、smembers、zrange可能产生的阻塞问题。对应的命令分别是scan、hscan(哈希)、sscan(集合)、zscan(有序集合),它们的用法基本类似。不过如果在scan的过程中如果有键的变化(增加、删除、修改),那么遍历效果可能会碰到如下问题:新增的键可能没有遍历到,遍历出了重复的键等情况。

3、列表list

(1)、命令
list以对列表两端插入(push)和弹出(pop),还可以获取指定范围的元素列表、获取指定索引下标的元素等。列表是一种比较灵活的数据结构,可以充当栈和队列的角色。


list操作

list操作

list操作
linsert key before|after pivot value

//列出所有
127.0.0.1:6379> lrange listkey 0 -1
1) "java"
2) "b"
3) "a"

lrem key count valuelrem命令会从列表中找到等于value的元素进行删除,
根据count的不同分为三种情况:   •count>0,从左到右,删除最多count个元素。
•count<0,从右到左,删除最多count绝对值个元素。   
•count=0,删除所有。
下面操作将从列表左边开始删除4个为a的元素:
127.0.0.1:6379> lrem listkey 4 a
(integer) 4

blpop和brpop是lpop和rpop的阻塞版本,如果timeout=3,那么客户端要等到3秒后返回,如果timeout=0,那么客户端一直阻塞等下去:
127.0.0.1:6379> brpop list:test 3
(nil)

(2)、使用场景
•lpush+lpop=Stack(栈)
•lpush+rpop=Queue(队列)
•lpsh+ltrim=Capped Collection(有限集合)
•lpush+brpop=Message Queue(消息队列,生产者客户端使用lrpush从列表左侧插入元素,多个消费者客户端使用brpop命令阻塞式的“抢”列表尾部的元素,多个客户端保证了消费的负载均衡和高可用性。)

(3)、内部编码
ziplist(压缩列表):当列表的元素个数小于list-max-ziplist-entries配置(默认512个),同时列表中每个元素的值都小于list-max-ziplist-value配置时(默认64字节),Redis会选用ziplist来作为列表的内部实现来减少内存的使用。
linkedlist(链表):当列表类型无法满足ziplist的条件时,Redis会使用linkedlist作为列表的内部实现。
quicklist:简单地说它是以一个ziplist为节点的linkedlist,它结合了ziplist和linkedlist两者的优势。
http://zhangtielei.com/posts/blog-redis-quicklist.html

quicklist

quicklistLZF结构表示一个被压缩过的ziplist

4、集合(set)

(1)、集合(set)类型也是用来保存多个的字符串元素,但和列表类型不一样的是,集合中不允许有重复元素,并且集合中的元素是无序的,不能通过索引下标获取元素。


set命令

集合的交集并集差集
获取所有元素smembers key

(2)、内部编码
•intset(整数集合):当集合中的元素都是整数且元素个数小于set-max-intset-entries配置(默认512个)时,Re-dis会选用intset来作为集合的内部实现,从而减少内存的使用。
•hashtable(哈希表):当集合类型无法满足intset的条件时,Redis会使用hashtable作为集合的内部实现。
(3)、使用场景
•sadd=Tagging(标签)
•spop/srandmember=Random item(生成随机数,比如抽奖)
•sadd+sinter=Social Graph(社交需求)

5、有序集合zset

(1)、有序集合保留了集合不能有重复成员的特性,但不同的是,有序集合中的元素可以排序。但是它和列表使用索引下标作为排序依据不同的是,它给每个元素设置一个分数(score)作为排序的依据。


image.png

image.png
返回指定排名范围的成员
127.0.0.1:6379> zrange user:ranking 0 2 withscores
1) "kris"
2) "1"
3) "frank"
4) "200"
5) "tim"
6) "220"

返回指定分数范围成员个数
127.0.0.1:6379> zrevrangebyscore user:ranking 221 200 withscores
1) "tim"
2) "220"
3) "frank"
4) "200"

(2)、内部编码
ziplist(压缩列表):当有序集合的元素个数小于zset-max-ziplist-entries配置(默认128个),同时每个元素的值都小于zset-max-ziplist-value配置(默认64字节)时,Redis会用ziplist来作为有序集合的内部实现,zi-plist可以有效减少内存的使用。
skiplist(跳跃表):当ziplist条件不满足时,有序集合会使用skiplist作为内部实现,因为此时ziplist的读写效率会下降。
(3)、有序集合比较典型的使用场景就是排行榜系统。

6、其他命令

rename key newkey
//为了防止被强行rename,Redis提供了renamenx命令,确保只有newKey不存在时候才被覆盖,renamex返回0表示newkey存在未完成重命名。
keys \* 输出所有key(注意,keys命令会遍历所有key,算法复杂度O(n),生成环境禁止使用)。
dbsize 获取key的总数(算法复杂度O(1))
exists key检查键是否存在,存在返回1否则返回0

•expire key seconds:键在seconds秒后过期。   
•expireat key timestamp:键在秒级时间戳timestamp后过期。
•pexpire key milliseconds:键在milliseconds毫秒后过期。   
•pexpireat key milliseconds-timestamp键在毫秒级时间戳timestamp后过期。

对于字符串类型键,执行set命令会去掉过期时间,
127.0.0.1:6379> expire hello 50
(integer) 1
127.0.0.1:6379> ttl hello(integer) 
46
127.0.0.1:6379> set hello world
OK
127.0.0.1:6379> ttl hello(integer) 
-1

flushdb/flushall
命令用于清除数据库,两者的区别的是flushdb只清除当前数据库,flushall会清除所有数据库。

dump+restore命令在Redis实例之间迁移数据


dump+restore命令在Redis实例之间迁移数据

migrate命令在Redis实例之间原子性的迁移数据


migrate命令在Redis实例之间原子性的迁移数据

HyperLogLog
完成数据统计,占用空间很少,不过有80%左右误差。

127.0.0.1:6379> pfadd 2016_03_06:unique:ids "uuid-1" "uuid-2" "uuid-3" "uuid-4"
(integer) 1
//uuid-90新增
127.0.0.1:6379> pfadd 2016_03_06:unique:ids "uuid-1" "uuid-2" "uuid-3" "uuid-90"
(integer) 1
127.0.0.1:6379> pfcount 2016_03_06:unique:ids
(integer) 5

7、redis运维命令

(1)、慢查询
redis提供以下两种命令记录慢查询日志。

config set slowlog-log-slower-than 20000
config set slowlog-max-len 1000
config rewrite

slowlog-log-slower-than为预设阀值,它的单位是微秒(1秒=1000毫秒=1000000微秒),默认值是10000,即10毫秒。(如果slowlog-log-slower-than=0会记录所有的命令,slowlog-log-slower-than<0对于任何命令都不会进行记录。)在高并发情况下,建议设置1毫秒
slowlog-max-len慢查询日志最多条数,以列表方式存储内存。线上建议1000以上,并不会占用过多内存。
慢查询只记录命令执行时间,并不包括命令排队和网络传输时间。


image.png
#获取慢查询日志,可选n条
slowlog get [n]
#慢查询条数
slowlog len
#重置慢查询
slowlog rset

CacheCloud提供更强大的redis运维功能:http://www.ywnds.com/?p=10610
(2)、redis-cli --bigkeys统计redis数据大小

[root@localhost ~]# redis-cli --bigkeys

# Scanning the entire keyspace to find biggest keys as well as
# average sizes per key type.  You can use -i 0.1 to sleep 0.1 sec
# per 100 SCAN commands (not usually needed).

[00.00%] Biggest zset   found so far 'myrank' with 3 members
[00.00%] Biggest set    found so far 'user:test' with 2 members
[00.00%] Biggest string found so far 'test' with 5 bytes
[00.00%] Biggest hash   found so far 'testmap' with 3 fields
[00.00%] Biggest list   found so far 'mylist' with 5 items
[00.00%] Biggest set    found so far 'myset' with 3 members

-------- summary -------

Sampled 8 keys in the keyspace!
Total key length in bytes is 61 (avg len 7.62)

Biggest string found 'test' has 5 bytes
Biggest   list found 'mylist' has 5 items
Biggest    set found 'myset' has 3 members
Biggest   hash found 'testmap' has 3 fields
Biggest   zset found 'myrank' has 3 members

1 strings with 5 bytes (12.50% of keys, avg size 5.00)
1 lists with 5 items (12.50% of keys, avg size 5.00)
4 sets with 7 members (50.00% of keys, avg size 1.75)
1 hashs with 3 fields (12.50% of keys, avg size 3.00)
1 zsets with 3 members (12.50% of keys, avg size 3.00)

(3)、stat
--stat选项可以实时获取Redis的重要统计信息,虽然info命令中的统计信息更全,但是能实时看到一些增量的数据。

[root@localhost ~]# redis-cli --stat
------- data ------ --------------------- load -------------------- - child -
keys       mem      clients blocked requests            connections
8          901.15K  3       0       282 (+0)            18
8          901.15K  3       0       284 (+2)            18
8          901.15K  3       0       286 (+2)            18
8          901.15K  3       0       288 (+2)            18
8          901.15K  3       0       290 (+2)            18
8          901.15K  3       0       291 (+1)            18
8          901.15K  3       0       292 (+1)            18

(4)、info Commandstats
命令平均耗时使用info Commandstats命令获取,包含每个命令调用次数、总耗时、平均耗时,单位为微秒。

127.0.0.1:6379> info Commandstats
# Commandstats
cmdstat_hmset:calls=1,usec=12,usec_per_call=12.00
cmdstat_hincrby:calls=2,usec=30,usec_per_call=15.00
cmdstat_scan:calls=3,usec=46,usec_per_call=15.33
cmdstat_srem:calls=1,usec=16,usec_per_call=16.00
cmdstat_dbsize:calls=4,usec=3,usec_per_call=0.75
cmdstat_publish:calls=5,usec=15,usec_per_call=3.00
cmdstat_llen:calls=4,usec=9,usec_per_call=2.25
cmdstat_lpush:calls=2,usec=25,usec_per_call=12.50
cmdstat_hlen:calls=3,usec=4,usec_per_call=1.33
cmdstat_scard:calls=12,usec=6,usec_per_call=0.50
cmdstat_command:calls=11,usec=71675,usec_per_call=6515.91
cmdstat_hkeys:calls=2,usec=23,usec_per_call=11.50
cmdstat_subscribe:calls=2,usec=7,usec_per_call=3.50
cmdstat_hdel:calls=2,usec=11,usec_per_call=5.50

四、pipeline

pipeline

pipeline批量命令节省网络交互时间,相对原生批量命令(mget等)是原子的,Pipeline是非原子的。 原生批量命令是Redis服务端支持实现的,而Pipeline需要服务端和客户端的共同实现。
Lua脚本:
•Lua脚本在Redis中是原子执行的,执行过程中间不会插入其他命令。
•Lua脚本可以帮助开发和运维人员创造出自己定制的命令,并可以将这些命令常驻在Redis内存中,实现复用的效果。
•Lua脚本可以将多条命令一次性打包,有效地减少网络开销。

Pipeline pi = jedis.pipelined();
//多个操作..
pi.sync();

redis集群下pipeline:
如果集群是由客户端做一致性hash(如shardingJedis),使用pipeline前需要对操作按hash算法做分组。
redisCluster目前不支持pipeline,解决方案参考:http://blog.csdn.net/youaremoon/article/details/51751991

五、事务

127.0.0.1:6379> multi
OK
127.0.0.1:6379> sadd testTran test1
QUEUED
127.0.0.1:6379> sadd testTran test2
QUEUED
127.0.0.1:6379> exec
1) (integer) 1
2) (integer) 1
127.0.0.1:6379> sismember testTran  test1//exec执行之前操作返回0
(integer) 1

redis事务并不支持回滚,同时无法实现命令之间的逻辑关系计算。
redis提供watch命令,如果事务执行中key被改的过,则事务不执行(exec结果为nil)。

#T1:客户端1
127.0.0.1:6379> set key "java"
OK
#T2:客户端1
127.0.0.1:6379> watch key
OK
#T3:客户端1
127.0.0.1:6379> multi 
OK
#T4:客户端2
127.0.0.1:6379> append key python
(integer) 11
#T5:客户端1
127.0.0.1:6379> append key jedis
QUEUED
#T6:客户端1
127.0.0.1:6379> exec
(nil)
#T7:客户端1
127.0.0.1:6379> get key
"javapython"

六、发布与订阅

发布与订阅
#T1
127.0.0.1:6379> publish channel:test "hello"
#T2
127.0.0.1:6379> subscribe channel:test
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "channel:test"
3) "hello"
#T2取消订阅
127.0.0.1:6379> unsubscribe channel:test

#当前channel:sports频道的订阅数为1:
127.0.0.1:6379> pubsub numsub channel:test
1) "channel:test"
2) (integer) 1

按模式匹配订阅
psubscribe pattern [pattern...]
punsubscribe [pattern [pattern ...]]

六、jedis客户端

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343

推荐阅读更多精彩内容

  • 本文为笔者对在学习Redis过程中所收集资料的一个总结,目的是为了以后方便回顾相关的知识,大部分为非原创内容。特此...
    EakonZhao阅读 14,407评论 0 9
  • 本文将从Redis的基本特性入手,通过讲述Redis的数据结构和主要命令对Redis的基本能力进行直观介绍。之后概...
    kelgon阅读 61,122评论 24 626
  • 之前在微博上看到一句话说,三观不合是种什么感觉?我说大海很美你却说它淹死过很多人。 从定义上来说三观就是人生观,世...
    筿筿阅读 648评论 0 5
  • 到底要有多坚强,才能让一个女人,在经历人生的大悲面前,收起眼泪,藏起悲伤,像一个从石头缝里钻出的小芽,为了活下去努...
    简爱的简阅读 381评论 2 1
  • 文/楹晞 1 下午和小燚一起去逛街,天气闷热,可能是一场雨要来了,空中的棉花云在蔚蓝的天际变换出无穷的图案,美极了...
    楹晞阅读 675评论 8 15