花了一周左右,全面的学习redis的知识,并记录学习笔记php
原文请访问个人技术博客番茄技术小栈java
Redis是一个使用ANSI C编写的开源、支持网络、基于内存、可选持久性的键值对存储数据库node
10w QPSpython
缘由mysql
$ wget http://download.redis.io/releases/redis-4.0.8.tar.gz
$ tar xzf redis-4.0.8.tar.gz
$ cd redis-4.0.8
$ make
复制代码
命令名 | 命令说明 |
---|---|
redis-server | redis服务器 |
redis-cli | redis命令行客户端 |
redis-benchmark | redis性能测试工具 |
redis-check-aof | aof文件修复工具 |
redis-check-dump | RDB文件检查工具 |
redis-sentinel | sentinel服务器(2.8之后) |
redis-server
复制代码
验证linux
ps aux | grep redis
复制代码
redis-cli -h 127.0.0.1 -p 6379 ping
复制代码
redis-server configPath
复制代码
redis-server --port 6380
复制代码
三种启动方式比较git
redis-cli -h 127.0.0.1 -p 6379
复制代码
返回值 | 返回值说明 |
---|---|
状态回复 | ping -> pong |
错误回复 | hget hello field -> (error) ERR wrong number of arguments for 'hget' command |
整数回复 | incr hello -> (integer) 1 |
字符串回复 | get hello -> "1" |
多行字符串回复 | mget hello foo -> 1) "1" 2) (nil) |
配置名 | 说明 |
---|---|
daemonize | 是不是守护进程(no/yes) |
port | redis对外端口号 |
logfile | redis系统日志 |
dir | redis工做目录 |
配置方式github
vim redis-6382.conf
port 6382
daemonize yes
logfile "6382.log"
dir ./workdata/
复制代码
redis-server redis-6382.conf
复制代码
ps -aux | grep -v redis-server | grep 6382
复制代码
vim workdata/6382.log
复制代码
命令 | 说明 | 时间复杂度 |
---|---|---|
keys [pattern] | 遍历全部key | O(N) |
dbsize | 遍历全部key | O(1) |
exists | 检查key是否存在 | O(1) |
del key | 删除指定的key-value | O(1) |
expire key seconds | key在seconds秒后过时 | O(1) |
ttl key | 查看key剩余的过时时间 | O(1) |
persist key | 去掉key的过时时间 | O(1) |
type key | 返回key的类型 | O(1) |
注意 keys命令通常再也不生产环境使用redis
** 演示**sql
➜ redis-4.0.8 redis-cli -h 127.0.0.1 -p 6382
127.0.0.1:6382> set hello word
OK
127.0.0.1:6382> set php good
OK
127.0.0.1:6382> set java best
OK
127.0.0.1:6382> keys *
1) "java"
2) "hello"
3) "php"
127.0.0.1:6382> dbsize
(integer) 3
127.0.0.1:6382> exists php
(integer) 1
127.0.0.1:6382> del php java
(integer) 2
127.0.0.1:6382> keys *
1) "hello"
127.0.0.1:6382> expire hello 20
(integer) 1
127.0.0.1:6382> ttl hello
(integer) 16
127.0.0.1:6382> ttl hello
(integer) 12
127.0.0.1:6382> persist hello
(integer) 1
127.0.0.1:6382> ttl hello
(integer) -1
127.0.0.1:6382> type hello
string
复制代码
单线程在程序执行时,所走的程序路径按照连续顺序排下来,前面的必须处理好,后面的才会执行。
key | value |
---|---|
hello | world |
counter | 1 |
bits | 101111101110 |
能够是字符串(json);数字,以及二进制
命令 | 说明 | 时间复杂度 |
---|---|---|
get key | 获取key对应的value | O(1) |
set key value | 设置key value | O(1) |
del key | 删除key-value | O(1) |
incr | key自增1, 若是key不存在,自增后get(key) = 1 | O(1) |
decr | key自减1, 若是key不存在,自增后get(key) = -1 | O(1) |
incrby key k | key自增k, 若是key不存在,自增后get(key) = k | O(1) |
decr key k | key自减k, 若是key不存在,自增后get(key) = -k | O(1) |
set key value | 无论能够是否存在 | O(1) |
setnx key value | key不存在,才设置 | O(1) |
set key value xx | key存在,才设置 | O(1) |
mget key1 key2 key3 | 批量获取key,原子操做 | O(N) |
mset key1 value1 key2 value2 | 批量设置key-value | O(1) |
getset key newvalue | set key newvalue并返回旧的value | O(1) |
append key value | 将value追加到旧的value | O(1) |
strlen key | 返回字符串的长度(注意中文,utf8下一个中文占用3个字符) | O(1) |
incrbyfloat key 3.5 | 增长key对应的值3.5 | O(1) |
getrange key start end | 获取字符串指定下标全部的值 | O(1) |
setrange key index value | 设置指定下标全部对应的值 | O(1) |
练习
127.0.0.1:6382> set hello "world"
OK
127.0.0.1:6382> get hell
(nil)
127.0.0.1:6382> get hello
"world"
127.0.0.1:6382> del hello
(integer) 1
127.0.0.1:6382> get hello
(nil)
127.0.0.1:6382> get counter
(nil)
127.0.0.1:6382> incr counter
(integer) 1
127.0.0.1:6382> get counter
"1"
127.0.0.1:6382> incrby counter 99
(integer) 100
127.0.0.1:6382> get counter
"100"
127.0.0.1:6382> decr counter
(integer) 99
127.0.0.1:6382> get counter
"99"
127.0.0.1:6382> decrby counter 100
(integer) -1
127.0.0.1:6382> get counter
"-1"
127.0.0.1:6382> exists php
(integer) 0
127.0.0.1:6382> set php good
OK
127.0.0.1:6382> setnx php bad
(integer) 0
127.0.0.1:6382> set php best xx
OK
127.0.0.1:6382> get php
"best"
127.0.0.1:6382> exists java
(integer) 0
127.0.0.1:6382> setnx java best
(integer) 1
127.0.0.1:6382> set java easy xx
OK
127.0.0.1:6382> get java
"easy"
127.0.0.1:6382> set hello world
OK
127.0.0.1:6382> getset hello php
"world"
127.0.0.1:6382> get hello
"php"
127.0.0.1:6382> append hell ",php"
(integer) 4
127.0.0.1:6382> get hello
"php"
127.0.0.1:6382> append hello ",php"
(integer) 7
127.0.0.1:6382> get hello
"php,php"
127.0.0.1:6382> strlen hello
(integer) 7
127.0.0.1:6382> set hello "吴军旗"
OK
127.0.0.1:6382> strlen hello
(integer) 9
复制代码
n次get操做
** 1次mget操做**
incr userid: pageview (**主要的是:单线程,因此无竞争)**)
复制代码
incr id
复制代码
命令 | 说明 | 时间复杂度 |
---|---|---|
hget key field | 获取hash key对应field的value | O(1) |
hset key field value | 设置has key 对应的field的value | O(1) |
hexists key field | 判断hash key 是否有field | O(1) |
hlen key | 获取hash key field的数量 | O(1) |
hmget key field1 field2...fieldN | 批量获取hash key的一批field对应的值 | O(N) |
hset key field1 value1 field2 value2...fieldN valueN | 批量设置hash key的一批field value | O(1) |
hgetall key | 返回hash key对应全部的field和value | O(N) |
hvals key | 返回hash key对应全部的field的value | O(N) |
hkeys key | 返回hash key对应全部的field | O(N) |
hsetnx key field value | 设置has key 对应的field的value(若是field已经存在,则失败) | O(1) |
hincrby key field intCounter | hash key对应的field的value自增intCounter | O(1) |
hincrbyfloat key field floatCounter | 浮点数版本 | O(1) |
注意 当心使用hgetall(牢记单线程)
练习
127.0.0.1:6382> hset user1 age 26
(integer) 1
127.0.0.1:6382> hset user1 name wujunqi
(integer) 1
127.0.0.1:6382> hget all user1
(nil)
127.0.0.1:6382> hgetall user1
1) "age"
2) "26"
3) "name"
4) "wujunqi"
127.0.0.1:6382> hdel user1 age
(integer) 1
127.0.0.1:6382> hgetall user1
1) "name"
2) "wujunqi"
127.0.0.1:6382> hget user1 name
"wujunqi"
127.0.0.1:6382> hexists user1 name
(integer) 1
127.0.0.1:6382> hlen user1
(integer) 1
127.0.0.1:6382> hmset user2 name xiaofang age 26
OK
127.0.0.1:6382> hmget user2 name age
1) "xiaofang"
2) "26"
127.0.0.1:6382> hgetall user2
1) "name"
2) "xiaofang"
3) "age"
4) "26"
127.0.0.1:6382> hvals user2
1) "xiaofang"
2) "26"
127.0.0.1:6382> hkeys user2
1) "name"
2) "age"
127.0.0.1:6382> hincrby user age 2
(integer) 2
127.0.0.1:6382> hgetall user2
1) "name"
2) "xiaofang"
3) "age"
4) "26"
127.0.0.1:6382> hincrby user2 age 2
(integer) 28
127.0.0.1:6382> hgetall user2
1) "name"
2) "xiaofang"
3) "age"
4) "28"
127.0.0.1:6382> hincrbyfloat user2 age 2.0
"30"
127.0.0.1:6382> hincrbyfloat user2 age 2.5
"32.5"
127.0.0.1:6382> hgetall user2
1) "name"
2) "xiaofang"
3) "age"
4) "32.5"
复制代码
练习
127.0.0.1:6382> rpush list1 a b c d
(integer) 4
127.0.0.1:6382> lpush list1 e f g h i
(integer) 9
127.0.0.1:6382> lrange list1 0 -1
1) "i"
2) "h"
3) "g"
4) "f"
5) "e"
6) "a"
7) "b"
8) "c"
9) "d"
127.0.0.1:6382> linsert list1 before i wu
(integer) 10
127.0.0.1:6382> lrange list1 0 -1
1) "wu"
2) "i"
3) "h"
4) "g"
5) "f"
6) "e"
7) "a"
8) "b"
9) "c"
10) "d"
127.0.0.1:6382> linsert list1 after i jun
(integer) 11
127.0.0.1:6382> lrange list1 0 -1
1) "wu"
2) "i"
3) "jun"
4) "h"
5) "g"
6) "f"
7) "e"
8) "a"
9) "b"
10) "c"
11) "d"
127.0.0.1:6382> lpop list1
"wu"
127.0.0.1:6382> rpop list1
"d"
127.0.0.1:6382> lrange list1 0 -1
1) "i"
2) "jun"
3) "h"
4) "g"
5) "f"
6) "e"
7) "a"
8) "b"
9) "c"
127.0.0.1:6382> lrem list1 1 i
(integer) 1
127.0.0.1:6382> lrange list1 0 -1
1) "jun"
2) "h"
3) "g"
4) "f"
5) "e"
6) "a"
7) "b"
8) "c"
127.0.0.1:6382> rpush list1 c c c c c
(integer) 13
127.0.0.1:6382> ltrem list1 -3 c
(error) ERR unknown command 'ltrem'
127.0.0.1:6382> lrem list1 -3 c
(integer) 3
127.0.0.1:6382> lrange list1 0 -1
1) "jun"
2) "h"
3) "g"
4) "f"
5) "e"
6) "a"
7) "b"
8) "c"
9) "c"
10) "c"
127.0.0.1:6382> lindex list 0
(nil)
127.0.0.1:6382> lindex list1 0
"jun"
127.0.0.1:6382> llen list1
(integer) 10
127.0.0.1:6382> lset list 0 wu
(error) ERR no such key
127.0.0.1:6382> lset list1 0 wu
OK
127.0.0.1:6382> lrange list1 0 -1
1) "wu"
2) "h"
3) "g"
4) "f"
5) "e"
6) "a"
7) "b"
8) "c"
9) "c"
10) "c"
复制代码
Redis 的 Set 是 String 类型的无序集合。集合成员是惟一的,这就意味着集合中不能出现重复的数据。 Redis 中集合是经过哈希表实现的,因此添加,删除,查找的复杂度都是 O(1)。
命令 | 说明 | 时间复杂度 |
---|---|---|
sadd key element | 向集合key添加element(若是element已经存在,添加失败) | O(1) |
srem key element | 将集合key中的element移除掉 | O(1) |
scard key | 计算集合大小 | O(1) |
sismember key element | 判断element 是否在集合中 | O(1) |
srandmember key count | 从集合中随机挑count个元素 | O(1) |
spop key | 从集合中随机弹出一个元素 | O(1) |
smembers key | 获取集合全部元素 | O(1) |
srem key element | 将集合key中的element移除掉 | O(1) |
命令 | 说明 | 时间复杂度 |
---|---|---|
sdiff key1 key2 | 差集 | O(1) |
sinter key1 key2 | 交集 | O(1) |
sunion key1 key2 | 并集 | O(1) |
sidff/sinter/suion + store destkey | 将差集、交集、并集保存在destkey中 | O(1) |
注意
练习
127.0.0.1:6382> sadd set1 a b c d
(integer) 4
127.0.0.1:6382> srem set1 a
(integer) 1
127.0.0.1:6382> smembers set1
1) "d"
2) "c"
3) "b"
127.0.0.1:6382> scard set1
(integer) 3
127.0.0.1:6382> sismember set1 d
(integer) 1
127.0.0.1:6382> srandmember set1 2
1) "d"
2) "b"
127.0.0.1:6382> srandmember set1 2
1) "b"
2) "c"
127.0.0.1:6382> spop set1
"c"
127.0.0.1:6382> smembers set1
1) "d"
2) "b"
127.0.0.1:6382> srem set1 d
(integer) 1
127.0.0.1:6382> smembers set1
1) "b"
127.0.0.1:6382> sadd set1 1 2 3 4 5
(integer) 5
127.0.0.1:6382> sadd set2 a b c 12 8 9 1 2
(integer) 8
127.0.0.1:6382> sdiff set1 set2
1) "3"
2) "4"
3) "5"
127.0.0.1:6382> sinter set1 set2
1) "2"
2) "b"
3) "1"
127.0.0.1:6382> sunion set1 set2
1) "5"
2) "2"
3) "4"
4) "1"
5) "a"
6) "8"
7) "3"
8) "b"
9) "9"
10) "12"
11) "c"
127.0.0.1:6382>
复制代码
总结
Redis 有序集合和集合同样也是string类型元素的集合,且不容许重复的成员。不一样的是每一个元素都会关联一个double类型的分数。redis正是经过分数来为集合中的成员进行从小到大的排序。有序集合的成员是惟一的,但分数(score)却能够重复。
命令 | 说明 | 时间复杂度 |
---|---|---|
zadd key score element | 添加score和element | O(logN) |
zrem key element(能够是多个) | 将集合key中的element移除掉 | O(1) |
zscore key element | 返回元素的分数 | O(1) |
zincrby key increScore element | 增长或减小元素的分数 | O(1) |
zcard key | 返回元素的总个数 | O(1) |
zrank(zrevrank) key member | 返回元素的排名 | O(1) |
zrange(zrevrank) key start end [WITHSCORES] | 返回指定索引范围内的升序元素[分值] | O(logN + m) |
zrangebyscore(zrevrangebyscore) key minScore maxScore | 返回指定分数范围内的升序元素 | O(logN + m) |
zcount key minScore maxScore | 返回有序集合内在指定分数范围内的个数 | O(logN + m) |
zremrangebyrank key start end | 删除指定排名内的升序元素 | O(logN + m) |
zremrangebyscore key minScore maxScore | 删除指定分数内的升序元素 | O(logN + m) |
ZINTERSTORE destination numkeys(表示key的个数) key [key ...] | 计算给定的一个或多个有序集的交集并将结果集存储在新的有序集合 key 中 | |
ZUNIONSTORE destination numkeys key [key ...] | 计算给定的一个或多个有序集的并集,并存储在新的 key 中 |
找相应语言的下载(通常选择有笑脸和星星的)
https://redis.io/clients#php
复制代码
两点说明
如何配置
什么是流水线
流水线的做用
两点注意
publish channel message
subscribe [channel] 一个或者多个
unsubscribe [channel] 一个或者多个
练习
127.0.0.1:6382> publish weibomovie "hello world"
(integer) 1
127.0.0.1:6382> publish weibomovie "hello world2"
(integer) 1
复制代码
另一个cli
127.0.0.1:6382> SUBSCRIBE weibomovie
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "weibomovie"
3) (integer) 1
1) "message"
2) "weibomovie"
3) "hello world"
1) "message"
2) "weibomovie"
3) "hello world2"
复制代码
命令 | 说明 | 时间复杂度 |
---|---|---|
setbit key offset value | 给位图指定索引设置值 | O(1) |
getbit key offset | 获取位图指定索引的值 | O(1) |
bitcount key start end | 获取位图指定范围(start 到end,单位为字节,若是不指定就获取所有)位值为1的个数 | O(1) |
bitop op destkey key [key...] | 作多个bitmap的and,or,not,xor操做并将结果保存在destkey中 | O(1) |
bitpos key targetBit [start][end | 计算位图指定范围(start到end,单位为字节,若是不指定就是获取所有)第一个偏移量对应的值等于targetBit的位置 | O(1) |
练习
127.0.0.1:6382> set hello big
OK
127.0.0.1:6382> getbit hello
(error) ERR wrong number of arguments for 'getbit' command
127.0.0.1:6382> getbit hello 0
(integer) 0
127.0.0.1:6382> setbit hello 0 1
(integer) 0
127.0.0.1:6382> get hello
"\xe2ig"
127.0.0.1:6382> set hell a
OK
127.0.0.1:6382> bitcount hell
(integer) 3
127.0.0.1:6382> bitop and hell hello
(integer) 3
127.0.0.1:6382> set a a
OK
127.0.0.1:6382> set b b
OK
127.0.0.1:6382> bitop and c a b
(integer) 1
127.0.0.1:6382> get c
"`"
127.0.0.1:6382> bitpos a 1
(integer) 1
127.0.0.1:6382> bitpos a 0
(integer) 0
127.0.0.1:6382> set user2 100
OK
复制代码
重要理解 使用位图去记录用户uid,其实就是记录索引值,好比userid=100表明位图下标100的值为1
命令 | 说明 |
---|---|
pfaddd key element [element...] | 向hyperloglog添加元素 |
pfcount key [key...] | 计算hyperloglog的独立总数 |
pfmerge destkey sourceKey [sourcekey...] | 合并多个hyperloglog |
练习
127.0.0.1:6382> pfadd puser1 "u1" "u2" "u3"
(integer) 1
127.0.0.1:6382> pfcount puser1
(integer) 3
127.0.0.1:6382> pfadd puser2 "u3" "u4" "u5"
(integer) 1
127.0.0.1:6382> pfmerge puser puser1 puser2
OK
127.0.0.1:6382> pfcount puser
(integer) 5
复制代码
是否能容忍错误(错误率:0.81%)
是否须要单条数据(没有办法取出)
命令 | 说明 |
---|---|
geoadd key longitude latitude member [longitude latitude member ...] | 增长地理位置信息 |
geopos key member[member... | 获取地理位置信息 |
geodist key member1 member2[unit] | 获取两个地理位置的距离,unit:m,km,mi,ft |
georadius | 获取指定位置范围内的地理位置信息集合 |
练习
127.0.0.1:6382> geoadd beijing 116.28 39.55
(error) ERR wrong number of arguments for 'geoadd' command
127.0.0.1:6382> geoadd geo 116.28 39.55 beijing 117.12 39.08 tianjin
(integer) 2
127.0.0.1:6382> geopos geo beijing
1) 1) "116.28000229597091675"
2) "39.5500007245470826"
127.0.0.1:6382> geodist geo beijing tianjin
"89206.0576"
127.0.0.1:6382>
复制代码
redis全部数据保存在内存中, 对数据的更新将异步地保存到磁盘上
* 文件策略:如存在老的RDB文件,新替换老
* 复杂度:O(N)
复制代码
**相关配置
配置参数 | 值 |
---|---|
save | 900 1 |
save | 300 10 |
save | 60 10000 |
dbfilename | dump-${port}.rdb |
dir | /bigdishpath |
stop-writes-on-bgsav-error | yes |
rdbcompression | yes |
其余的方式也会触发生成RDB文件
三种策略比较
-------------------------华丽的分割线--------------------
看完的朋友能够点个喜欢/关注,您的支持是对我最大的鼓励。
想了解更多,欢迎关注个人微信公众号:番茄技术小栈