Redis系列-Redis筆記(一)

Redis基礎

Redis安裝

# 下載
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

Redis啓動方式

  • 直接啓動:redis-server
  • 動態參數啓動:redis-server --port 6380
  • 指定配置文件啓動:redis-server configPath

生產環境建議指定配置文件啓動,單機多實例配置文件能夠用端口區分開,好比像下面這樣: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客戶端

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基礎配置

  • daemonize:是否守護進程(no|yes)
  • port:Redis對外端口
  • logfile:Redis系統日誌
  • dir:Redis工做目錄

除了上面幾個最基本的配置,redis還有下面這些高級配置:sql

  1. RDB config
  2. AOF config
  3. show Log config
  4. maxMemory等等

    後續穿插講解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

Redis API

通用命令

  • keys [pattern]:顯示通配符匹配的key
  • dbsize:查看key總數
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
  • exist key:判斷key是否存在
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
  • del key [key...] 刪除指定key-value
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)
  • expire key seconds 設置key過時時間
  • ttl key 查看key的剩餘過時時間
  • persist key 刪除key的過時設置
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"
  • type key 查看數據類型

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)

數據結構和內部編碼

clipboard.png

單線程

Redis單線程設計,所以使用時要注意幾點:緩存

  1. 一次只運行一條命令
  2. 不要執行長(慢)命令

長(慢)命令:keys, flushall, flushdb, slow lua script, mutil/exec, operate big value(collection)

字符串

使用場景

  • 緩存
  • 計數器
  • 分佈式鎖
  • 等等

命令

  • get、set、del
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)
  • incr、decr、incrby、decrby
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"
  • set、setnx、setxx、setex
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)
  • mset、mget
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"
  • getset、append、strlen
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個字節)
  • incrbyfloat、getrange、setrange
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)

實戰

  1. 記錄網站每一個用戶我的主頁的訪問量

    incr userid:pageview(單線程:無競爭)

  2. 緩存視頻的基本信息(數據源在MySQL中)

    clipboard.png

    僞代碼以下:

    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;
      }
  3. 分佈式id生成器

    利用了redis的單線程,即便是多個服務同時來獲取ID,也得一個個來。

哈希

命令

  • hget、hset、hdel
# 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"
  • hexists、hlen
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
  • hmget、hmset
# 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、hvals、hkeys
# 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的api
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)

實戰

  1. 記錄網站每一個用戶我的主頁的訪問量

    hincrby user:1:info pageview count

  2. 緩存視頻的基本信息(數據源在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;
    }

列表

有序可重複列表

數據結構
clipboard.png

clipboard.png

命令

  • rpush、lpush

clipboard.png

clipboard.png

  • linsert

clipboard.png

  • lpop、rpop

clipboard.png

clipboard.png

  • lrem

clipboard.png

clipboard.png

  • ltrim

clipboard.png

clipboard.png

  • lrange

clipboard.png

  • lindex

clipboard.png

  • llen

clipboard.png

  • lset

clipboard.png

  • blpop、brpop

clipboard.png

實戰規則

  • 棧:LPUSH + LPOP
  • 隊列: LPUSH + RPOP
  • 定長集合:LPUSH + LTRIM
  • 消息隊列:LPUSH + BRPOP

集合

集合元素無序不可重複

API介紹

  • sadd srem

clipboard.png

  • scard sismember srandmember smembers

clipboard.png

  • sdiff sinter sunion

clipboard.png

  • 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介紹

  • zadd

clipboard.png

  • zrem

clipboard.png

  • zscore

clipboard.png

  • zincrby

clipboard.png

  • zcard

clipboard.png

  • zrange

clipboard.png

  • zrangebyscore

clipboard.png

  • zcount

clipboard.png

  • zremrangebyrank

clipboard.png

  • zremrangebyscore

clipboard.png

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查漏補缺

  • zrevrank:zrank的反排
  • zrevrange zrange的反排
  • zrevrangebyscore zrangebyscore的反排
  • zinterstore 集合交集運算並存儲
  • zunionstore 集合並集運算並存儲

zset總結

操做類型 命令
基本操做 zadd zrem zcard zincrby zscore
範圍操做 zrange zrangebyscore zcount zremrangebyrank
集合操做 zunionstore zinterstore

Redis開源客戶端

Redis開源客戶端涵蓋各類語言:

clipboard.png

參考:https://redis.io/clients

Java客戶端Jedis

這裏主要詳細介紹下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)
  • host: Redis節點機器IP
  • post: Redis端口
  • connectionTimeout: 鏈接超時時間
  • 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();
    }
}
相關文章
相關標籤/搜索