美文网首页
Redis学习笔记

Redis学习笔记

作者: 智明书 | 来源:发表于2019-06-14 21:13 被阅读0次

Redis入门

Redis(Remote directory server,远程字典服务器)是完全开源免费的,用C语言编写的,遵守BSD协议,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。

Redis是单进程

Redis作者antirez,也是dump1090的作者

Redis写每秒8w,读每秒11w

Redis能读的速度是110000次/s,写的速度是81000次/s

相比memcached,Redis支持更丰富的数据结构,例如hashes, lists, sets等,同时支持数据持久化。除此之外,Redis还提供一些类数据库的特性,比如事务,HA,主从库。可以说Redis兼具了缓存系统数据库的一些特性,因此有着丰富的应用场景。

redis指令不区分大小写,但是出于规范考虑,应该使用大写

redis中存放的是区分大小写的

redis的值是否区分大小写?????

Redis 与其他 key - value 缓存产品有以下三个特点

  • Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用
  • Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储(memecache只支持KV的数据类型)
  • Redis支持数据的备份,即master-slave模式的数据备份(虽然数据放在内存中, 但是写到硬盘)

统一密码管理,16个数据库都使用一个密码

默认端口是6379(数字键盘的merz,而MERZ取自意大利歌女Alessia Merz的名字)


安装&执行

使用gcc执行make

make install

安装在了/usr/local/bin

redis-server /opt/redis-4.0.9/redis.conf

redis-cli -p 6379  

127.0.0.1:6379> set k1 hello
OK
127.0.0.1:6379> get k1
"hello"

# 关闭
127.0.0.1:6379> SHUTDOWN 

杂的命令

SELECT index 切换到指定的数据库

默认16个数据库,在业务上,某些服务找一号库,某些服务找二号库,这样1.有分担,2.逻辑上清晰

select 0
select 15
select 16   // 错误

PING 查看服务是否运行

QUIT 关闭当前连接

# 查看value的类型
127.0.0.1:6379>  type k1
string

dbsize,当前数据库中key的数量

keys *,列出所有的key

127.0.0.1:6379> dbsize
(integer) 2
127.0.0.1:6379> keys *
1) "k1"
2) "k2"

FLUSHALL 清除所有库的数据(一定不要用这个命令!!!)

FLUSHDB 清除当前库的数据

127.0.0.1:6379> info
# Server
redis_version:3.2.100
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:dd26f1f93c5130ee
redis_mode:standalone
os:Windows
arch_bits:64
multiplexing_api:WinSock_IOCP
process_id:1528
run_id:885af51c70425c50f51ecfff882006b0dfbe8349
tcp_port:6379
uptime_in_seconds:5168
uptime_in_days:0
hz:10
lru_clock:14086418
executable:C:\Program Files\Redis\redis-server.exe
config_file:C:\Program Files\Redis\redis.windows-service.conf

# Clients
connected_clients:1
client_longest_output_list:0
client_biggest_input_buf:0
blocked_clients:0

# Memory
used_memory:689664
used_memory_human:673.50K
used_memory_rss:651776
used_memory_rss_human:636.50K
used_memory_peak:1657216
used_memory_peak_human:1.58M
total_system_memory:0
total_system_memory_human:0B
used_memory_lua:37888
used_memory_lua_human:37.00K
maxmemory:0
maxmemory_human:0B
maxmemory_policy:noeviction
mem_fragmentation_ratio:0.95
mem_allocator:jemalloc-3.6.0

# Persistence
loading:0
rdb_changes_since_last_save:0
rdb_bgsave_in_progress:0
rdb_last_save_time:1524031052
rdb_last_bgsave_status:ok
rdb_last_bgsave_time_sec:0
rdb_current_bgsave_time_sec:-1
aof_enabled:1
aof_rewrite_in_progress:0
aof_rewrite_scheduled:0
aof_last_rewrite_time_sec:-1
aof_current_rewrite_time_sec:-1
aof_last_bgrewrite_status:ok
aof_last_write_status:ok
aof_current_size:58101543
aof_base_size:58101204
aof_pending_rewrite:0
aof_buffer_length:0
aof_rewrite_buffer_length:0
aof_pending_bio_fsync:0
aof_delayed_fsync:0

# Stats
total_connections_received:1
total_commands_processed:22
instantaneous_ops_per_sec:0
total_net_input_bytes:688
total_net_output_bytes:5891669
instantaneous_input_kbps:0.00
instantaneous_output_kbps:0.00
rejected_connections:0
sync_full:0
sync_partial_ok:0
sync_partial_err:0
expired_keys:0
evicted_keys:0
keyspace_hits:8
keyspace_misses:0
pubsub_channels:0
pubsub_patterns:0
latest_fork_usec:52006
migrate_cached_sockets:0

# Replication
role:master
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0

# CPU
used_cpu_sys:0.95
used_cpu_user:5.55
used_cpu_sys_children:0.00
used_cpu_user_children:0.00

# Cluster
cluster_enabled:0

# Keyspace
db0:keys=3,expires=0,avg_ttl=0

Redis数据类型 五+一

Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储。这就是为什么redis干掉了memcache的原因

string(字符串)

hash(哈希)

list(列表)

set(集合)

zset(sorted set:有序集合)

Key 键 KEYS EXISTS MOVE EXPIRE TTL

keys *
# 判断一个key是否存在
127.0.0.1:6379> EXISTS k1
(integer) 1
127.0.0.1:6379> set k1 hello
OK
127.0.0.1:6379> get k1
"hello"
# 把k1移动到1号库
127.0.0.1:6379> MOVE k1 1
(integer) 0
# EXPIRE给指定的key设置过期时间;
# TTL 查看key还有多久过期(-1永不过期,-2已经过期)
# 比如某个促销活动就持续三天,就可以设置key的过期时间
127.0.0.1:6379> TTL k1
(integer) -1

# 设置k2在10s后消失,过期后,k2会自动被移出数据库
127.0.0.1:6379> expire k2 10
(integer) 1
127.0.0.1:6379> ttl k2
(integer) 2
127.0.0.1:6379> ttl k2
(integer) 0
127.0.0.1:6379> ttl k2
(integer) -2                # 过期了

# 再次查看,k2已经是空值了
127.0.0.1:6379> get k2
(nil)

# 查看keys *,已经没有k2了
127.0.0.1:6379> keys *
1) "k1"

String 字符串 SET GET APPEND STRLEN INCR DECR INCBY DECRBY

string类型是二进制安全的。意思是redis的string可以包含任何数据。比如jpg图片或者序列化的对象 。

String是单值单value

127.0.0.1:6379> get k1
"v1"
127.0.0.1:6379> set k1 v11
OK
127.0.0.1:6379> get k1
"v11"                   # 被覆盖
# append和STRLEN
127.0.0.1:6379> get k1
"v11"

127.0.0.1:6379> APPEND k1 hello
(integer) 8

127.0.0.1:6379> get k1
"v11hello"

127.0.0.1:6379> STRLEN k1
(integer) 8
# INCR,DECR,INCRBY,DECBY
# 注意:value必须是数字!!!!

127.0.0.1:6379> set k2 1
OK
127.0.0.1:6379> INCR k2
(integer) 2
127.0.0.1:6379> INCR k2
(integer) 3
127.0.0.1:6379> INCR k2
(integer) 4
127.0.0.1:6379> DECR k2
(integer) 3
127.0.0.1:6379> DECR k2
(integer) 2
127.0.0.1:6379> DECR k2
(integer) 1
127.0.0.1:6379> DECR k2
(integer) 0
127.0.0.1:6379> DECR k2
(integer) -1
127.0.0.1:6379> DECR k2
(integer) -2

# INCRBY DECRBY
127.0.0.1:6379> INCRBY k2 3     // 每次递增3
(integer) 1
127.0.0.1:6379> INCRBY k2 3
(integer) 4
127.0.0.1:6379> DECRBY k2 2
(integer) 2
127.0.0.1:6379> DECRBY k2 2
(integer) 0
# getrange setrange (范围内取值,范围内设置,类似Java的substring)
127.0.0.1:6379> set k3 helloworld
OK
127.0.0.1:6379> get k3
"helloworld"
127.0.0.1:6379> getrange k3 0 4
"hello"
127.0.0.1:6379> setrange k3 5 xxxxx
(integer) 10
127.0.0.1:6379> get k3
"helloxxxxx"
# setex(set with expire)
127.0.0.1:6379> setex k4 10 v4
OK
127.0.0.1:6379> ttl k4
(integer) 6
127.0.0.1:6379> ttl k4
(integer) 4
127.0.0.1:6379> ttl k4
(integer) 0
127.0.0.1:6379> ttl k4
(integer) -2                // k4死掉了
# setnx(set if not exist)只有key不存在的时候才能set
127.0.0.1:6379> get k1
"v11hello"
127.0.0.1:6379> setnx k1 v1
(integer) 0
127.0.0.1:6379> get k1
"v11hello"
# mset mget msetnx 连续多个的设置

127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3
OK
127.0.0.1:6379> mget k1 k2 k3 k4
1) "v1"
2) "v2"
3) "v3"
4) (nil)

msetnx,如果部分已经存在了,那所有的都不成功

List 列表 LPUSH RPUSH LPOP RPOP LRANGE

Redis的List是类似Java中的ArrayList还是LinkedList?

答案:它底层是一个链表

正因为是链表,因此可以在list的头部和尾部添加一个元素

因此List在头部或者尾部插入元素的效率都很高,但是在中间插入元素,效率就很低

# lpush rpush lrange

# lpush是一个栈,后进的先出
127.0.0.1:6379> lpush list1 1 2 3 4 5
(integer) 5
127.0.0.1:6379> lrange list1 0 -1
1) "5"
2) "4"
3) "3"
4) "2"
5) "1"

# rpush是怎么进来的怎么出去,是一个队列
127.0.0.1:6379> rpush list2 1 2 3 4 5
(integer) 5
127.0.0.1:6379> lrange list2 0 -1
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
# lpop和rpop

127.0.0.1:6379> lrange list1 0 -1
1) "5"
2) "4"
3) "3"
4) "2"
5) "1"
127.0.0.1:6379> lpop list1      
"5"
127.0.0.1:6379> rpop list1
"1"
# lindex 按照索引下标获取元素
127.0.0.1:6379> lrange list1 0 -1
1) "4"
2) "3"
3) "2"
127.0.0.1:6379> lindex list1 0
"4"
127.0.0.1:6379> lindex list1 1
"3"
127.0.0.1:6379> lindex list1 2
"2"
127.0.0.1:6379> lindex list1 3
(nil)
# lrem 删除n个value

127.0.0.1:6379> lrange list4 0 -1
1) "3"
2) "3"
3) "3"
4) "2"
5) "2"
6) "2"
7) "1"
8) "1"
9) "1"
127.0.0.1:6379> lrem list4 2 1          // 删除两个1
(integer) 2
127.0.0.1:6379> lrange list4 0 -1
1) "3"
2) "3"
3) "3"
4) "2"
5) "2"
6) "2"
7) "1"
# ltrim key 开始index 结束index 
# rpoplpush 从一个list取数据,然后添加到另一个数据中

127.0.0.1:6379> lpush list01 1 2 3
(integer) 3
127.0.0.1:6379> lrange list01 0 -1
1) "3"
2) "2"
3) "1"

127.0.0.1:6379> lpush list02 4 5 6
(integer) 3
127.0.0.1:6379> lrange list02 0 -1
1) "6"
2) "5"
3) "4"

127.0.0.1:6379> rpoplpush list01 list02
"1"
127.0.0.1:6379> lrange list02 0 -1
1) "1"
2) "6"
3) "5"
4) "4"

# lset 更新指定索引的元素 

127.0.0.1:6379> lrange list01 0 -1
1) "3"
2) "2"
127.0.0.1:6379> lset list01 0 1
OK
127.0.0.1:6379> lrange list01 0 -1
1) "1"
2) "2"

# linsert 在某个元素之前之后插入

127.0.0.1:6379> lrange list01 0 -1
1) "1"
2) "2"
127.0.0.1:6379> linsert list01 before 1 0
(integer) 3
127.0.0.1:6379> lrange list01 0 -1
1) "0"
2) "1"
3) "2"


Set 集合 SADD SMEMBERS SISMEMBER

# sadd 添加,smembers 查看set中所有元素,sismember 查看一个值是不是set的元素

127.0.0.1:6379> sadd set01 1 1 2 2 3 3
(integer) 3
127.0.0.1:6379> smembers set01
1) "1"
2) "2"
3) "3"

127.0.0.1:6379> sismember set01 1
(integer) 1
127.0.0.1:6379> sismember set01 5
(integer) 0
# scard 查看有多少个元素

127.0.0.1:6379> smembers set01
1) "1"
2) "2"
3) "3"

127.0.0.1:6379> scard set01
(integer) 3
# srem 移出某个元素
# srandmember key N (从某个set中随机的取出N个元素,比如一个砸金蛋的业务,从几万个人中随机的选出N个人)
# spop key 随机一个元素出栈


127.0.0.1:6379> sadd set01 1 2 3 4 5 6 7 8 9
(integer) 7
127.0.0.1:6379> SMEMBERS set01
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
6) "6"
7) "7"
8) "8"
9) "9"
127.0.0.1:6379> SRANDMEMBER set01 3
1) "6"
2) "3"
3) "4"
127.0.0.1:6379> SRANDMEMBER set01 3
1) "1"
2) "3"
3) "2"
127.0.0.1:6379> 

127.0.0.1:6379> spop set01
"4"
127.0.0.1:6379> spop set01
"7"
数学的差集 sdiff 交集sinter 并集 sunion

127.0.0.1:6379> SMEMBERS set01
1) "1"
2) "2"
3) "3"
4) "5"
5) "6"
6) "8"
7) "9"
127.0.0.1:6379> SMEMBERS set02
1) "9"
2) "10"
3) "11"
4) "12"

127.0.0.1:6379> sdiff set01 set02           //在set01中,但是不再set02中
1) "1"
2) "2"
3) "3"
4) "5"
5) "6"
6) "8"
127.0.0.1:6379> sdiff set02 set01       // //在set02中,但是不再set01
1) "10"
2) "11"
3) "12"

127.0.0.1:6379> sinter set01 set02
1) "9"

127.0.0.1:6379> sunion set01 set02
 1) "1"
 2) "2"
 3) "3"
 4) "5"
 5) "6"
 6) "8"
 7) "9"
 8) "10"
 9) "11"
10) "12"

Hash 哈希 重中之重

hash是一个键值对的集合,类似Java中的Map<String, Object>

hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。

假设有两个java类,一个user,一个school

class User {
    private String name;
    private Integer id;
}

class School {
    private String name;
    private String city;
    private Integer level;
}
# hset hget hmset hmget 

127.0.0.1:6379> hset user01 id 1
(integer) 1
127.0.0.1:6379> hset user01 name jack
(integer) 1

127.0.0.1:6379> hmget user01 id name
1) "1"
2) "jack"

127.0.0.1:6379> hmset user02 id 2 name zhichao
OK
127.0.0.1:6379> hmget user02 id name
1) "2"
2) "zhichao"
# 

127.0.0.1:6379> hgetall user01
1) "id"
2) "1"
3) "name"
4) "jack"
# hdel hgetall

127.0.0.1:6379> hdel user01 name
(integer) 1
127.0.0.1:6379> hgetall user01
1) "id"
2) "1"
# hkeys hvals

127.0.0.1:6379> hkeys user02
1) "name"
2) "id"
127.0.0.1:6379> hvals user02
1) "zhichao"
2) "2"

Zset 有序集合

相对于Set,Zset的每一个元素都会关联一个double类型的分数,通过分数来排序

游戏用这个数据结构非常的多,就是通过Zset进行排序的

如果两个元素的double的分数相同,怎么办?

# zadd zrange 

127.0.0.1:6379> zadd zset01 50 v1 60 v2 70 v3 80 v4 90 v5
(integer) 5
127.0.0.1:6379> zadd zset01 50.5 v6
(integer) 1

127.0.0.1:6379> zrange zset01 0 -1  // 从小到大排列
1) "v1"
2) "v6"
3) "v2"
4) "v3"
5) "v4"
6) "v5"

解析配置文件 redis.conf

在redis启动的时候,需要指定配置文件

./redis-server /path/to/redis.conf
# Note on units: when memory size is needed, it is possible to specify
# it in the usual form of 1k 5GB 4M and so forth:
#
# 1k => 1000 bytes
# 1kb => 1024 bytes
# 1m => 1000000 bytes
# 1mb => 1024*1024 bytes
# 1g => 1000000000 bytes
# 1gb => 1024*1024*1024 bytes

redis日志的记录方式

dir ./是rdb和aom两者都生效的?

常用配置

参数说明
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. 设置当本机为slave服务时,设置master服务的IP地址及端口,在Redis启动时,它会自动从master进行数据同步
    slaveof <masterip> <masterport>
  14. 当master服务设置了密码保护时,slave服务连接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

INCLUDES 可以包含其他配置文件

Include one or more other config files here. This is useful if you have a standard template that goes to all Redis servers but also need to customize a few per-server settings.

# include /path/to/local.conf
# include /path/to/other.conf

MODULES 不知道


NETWORK

让redis只监听指定ip地址下的客户端连接

如果不配置的话,redis服务会暴露在整个公网下,这样很危险

tcp-backlog 511

# Close the connection after a client is idle for N seconds (0 to disable)
timeout 0

# A reasonable value for this option is 300 seconds, which is the new
# Redis default starting with Redis 3.2.1.
tcp-keepalive 300

GENERAL


SNAPSHOTTING 和rdb有关

Redis是内存数据库,掉点应该保存到硬盘上

# Save the DB on disk:
#
#   save <seconds> <changes> 如果有N个键有更改,那么X秒之后进行备份
save 900 1
save 300 10
save 60 10000 

一分钟之内改了1w次,5分钟之内改了10次,15分钟之内改了1次

如果不想保存备份,就不设置任何save指令,或者save ""

#   Note: you can disable saving completely by commenting out all "save" lines.
#
#   It is also possible to remove all the previously configured save
#   points by adding a save directive with a single empty string argument
#   like in the following example:
#
#   save ""

stop-writes-on-bgsave-error 如果后台在save的时候发生了错误,那么前台是否要停止写操作,默认yes

如果不介意数据的一致性,可以改成no

# By default Redis will stop accepting writes if RDB snapshots are enabled
# (at least one save point) and the latest background save failed.
# This will make the user aware (in a hard way) that data is not persisting
# on disk properly, otherwise chances are that no one will notice and some
# disaster will happen.
#
# If the background saving process will start working again Redis will
# automatically allow writes again.
#
# However if you have setup your proper monitoring of the Redis server
# and persistence, you may want to disable this feature so that Redis will
# continue to work as usual even if there are problems with disk,
# permissions, and so forth.
stop-writes-on-bgsave-error yes

是否启用LZF压缩算法来压缩.rdb文件,默认是yes

# Compress string objects using LZF when dump .rdb databases?
# For default that's set to 'yes' as it's almost always a win.
# If you want to save some CPU in the saving child set it to 'no' but
# the dataset will likely be bigger if you have compressible values or keys.
rdbcompression yes

在存储快照后,会通过CRC64进行数据校验

但是在保存或加载rdb文件的时候,会增加10%的性能消耗

如果希望提升性能,这一项可以改成no

# Since version 5 of RDB a CRC64 checksum is placed at the end of the file.
# This makes the format more resistant to corruption but there is a performance
# hit to pay (around 10%) when saving and loading RDB files, so you can disable it
# for maximum performances.
#
# RDB files created with checksum disabled have a checksum of zero that will
# tell the loading code to skip the check.
rdbchecksum yes
# The filename where to dump the DB
dbfilename dump.rdb

指定rdb的存放目录(这个配置说明是在当前目录存放数据库)(“当前”指的是,从那个目录启动的redis,哪个目录就是当前)

# The working directory.
#
# The DB will be written inside this directory, with the filename specified
# above using the 'dbfilename' configuration directive.
#
# The Append Only File will also be created inside this directory.
#
# Note that you must specify a directory here, not a file name.
dir ./

REPLICATION


SECURITY

Redis的安全理念是,既然你已经能进入到linux,就默认你是安全的用户,可以直接使用redis

但是如果你是一个无聊的强迫症, 那可以设置密码

一旦设置了密码,那执行命令之前,需要先输入密码

# Require clients to issue AUTH <PASSWORD> before processing any other
# commands.
127.0.0.1:6379> config get requirepass
1) "requirepass"
2) ""

# 设置密码
127.0.0.1:6379> config set requirepass "qweasd"
OK

# 此时再请求,就需要输入密码
127.0.0.1:6379> config get requirepass
(error) NOAUTH Authentication required.

# 输入密码
127.0.0.1:6379> auth qweasd
OK
127.0.0.1:6379> config get requirepass
1) "requirepass"
2) "qweasd"
127.0.0.1:6379> ping
PONG


# 取消密码
127.0.0.1:6379> config set requirepass ""
OK

CLIENTS

默认最大连接数是10000,设置redis同时可以与多少个客户端进行连接。默认情况下为10000个客户端。
如果达到了此限制,redis则会拒绝新的连接请求,并且向这些连接请求方发出“max number of clients reached”以作回应。

# maxclients 10000

MEMORY MANAGEMENT

When the memory limit is reached Redis will try to remove keys according to the eviction policy selected (see maxmemory-policy).

# maxmemory <bytes> 

内存的移除策略(当内存不够用了,应该怎么清理内存),默认是noeviction,永不过期

# MAXMEMORY POLICY: how Redis will select what to remove when maxmemory
# is reached. You can select among five behaviors:
#
# volatile-lru -> Evict using approximated LRU among the keys with an expire set.
# allkeys-lru -> Evict any key using approximated LRU.
# volatile-lfu -> Evict using approximated LFU among the keys with an expire set.
# allkeys-lfu -> Evict any key using approximated LFU.
# volatile-random -> Remove a random key among the ones with an expire set.
# allkeys-random -> Remove a random key, any key.
# volatile-ttl -> Remove the key with the nearest expire time (minor TTL)
# noeviction -> Don't evict anything, just return an error on write operations.
#
# LRU means Least Recently Used
# LFU means Least Frequently Used
#

# The default is:
#
# maxmemory-policy noeviction

  • noeviction: 不进行置换,表示即使内存达到上限也不进行置换,所有能引起内存增加的命令都会返回error
  • allkeys-lru: 优先删除掉最近最不经常使用的key,用以保存新数据
  • volatile-lru: 只从设置失效(expire set)的key中选择最近最不经常使用的key进行删除,用以保存新数据
  • allkeys-random: 随机从all-keys中选择一些key进行删除,用以保存新数据
  • volatile-random: 只从设置失效(expire set)的key中,选择一些key进行删除,用以保存新数据
  • volatile-ttl: 只从设置失效(expire set)的key中,选出存活时间(TTL)最短的key进行删除,用以保存新数据

Redis有这样一个配置——maxmemory-samples,Redis的LRU是取出配置的数目的key,然后从中选择一个最近最不经常使用的key进行置换,默认的5

maxmemory-samples 5

LAZY FREEING


APPEND ONLY MODE

默认是关闭的

# By default Redis asynchronously dumps the dataset on disk. This mode is
# good enough in many applications, but an issue with the Redis process or
# a power outage may result into a few minutes of writes lost (depending on
# the configured save points).
#
# The Append Only File is an alternative persistence mode that provides
# much better durability. 

# AOF和RDB是可以并存的,如果AOF存在,则redis在启动的时候会load AOF
# AOF and RDB persistence can be enabled at the same time without problems.
# If the AOF is enabled on startup Redis will load the AOF, that is the file
# with the better durability guarantees.

appendonly no
# The name of the append only file (default: "appendonly.aof")
appendfilename "appendonly.aof"
# appendfsync always        // 同步持久化,每次发生数据变更会立刻记录到磁盘,性能差但是数据完整性好.但是在双十一大型活动的时候, 这个选项会严重拖慢机器性能

appendfsync everysec        // 默认配置,异步操作,每一秒记录一次,如果一秒内宕机,则有数据丢失

# appendfsync no    // 
no-appendfsync-on-rewrite no

LUA SCRIPTING

REDIS CLUSTER

CLUSTER DOCKER/NAT support

SLOW LOG

LATENCY MONITOR

EVENT NOTIFICATION

ADVANCED CONFIG

ACTIVE DEFRAGMENTATION


Redis的持久化 Persistence

Redis支持数据持久化,可以将内存中的数据保存在硬盘上,然后重启的时候再次加载进行使用


RDB Redis DataBase 指定的时间间隔对数据进行快照存储

在指定的时间间隔之内将内存中的数据快照写入磁盘,即Snapshot快照

一个子进程,将数据写入到一个临时文件中,等待这个持久化结束后,将临时文件的内容替换到上一次持久化的文件中

主进程在这期间不进行IO操作(???不懂)

RDB持久化保存的是dump.rdb文件

只在本机保存dump.rdb文件肯定是不安全的,因此当拿到dump.rdb文件后,运维人员会写定时的脚本,定期的将本台的rdb文件备份到另一台机器上


如何触发rdb备份

TODO:执行flushall之后,也会立刻生成rdb文件(但是别手贱)

  1. 按照save的配置规则
  2. 使用save或bgsave命令
    1. save 命令会阻塞其他操作, 此时redis只能进行保存
    2. bgsave(background)后台异步的备份
127.0.0.1:6379> bgsave 
Background saving started

恢复dump.rdb

只要有了dump.rdb,redis启动后会自动进行快速的回复

127.0.0.1:6379> config get dir
1) "dir"
2) "C:\\Program Files\\Redis"

修复dump.rdb redis-check-dump


AOF Append Only File 记录的写指令,服务器重启后会再执行一遍写指令

RDB会丢失最后一次的数据(比如在默认的配置文件中,会丢失15min内的数据),技术大拿们有强迫症,比如要解决这个问题,推出了AOF:

AOF 以日志形式记录了每个写操作(注意:只记录写操作),在redis启动之后会按照日志中的记录重新执行一遍写指令

appendonly.aof文件,内容如下

*2
$6
SELECT
$1
0
*3
$3
set
$2
k1
$2
v1
*3
$3
set
$2
k2
$2
v2

恢复

flushall之后,dump.rdb中是空,appendonly.aof中也是空


修复

如果appendonly.aof文件中有损坏,是否能启动服务???

答:如果appendonly.aof文件有损坏,服务不会启动!!!

但是有一个redis-check-aof.exe

C:\Program Files\Redis>redis-check-aof.exe ./appendonly.aof
0x              51: Expected prefix ''', got: '*'
AOF analyzed: size=90, ok_up_to=81, diff=9
AOF is not valid
# --fix 参数可以修复aof文件

C:\Program Files\Redis>redis-check-aof.exe --fix ./appendonly.aof
0x              68: Expected prefix '1', got: '*'
AOF analyzed: size=116, ok_up_to=104, diff=12
This will shrink the AOF from 116 bytes, with 12 bytes, to 104 bytes
Continue? [y/N]: y
Successfully truncated AOF

rewrite

AOF什么都好,但是AOF采用文件追加方式,文件会越来越大。尤其是大型活动的时候,文件瞬间就撑爆了。为避免出现此种情况,新增了重写机制,当AOF文件的大小超过所设定的阈值时,Redis就会启动AOF文件的内容压缩,只保留可以恢复数据的最小指令集。可以使用命令bgrewriteaof

AOF自身有一个精简的能力:

set k1 10
incr k1 
incr k1 
incr k1 
... // 增加了1w次

这种操作在AOF中会被优化成
set k1 100000           

精简典型的场景比如:

1+1+1+1...+1 = 8
2<<3 = 8

精简的触发条件是:当AOF文件的大小超过了所设定的阈值,Redis就会启动AOF文件的压缩

只保留能够恢复数据的最小指令集

bgrewriteaof

触发rewrite

  1. bgrewriteof
127.0.0.1:6379> BGREWRITEAOF
Background append only file rewriting started
  1. 当AOF文件大小是上次rewire后大小的一倍,且文件大于64M时触发
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

在生产环境,3G是起步价

重写的原理

当AOF过大时,会fork出一个新进程来将文件重写(类似dump的过程),


总结

如何选择:

  • 如果你只希望用redis做缓存,即只希望数据在服务器运行的时候存在,此时就不需要使用持久化
  • 如果允许一些数据的丢失,就优先使用rdb

如果两个持久化策略都存在

可以同时使用两种持久化,重启后会使用AOF进行恢复,因为AOF的数据集更完整。

但是作者不建议只是用AOF,此时RDB是作为一个以防万一的backup

RDB

  • 优势:对数据的完整性要求不高的时候,适合大规模的数据恢复
  • 劣势:
    • 因为是隔一段时间进行备份,那万一服务挂掉了,那数据不会即时保存
    • 备份的时候会fork一个子线程进行备份,这样占用内存增大一倍

AOF

  • 优势: 配置非常的灵活,appendsync有always,everysec,no
  • 劣势:
    • 相同数据集的数据要远大于rdb,恢复速度慢于rdb
    • aof运行效率慢于rdb

Redis的事务 MULTI EXEC DISCARD WATCH

一个事务的所有命令都会被序列化,按顺序地

Starting with version 2.2, Redis allows for an extra guarantee to the above two, in the form of optimistic locking in a way very similar to a check-and-set (CAS) operation. This is documented later on this page.

一个事务顺利执行会有三个阶段

  • MULTI 开启一个事务
  • 入列
  • EXEC 执行

Calling DISCARD instead will flush the transaction queue and will exit the transaction.(比如在敲命令的时候,打错了,此时就可以用DISCARD放弃此次事务)

Redis事务的特性:

  • 没有隔离级别的概念
  • 但是周扬的脑图说,redis不保证原子性:看冤有头债有主

怎么玩儿

正常执行

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> get k2          // 任何命令都可以放在事务中
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> EXEC
1) OK               // 依次返回各个语句的执行结果
2) OK
3) "v2"
4) OK

放弃事务 DISCARD

比如说我在写一个事务的时候,中间写错了一个数据,可以用DISCARD来放弃此次事务

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set k1 11
QUEUED
127.0.0.1:6379> set k2 22
QUEUED
127.0.0.1:6379> set k3 33
QUEUED
127.0.0.1:6379> DISCARD
OK
127.0.0.1:6379> get k1      // 全部不提交
"v1"

全体连坐

127.0.0.1:6379> get k1
"11"
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> fsjkdl fsa
(error) ERR unknown command 'fsjkdl'        // 在事务中,插入一个错误的命令
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> EXEC        // 整个事务中的操作都不提交
(error) EXECABORT Transaction discarded because o
127.0.0.1:6379> get k1
"11"

冤有头债有主 对的执行 错的抛出

127.0.0.1:6379> set k1 aa
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> incr k1     // aa不能加一,但是被正确的加入到了队列中
QUEUED
127.0.0.1:6379> set k2 22
QUEUED
127.0.0.1:6379> set k3 33
QUEUED
127.0.0.1:6379> set k4 44
QUEUED
127.0.0.1:6379> get k4
QUEUED
127.0.0.1:6379> EXEC        // 只有incr k1没有执行,剩下的都照常执行
1) (error) ERR value is not an integer or out of range
2) OK
3) OK
4) OK
5) "44"

所以redis是部分支持事务,正确的会执行,错误的不执行

watch监控 类似乐观锁

悲观锁:传统的关系型数据库就用到了这种锁机制,比如行锁,表锁、读锁,写锁等,都是操作之前先上锁

其中表锁:一致性最好,但是并发性最差

乐观锁:提交的版本必须要大于记录的当前版本才能执行

乐观锁:提交的版本必须要大于记录的当前版本才能执行

乐观锁:提交的版本必须要大于记录的当前版本才能执行

WATCH 监控一个或者多个key

UNWATCH 取消WATCH命令对所有key的监控

watch基本操作

初始化信用卡的可用余额和欠额

127.0.0.1:6379> set balance 100
OK
127.0.0.1:6379> set debt 0
OK

// 模拟使用信用卡花费20块钱的操作
// 在执行事务之前,先对balance进行WATCH
127.0.0.1:6379> WATCH balance
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> DECRBY balance 20
QUEUED
127.0.0.1:6379> INCRBY debt 20
QUEUED
127.0.0.1:6379> EXEC
1) (integer) 80
2) (integer) 20
127.0.0.1:6379> get balance
"80"

加塞篡改,会发生的事情

// 开启两个客户端

127.0.0.1:6379> WATCH balance
OK                              // 在这期间,有另一个客户端更改了key
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> DECRBY balance 20
QUEUED
127.0.0.1:6379> INCRBY debt 20
QUEUED
127.0.0.1:6379> EXEC
(nil)               // nil

WATCH之后,然后执行EXEC后,这个WATCH之后还有效吗?


Redis的发布订阅 SUBSCRIBE PUBLISH

类似在微信上订阅公众号,然后订阅后就会收到公众号推送的文章

发布订阅是进程之间的一种通讯方式

Redis很有野心,在高速缓存这块已经把memcache干掉了,那能不能再做做MQ?

但是企业中很少有人把Redis当消息中间件

redis 127.0.0.1:6379> SUBSCRIBE redisChat

Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "redisChat"
3) (integer) 1
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"

Redis的复制 Master/Slave

现在最流行主从复制、读写分离

是什么

是主从复制:当主机数据更新后,根据配置的策略,将数据自动同步到备机

其中Master以写为主,Slave以读为主(所以这就是读写分离???)

能干嘛

  • 容灾备份:当主机挂了,从机依旧能够工作
  • 读写分离:不懂为啥要读写分离

怎么玩

只配从机,不能配主机

配置:

  • 启动的时候配置:
  • 配置进从机的redis.conf中:这样即使从机跟主机断开后,再
redis-cli -p 6379
redis-cli -p 6380
redis-cli -p 6381
使用info replication查看

127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0

一主二仆 最常用的配置!!!

让6379为主,6380和6381为仆

// 80和81执行
slaveof 127.0.0.1 6379

通过info replication 查看一下三台机器的状态

// 6379
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:0
master_replid:9e0f66e7759801c7be8cdd6134e0c734b963acb3
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0

// 

注意:从机会把主机上所有的数据都拿到手

  • 如果在79、80、81上都新写一个数据set k6 v6:在主机上可写,但是从机是readonly,不能写(因为读写分离)
  • 当主机出故障了,把79 shutdown,两个从机还是从机(原地待命,不会有人抢成为主)
    • 当此时主机又恢复了(老领导回来了),老领导仍旧是主的身份
  • 当从机出故障了,比如80 shutdown,此时79和81都能够正常的运作。
    • 当此时80从机又恢复了,此时80机器的状态是master(info replication查看),因为该从机目前没有在之前的主从体系中。此时需要重新执行slaveof 127.0.0.1 6379,就会重新回来

薪火相传

主从的问题是,主是固定不变的,分担的任务非常重

薪火相传:去中心化,上一个slave是下一个slave的master,

将81配置成slaveof 127.0.0.1 6381

将80配置成slaveof 127.0.0.1 6380

即79是80的主,80是81的主

此时info replication

79肯定是master
80得到的是salve
81肯定是slave

反客为主

反客为主:当主挂了,从不是向一主二仆那样原地待命,而是直接上位成为主

slavef no one,使当前数据库停止与其他数据库的同步,转而成为新的主

最开始的时候,79是主,80、81是从

然后主79挂掉了(shutdown),在80中执行SLAVEOF no one,此时80成为新的主,此时80可以进行写操作。而此时81有两种选择:1跟着新老板80(执行slaveof 127.0.0.1 6380 ),2默默等着79

然后79重启,此时该机器是master(info replication查看),因为该从机目前没有在之前的主从体系中


哨兵模式 sentinel 最重要 反客为主的自动版

凌晨2点服务挂了,自动有一个从机成为新的主,运维不用起床了。。。

监控主是否故障,如果故障了将根据投票自动将从库专程主库

创建一个sentinel.conf的配置文件

# 最后一个1表示,剩余的从机,谁的票数多余一票,谁就是新的领导
sentinel monitor host6379 127.0.0.1 6379 1

redis有一个服务叫redis-sentinel,用来监控79服务,一旦79挂掉了,就让80和81进行投票,然后选举出新的主机

redis-sentinel /myredis/sentinel.conf

当79服务挂掉了,等几秒钟后,哨兵会选出新的主,比如选出了80服务,则通过info replication查看后,80是master,此时81是slave

此时79如果服务回来了,不是像一仆二主和反客为主那样,79成为一个毫不相干的master。而是启动后,经过哨兵的通知,79会成为一个slave(通过info replication可以查看到)

一组sentinel能够同时监控多个master,应该是在sentinel.conf中写多个语句即可

sentinel monitor host6379 127.0.0.1 6379 1
sentinel monitor hostXXXX 127.0.0.1 6379 1

复制原理

首次复制是全量复制,之后是增量复制

但只要是重新连接master,就要执行一次全量复制


复制的缺点

由于所有写操作都是在master上进行,然后再同步到slave上,从master同步到slave上一定有延迟。


Redis的Java客户端Jedis

需要两个jar包,其中jedis jar依赖于commons-pool2

事务提交

会报错:Exception in thread "main" redis.clients.jedis.exceptions.JedisDataException: EXECABORT Transaction discarded because of previous errors.

public class Test {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1", 6379);

        Transaction transaction = jedis.multi();

        transaction.set("k4", "v4");
        transaction.set("k5", "v5");

        transaction.exec();
    }
}
public class Test {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1", 6379);

        Transaction transaction = jedis.multi();

        transaction.set("k4", "v4");
        transaction.set("k5", "v5");

//        transaction.exec();
        transaction.discard();
    }
}

模拟一个消费的操作

public class TestTX {
    public boolean transMethod() throws InterruptedException {
        Jedis jedis = new Jedis("127.0.0.1", 6379);
        /**
         * 注意:模拟该操作之前,先在redis中建立balance和debt
         */
        int balance;// 可用余额
        int debt;// 欠额
        // 模拟刷卡消费了10元
        int amtToSubtract = 10;

        // 在执行事务之前,需要先watch
        jedis.watch("balance");

//        jedis.set("balance","5");//此句不该出现,讲课方便。模拟其他程序已经修改了该条目
        Thread.sleep(7000);
        balance = Integer.parseInt(jedis.get("balance"));

        // 要消费的金额必须要小于账户的可用余额
        if (balance < amtToSubtract) {
            jedis.unwatch();
            System.out.println("modify");
            return false;

        } else {
            System.out.println("***********transaction");
            Transaction transaction = jedis.multi();
            transaction.decrBy("balance", amtToSubtract);
            transaction.incrBy("debt", amtToSubtract);
            transaction.exec();
            balance = Integer.parseInt(jedis.get("balance"));
            debt = Integer.parseInt(jedis.get("debt"));

            System.out.println("*******" + balance);
            System.out.println("*******" + debt);
            return true;
        }
    }

    /**
     * 通俗点讲,watch命令就是标记一个键,如果标记了一个键,在提交事务前如果该键被别人修改过,那事务就会失败,这种情况通常可以在程序中
     * 重新再尝试一次。
     * <p>
     * 首先标记了键balance,
     * 然后检查余额是否足够,不足就取消标记,并不做扣减;
     * 足够的话,就启动事务进行更新操作
     * <p>
     * 如果在此期间键balance被其它人修改, 那在提交事务(执行exec)时就会报错,
     * 程序中通常可以捕获这类错误再重新执行一次,直到成功。
     *
     * @throws InterruptedException
     */
    public static void main(String[] args) throws InterruptedException {
        TestTX test = new TestTX();
        boolean retValue = test.transMethod();
        System.out.println("main retValue-------: " + retValue);
    }
}

主从复制

启动6379和6380两个服务

主机写,从机读的例子

public class TestMS {
    public static void main(String[] args) {
        Jedis jedisM = new Jedis("127.0.0.1", 6379);
        Jedis jedisS = new Jedis("127.0.0.1", 6380);

        // 让80成为79的从机
      // 注意:这个命令其实不应该在Java中写,而是在linux下通过命令去配置主从关系!!!!
        jedisS.slaveof("127.0.0.1", 6379);

        jedisM.set("class", "1122V2");

        String result = jedisS.get("class");
        System.out.println(result);
    }
}

JedisPool 通过池来获取Jedis实例

从JedisPool中获取jedis实例

然后使用完毕再放回JedisPool中

如何设计一个池子:池子应该是唯一的,因此池子是单例的

public class JedisPoolUtil {
    private static volatile JedisPool jedisPool = null;

    private JedisPoolUtil() {}

    // 获取一个Jedis池子
    public static JedisPool getJedisPoolInstance() {
        if (null == jedisPool) {
            synchronized (JedisPoolUtil.class) {
                if (null == jedisPool) {
                    JedisPoolConfig poolConfig = new JedisPoolConfig();
                    // 池子中能分配多少个Jedis实例
                    poolConfig.setMaxActive(1000);
                    // 池子中最多能够有多少个状态是idle的jedis实例
                    poolConfig.setMaxIdle(32);
                    // 当从池子中取一个jedis的时候,最大的等待时间,如果超时,抛JedisConnectionException
                    poolConfig.setMaxWait(100 * 1000);
                    // 获得一个jedis实例的时候,是否检查连接可用(ping命令)
                    poolConfig.setTestOnBorrow(true);

                    jedisPool = new JedisPool(poolConfig, "127.0.0.1", 6379);
                }
            }
        }
        return jedisPool;
    }

    // 
    public static void release(JedisPool jedisPool, Jedis jedis) {
        if (null != jedis) {
            jedisPool.returnResourceObject(jedis);
        }
    }

}
public class TestPool {

    public static void main(String[] args) {
        JedisPool jedisPool = JedisPoolUtil.getJedisPoolInstance();
        JedisPool jedisPool2 = JedisPoolUtil.getJedisPoolInstance();

        System.out.println(jedisPool == jedisPool2);

        Jedis jedis = null;
        try {
            // 通过getResource()来获取一个jedis
            jedis = jedisPool.getResource();
            jedis.set("aa", "bb");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JedisPoolUtil.release(jedisPool, jedis);
        }
    }
}

j

相关文章

  • 【Redis】Redis学习笔记(五)jedis(JedisCl

    redis系列文章目录 redis学习笔记(一)redis3.2.5集群安装与测试 redis学习笔记(二)Jed...

  • Redis学习笔记:事务

    Redis学习笔记:事务 原文链接:Redis学习笔记:事务 一、事务的描述 和MySQL一样,Redis中也有事...

  • Redis 参数

    Redis Redis 学习笔记 II 常见参数配置

  • 2018-11-12

    Redis学习笔记 Redis在在线测试 Redis官网 REmote DIctionary Server(Red...

  • Redis 初识

    初识 Redis 《 Redis 开发与运维》的学习笔记,希望大家多多指导。 什么是 Redis Redis 的全...

  • Redis—Redis入门安装及基础数据的知识

    Redis学习笔记所有文章:https://www.jianshu.com/nb/33547142 Redis安装...

  • 数据库Redis在Windows上的使用教程

    Redis学习笔记----Redis在windows上的安装配置和使用 Redis简介 redis是一个key-v...

  • Redis 学习笔记

    Redis 学习笔记 原文:硬核!16000 字 Redis 面试知识点总结,建议收藏! Redis 基础知识 R...

  • Redis 数据类型

    Redis Redis 学习笔记 II Redis是一个开源的使用ANSI C语言编写、遵守BSD协议、支持网络、...

  • Redis 安装

    Redis Redis 学习笔记 I Redis是一个开源的使用ANSI C语言编写、遵守BSD协议、支持网络、可...

网友评论

      本文标题:Redis学习笔记

      本文链接:https://www.haomeiwen.com/subject/zismfctx.html