# 下载 cd /tmp wget http://download.redis.io/releases/redis-3.2.11.tar.gz # 解压 tar -zxvf redis-3.2.11.tar.gz # 创建软链接 ln -s redis-3.2.11 redis # 编译安装 cd redis make make install # 查看redis-开头的可执行文件 cd src ll | grep redis- # 显示以下 -rwxr-xr-x 1 root root 2432576 Nov 25 18:00 redis-benchmark -rw-rw-r-- 1 root root 29559 Sep 21 22:20 redis-benchmark.c -rw-r--r-- 1 root root 108448 Nov 25 18:00 redis-benchmark.o -rwxr-xr-x 1 root root 25168 Nov 25 18:00 redis-check-aof -rw-rw-r-- 1 root root 6328 Sep 21 22:20 redis-check-aof.c -rw-r--r-- 1 root root 33688 Nov 25 18:00 redis-check-aof.o -rwxr-xr-x 1 root root 5191328 Nov 25 18:00 redis-check-rdb -rw-rw-r-- 1 root root 12789 Sep 21 22:20 redis-check-rdb.c -rw-r--r-- 1 root root 54496 Nov 25 18:00 redis-check-rdb.o -rwxr-xr-x 1 root root 2585448 Nov 25 18:00 redis-cli -rw-rw-r-- 1 root root 90678 Sep 21 22:20 redis-cli.c -rw-r--r-- 1 root root 366344 Nov 25 18:00 redis-cli.o -rwxr-xr-x 1 root root 5191328 Nov 25 18:00 redis-sentinel -rwxr-xr-x 1 root root 5191328 Nov 25 18:00 redis-server -rwxrwxr-x 1 root root 60852 Sep 21 22:20 redis-trib.rb # 回到redis目录 cd .. # 启动redis redis-server # 能够用下面三种方式验证redis服务器是否正常 # 查询redis进程 ps -ef | grep redis root 30253 27126 0 18:10 pts/0 00:00:00 redis-server *:6379 root 30279 30259 0 18:19 pts/1 00:00:00 grep --color=auto redis # 查询redis端口占用 netstat -antpl | grep redis tcp 0 0 0.0.0.0:6379 0.0.0.0:* LISTEN 30253/redis-server tcp6 0 0 :::6379 :::* LISTEN 30253/redis-server # 用redis客户端进行链接,而后执行ping,返回PONG redis-cli -h 127.0.0.1 -p 6379 127.0.0.1:6379> ping PONG
生产环境建议指定配置文件启动,单机多实例配置文件能够用端口区分开,好比像下面这样:php
mv redis.conf redis-6380.conf vim redis-6380.conf
暂时咱们仅指定如下几项基础配置:java
# 是否以守护进程方式启动 daemonize yes # redis对外端口 port 6380 # 工做目录 dir ./ # 日志文件 logfile "6380.log"
用指定配置文件方式从新启动Redisnode
redis-server redis-6380.conf ps -ef | grep redis root 30339 1 0 18:55 ? 00:00:00 redis-server 127.0.0.1:6380 root 30344 30259 0 18:56 pts/1 00:00:00 grep --color=auto redis
redis-cli链接mysql
# 咱们在任意目录下启动redis客户端 redis-cli -h 127.0.0.1 -p 6379 127.0.0.1:6379> ping PONG 127.0.0.1:6379> incr number (integer) 1 127.0.0.1:6379> set hello world OK 127.0.0.1:6379> set foo bar OK 127.0.0.1:6379> get hello "world" 127.0.0.1:6379>
redis-cli返回值redis
127.0.0.1:6379> ping PONG
127.0.0.1:6379> hget hello (error) ERR wrong number of arguments for 'hget' command
127.0.0.1:6379> incr number (integer) 1
127.0.0.1:6379> set hello world OK 127.0.0.1:6379> get hello "world"
127.0.0.1:6379> set foo bar OK 127.0.0.1:6379> mget hello foo 1) "world" 2) "bar"
除了上面几个最基本的配置,redis还有下面这些高级配置:sql
后续穿插讲解shell
能够经过如下命令列出配置:vim
# 列出配置文件的配置项,去除注释行和空行 cat redis.conf | grep -v '#' | grep -v '^$'
bind 127.0.0.1 protected-mode yes port 6379 tcp-backlog 511 timeout 0 tcp-keepalive 300 daemonize yes supervised no pidfile /var/run/redis_6379.pid loglevel notice logfile "" databases 16 save 900 1 save 300 10 save 60 10000 stop-writes-on-bgsave-error yes rdbcompression yes rdbchecksum yes dbfilename dump.rdb dir ./ slave-serve-stale-data yes slave-read-only yes repl-diskless-sync no repl-diskless-sync-delay 5 repl-disable-tcp-nodelay no slave-priority 100 appendonly no appendfilename "appendonly.aof" appendfsync everysec no-appendfsync-on-rewrite no auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb aof-load-truncated yes lua-time-limit 5000 slowlog-log-slower-than 10000 slowlog-max-len 128 latency-monitor-threshold 0 notify-keyspace-events "" hash-max-ziplist-entries 512 hash-max-ziplist-value 64 list-max-ziplist-size -2 list-compress-depth 0 set-max-intset-entries 512 zset-max-ziplist-entries 128 zset-max-ziplist-value 64 hll-sparse-max-bytes 3000 activerehashing yes client-output-buffer-limit normal 0 0 0 client-output-buffer-limit slave 256mb 64mb 60 client-output-buffer-limit pubsub 32mb 8mb 60 hz 10 aof-rewrite-incremental-fsync yes
127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3 k4 v4 OK 127.0.0.1:6379> dbsize (integer) 4 127.0.0.1:6379> sadd myset a b c d e (integer) 5 127.0.0.1:6379> dbsize (integer) 5
127.0.0.1:6379> set a b OK 127.0.0.1:6379> exist a (integer) 1 127.0.0.1:6379> del a (integer) 1 127.0.0.1:6379> exists a (integer) 0
127.0.0.1:6379> set a b OK 127.0.0.1:6379> get a "b" 127.0.0.1:6379> del a (integer) 1 127.0.0.1:6379> get a (nil)
127.0.0.1:6379> set hello world OK 127.0.0.1:6379> expire hello 20 (integer) 1 127.0.0.1:6379> ttl hello (integer) 16 127.0.0.1:6379> get hello "world" 127.0.0.1:6379> ttl hello (integer) 7 127.0.0.1:6379> ttl hello (integer) -2 (-2表明key已经不存在了) 127.0.0.1:6379> get hello (nil)
127.0.0.1:6379> set hello world OK 127.0.0.1:6379> expire hello 20 (integer) 1 127.0.0.1:6379> ttl hello (integer) 16 (还有16秒过时) 127.0.0.1:6379> persist hello (integer) 1 127.0.0.1:6379> ttl hello (integer) -1 (-1表明key存在,而且没有设置过时时间) 127.0.0.1:6379> get hello "wordl"
redis数据类型:string hash list set zsetapi
127.0.0.1:6379> set a b OK 127.0.0.1:6379> type a string 127.0.0.1:6379> sadd myset 1 2 3 (integer) 3 127.0.0.1:6379> type myset set
命令 | 时间复杂度 |
---|---|
keys | O(n) |
dbsize | O(1) |
del | O(1) |
exists | O(1) |
expire | O(1) |
type | O(1) |
Redis单线程设计,所以使用时要注意几点:缓存
长(慢)命令:keys, flushall, flushdb, slow lua script, mutil/exec, operate big value(collection)
使用场景
命令
127.0.0.1:6379> set hello "world" OK 127.0.0.1:6379> get hello "world" 127.0.0.1:6379> del hello 1 127.0.0.1:6379> get hello (nil)
127.0.0.1:6379> get counter (nil) 127.0.0.1:6379> incr counter (integer) 1 127.0.0.1:6379> get counter "1" 127.0.0.1:6379> incrby counter 99 (integer) 100 127.0.0.1:6379> decr counter (integer) 99 127.0.0.1:6379> get counter "99"
127.0.0.1:6379> exists php (integer) 0 (0表明不存在) 127.0.0.1:6379> set php good OK 127.0.0.1:6379> sexnx php bad (integer) 0 127.0.0.1:6379> set php best xx OK 127.0.0.1:6379> get php "best" 127.0.0.1:6379> exists java (integer) 0 127.0.0.1:6379> set java best (integer) 1 127.0.0.1:6379> set java easy xx OK 127.0.0.1:6379> get java "easy" 127.0.0.1:6379> exists lua (integer) 0 127.0.0.1:6379> set lua hehe xx (nil)
127.0.0.1:6379> mset hello world java best php good OK 127.0.0.1:6379> mget hello java php "world" "best" "good"
127.0.0.1:6379> set hello world OK 127.0.0.1:6379> getset hello php "world" 127.0.0.1:6379> get hello "php" 127.0.0.1:6379> append hello ",java" (integer) 8 127.0.0.1:6379> get hello "pho,java" 127.0.0.1:6379> strlen hello (integer) 8 127.0.0.1:6379> set hello "足球" OK 127.0.0.1:6379> strlen hello (integer) 4 (一个中文占2个字节)
127.0.0.1:6379> incr counter (integer) 1 127.0.0.1:6379> incrbyfloat counter 1.1 "2.1" 127.0.0.1:6379> get counter "2.1" 127.0.0.1:6379> set hello javabest OK 127.0.0.1:6379> getrange hello 0 2 "jav" 127.0.0.1:6379> setrange hello 4 p (integer) 8 127.0.0.1:6379> get hello "javapest"
命令 | 含义 | 时间复杂度 |
---|---|---|
set key value | 设置key-value | O(1) |
get key | 获取key-value | O(1) |
del key | 删除key-value | O(1) |
setnx setxx | 根据key是否存在设置key-value | O(1) |
incr decr | 计数 | O(1) |
mget mset | 批量操做key-value | O(n) |
实战
incr userid:pageview(单线程:无竞争)
缓存视频的基本信息(数据源在MySQL中)
伪代码以下:
public VideoInfo get(long id) { String redisKey = redisPrefix + id; VideoInfo videoInfo = redis.get(redisKey); if (videoInfo == null) { videoInfo = mysql.get(id); if (videoInfo != null) { // 序列化 redis.set(redisKey, serialize(videoInfo)); } } return videoInfo; }
利用了redis的单线程,即便是多个服务同时来获取ID,也得一个个来。
命令
# hset 设置field-value对 127.0.0.1:6379> hset user:1:info age 23 (integer) 1 # hget 获取field的value 127.0.0.1:6379> hget user:1:info age "23" 127.0.0.1:6379> hset user:1:info name ronaldo (integer) 1 # hgetall 获取全部key-value对 127.0.0.1:6379> hgetall user:1:info 1) "age" 2) "23" 3) "name" 4) "ronaldo" # hdel 删除key-value对 127.0.0.1:6379> hdel user:1:info age (integer) 1 127.0.0.1:6379> hgetall user:1:info 1) "name" 2) "ronaldo"
127.0.0.1:6379> hgetall user:1:info 1) "age" 2) "23" 3) "name" 4) "ronaldo" # hexists 判断filed是否存在 127.0.0.1:6379> hexists user:1:info name (integer) 1 # 判断field长度 127.0.0.1:6379> hlen user:1:info (integer) 2
# hmset 设置多个filed-value对 127.0.0.1:6379> hmset user:2:info age 30 name kaka page 50 OK 127.0.0.1:6379> hlen user:2:info (integer) 3 # hmget 获取多个field的value 127.0.0.1:6379> hmget user:2:info age name 1) "30" 2) "kaka"
# hgetall key:返回hash key对应全部的field和value 127.0.0.1:6379> hgetall user:2:info 1) "age" 2) "30" 3) "name" 4) "kaka" 5) "page" 6) "50" # hvals key:返回hash key对应全部filed的value 127.0.0.1:6379> hvals user:2:info 1) "30" 2) "kaka" 3) "50" # hkeys key:返回hash key对应全部field 127.0.0.1:6379> hkeys user:2:info 1) "age" 2) "name" 3) "page"
string | hash |
---|---|
get | hget |
set setnx | hset hsetnx |
del | hdel |
incr incrby decr decrby | hincrby |
mset | hmset |
mget | hmget |
命令 | 时间复杂度 |
---|---|
hget hset hdel | O(1) |
hexists | O(1) |
hincrby | O(1) |
hgetall hvals hkeys | O(n) |
hmget hmset | O(n) |
实战
hincrby user:1:info pageview count
缓存视频的基本信息(数据源在MySQL中)
伪代码以下:
public VideoInfo get(long id) { String redisKey = redisPrefix + id; Map<String, String> hashMap = redis.hgetAll(redisKey); VideoInfo videoInfo = transferMapToVideo(hashMap); if (videoInfo == null) { videoInfo = mysql.get(id); if (videoInfo != null) { redis.hmset(redisKey, transferVideoToMap(videoInfo)); } } return videoInfo; }
有序可重复列表
数据结构
命令
实战规则
集合元素无序不可重复
API介绍
127.0.0.1:6379> sadd user:1:follow it news his sports (integer) 4 127.0.0.1:6379> smembers user:1:follow 1) "news" 2) "his" 3) "it" 4) "sports" 127.0.0.1:6379> spop user:1:follow "news" 127.0.0.1:6379> smembers user:1:follow 1) "his" 2) "it" 3) "sports" 127.0.0.1:6379> scard user:1:follow (integer) 3 127.0.0.1:6379> sismember user:1:follow entertainment (integer) 0
有序不可重复集合
API介绍
API使用演示
# zadd 添加集合元素 127.0.0.1:6379> zadd player:rank 1000 ronaldo 900 messi 800 c-ronaldo 600 kaka (integer) 4 # zscore 获取分值 127.0.0.1:6379> zscore player:rank kaka "600" # zcard 获取元素个数 127.0.0.1:6379> zcard player:rank (integer) 4 # zrank 获取排名,从0开始算 127.0.0.1:6379> zrank player:rank ronaldo (integer) 3 # zrem 删除元素 127.0.0.1:6379> zrem player:rank messi (integer) 1 # zrange 获取指定索引范围内的升序元素 127.0.0.1:6379> zrange player:rank 0 -1 withscores 1) "kaka" 2) "600" 3) "c-ronaldo" 4) "800" 5) "ronaldo" 6) "1000"
127.0.0.1:6379> zadd player:rank 1000 ronaldo 900 messi 800 c-ronaldo 600 kaka (integer) 4 127.0.0.1:6379> zrange player:rank 0 -1 1) "kaka" 2) "c-ronaldo" 3) "messi" 4) "ronaldo" 127.0.0.1:6379> zcount player:rank 700 901 (integer) 2 127.0.0.1:6379> zrangebyscore player:rank 700 901 1) "c-ronaldo" 2) "messi" 127.0.0.1:6379> zremrangebyrank player:rank 0 1 (integer) 2 127.0.0.1:6379> zrange player:rank 0 -1 1) "messi" 2) "ronaldo" 127.0.0.1:6379> zrange player:rank 0 -1 withscores 1) "messi" 2) "900" 3) "ronaldo" 4) "1000"
API查漏补缺
zset总结
操做类型 | 命令 |
---|---|
基本操做 | zadd zrem zcard zincrby zscore |
范围操做 | zrange zrangebyscore zcount zremrangebyrank |
集合操做 | zunionstore zinterstore |
Redis开源客户端涵盖各类语言:
这里主要详细介绍下Redis的Java开源客户端工具Jedis的使用
获取jedis
<dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.0</version> <type>jar</type> <scope>compile</scope> </dependency>
jedis直连
Jedis jedis = new Jedis("127.0.0.1", 6379); jedis.set("hello", "world"); String value = jedis.get("hello");
Jedis构造函数
Jedis(String host, int port, int connectionTimeout, int soTimeout)
这里只列了经常使用参数,其余像密码,或基于安全的SSL,用到的时候自行查阅便可。
简单使用
// 1. string jedis.set("hello", world); // OK jedis.get("hello"); // "world" jedis.incr("counter"); // 1
// 2. hash jedis.hset("myhash", "f1", "v1"); jedis.hset("myhash", "f2", "v2"); jedis.hgetAll("myhash"); // {f1=v1, f2=v2}
// 3. list jedis.rpush("mylist", 1); jedis.rpush("mylist", 2); jedis.rpush("mylist", 3); jedis.lrange("mylist", 0, -1); // [1, 2, 3]
// 4. set jedis.sadd("myset", "a"); jedis.sadd("myset", "b"); jedis.sadd("myset", "a"); jedis.smembers("myset"); // [b, a]
// 5. zset jedis.zadd("myzset", 99, "tom"); jedis.zadd("myzset", 66, "peter"); jedis.zadd("myzset", 33, "james"); jedis.zrangeWithScores("myzset", 0, -1); // [[["james"], 33.0], [["peter"], 66.0], [["tom"], 99.0]]
Jedis链接池
GenericObjectPoolConfig poolConfig = new GenericObjectPollConfig(); JedisPool jedisPool = new JedisPool(poolConfig, "127.0.0.1", 6379);
Jedis jedis = null; try { // 1. 从链接池获取Jedis对象 jedisPool.getResource(); // 2. 执行操做 jedis.set("hello", "world"); } catch(Exception e) { e.printStackTrace(); } finally { if (jedis != null) { // 3. 若是使用JedisPool,close操做不是关闭链接,而是归还链接池 jedis.close(); } }