1.Redis介绍
- REmote DIctionary Server(远程字典服务器),是一个高性能的(key/value)分布式内存数据库,基于内存运行并支持持久化的NoSQL数据库
- Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启时可以再次加载使用
- Redis不仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储
- Redis支持数据的备份,即master-slave模式的数据备份
分布式数据库中CAP+BASE
对比传统的ACID,C(Consistency强一致性(Availability高可用性)P(Partition tolerance分区容错性),一个分布式系统不可能同时满足这三个需求,最多只能同时较好地满足两个,AP大多数网站架构的选择
BA(Basically Available基本可用)S(Soft state软状态)E(Eventually consistent最终一致)
2.Redis数据类型
2.1 Redis键(key)
#判断某个key是否存在
exists key name
#移动key到指定库
move key db
#给key设置过期时间
expire key 秒
#查看多少秒过期,-1表示永不过期,-2表示已过期
ttl key
#查看key是什么类型
type key
#删除key
del key
2.2 Redis字符串(String)
- 最基本的类型,可以理解为与Memcached一模一样的类型,一个key对应一个value(单值单value),一个redis字符串value最多512M
- 二进制安全的,redis的string可以包含任何数据,比如jpg图片或序列化的对象
set/get/del/append/strlen
incr/decr/incrby/decrby
getrange/setrange
setex(set with expire)/setnx(set if not exist)
mset/mget/msetnx
getset(先get再set)
2.3 Redis列表(List)
- 底层实际是个链表,简单的字符串列表,按照插入顺序排序,可以添加一个元素到列表的头部或尾部,单值多value
- 如果值全移除,对应的键也就消失了。链表的操作无论从头和尾的效率都极高,但对中间元素进行操作,效率惨淡
lpush/rpush/lrange
lpop/rpop
lindex
llen
lrem key
ltrim key
rpoplpush
lset key index value
linsert key before/after
2.4 Redis集合(Set)
- 是string类型的无序无重复的集合(单值多value),通过HashTable实现的
sadd/smembers/sismember
scard
srem key value
srandmember key
spop key [count]
smove key1 key2
差集 sdiff
交集 sinter
并集 sunion
2.5 Redis哈希(Hash)
- 类似于Java中的Map<String,Object>,是一个键值对集合
- 是一个string类型的field和value的映射表,特别适合用于存储对象
- KV模式不变,但V是一个键值对
hset/hget/hmset/hmget/hgetall/hdel
hlen
hexists key
hkeys/hvals
hincrby/hincrbyfloat
hsetnx
2.6 Reid有序集合(Zset sorted set)
- zset和set一样也是string类型元素的集合,且不允许重复的成员
- 不同的是每个元素都会关联一个double类型的分数,redis通过分数来为集合中的成员进行从小到大的排序,zet的成员是唯一的,但分数(score)可以重复
zadd/zrange
zrangebyscore [(]key [withscores] [limit]
zrem key
zcard/zcount key/zrank key values/zscore key
zrevrank key values[withscores]
zrevrangebyscore key values
3.解析配置文件redis.conf
INDLUDES包含
- 可以通过includes包含,redis.conf可以包含其他
GENERAL通用
- daemonize 是不是以守护进程运行,默认no
- pidfile 以守护进程运行时,默认会把pid写入/var/redis.pid文件,可以指定
- port 端口,默认6379
- tcp-backlog 511 设置tcp的bcklog,bcklog是一个连接队列,在高并发下需要一个高backlog值来避免慢客户吨连接问题
- timeout 0 客户端闲置多长时间后关闭连接,0标识关闭该功能
- bind 绑定的主机地址
- tcp-keeplive 如果设置为0,则不会进行keep live检测,建议设置60
- loglevel 服务器log级别 debug/verbose/notice/warning,默认notice
- logfile 日志记录方式,默认为标准输出
- syslog-enabled 是否把日志输出到syslog中
- syslog-ident redis 指定syslog里的日志标识
- syslog-facility 指定syslog设备,user或local0-local7
- databases 设置数据库数
SNAPSHOTTING快照
- save 默认1分钟修改1万次,5分钟改了10次,15分钟改1次
- stop-writes-on-bgsve-error 如果配置no,表示不在乎数据是否一致或有其他手段进行控制
- rdbcompression 对于存储到磁盘中的快照,是否设置进行LZF算法压缩存储,默认yes。不想消耗CPU进行压缩,可以设置no
- rdbchecksum 在存储快照后,使用CRC64算法进行数据校验。增加约10%的性能消耗。希望最大的性能提升,可以设置no
- dbflenme dump.rdb
- dir 默认./
REPLICTION复制
SECURITY安全
- 访问密码的查看、设置和取消
- config set/get requirepass + password
- auth + password
LIMITS限制
- maxclients 默认10000
- maxmemory 最大内存限制
- maxmemory-policy LRU(Least Recently User),LFU(Least Frequently Used)volatile-lru ->
allkeys-lru -> .
volatile-lfu ->
allkeys-lfu ->
volatile-random ->
allkeys-random ->
volatile-ttl ->默认noeviction - maxmemory-samples 设置样本的大小,默认5
APPEND ONLY MODE追加
- appendonly 默认no。yes打开aof持久化
- appendfilename “appendonly.aof”
- appendfsync always 性能差,数据完整性好 | everysec 默认设置,异步操作,如果一秒内宕机,数据有丢失 | no
- no-ppendfsync-on-rewrite 重写时是否可以运用appendfsync,用默认no,保证数据安全性
- auto-aof-rewrite-min-size 64mb 设置重写的基准值
- auto-aof-rewrite-percentage 100 设置重写的值
4.Redis的持久化
4.1 RDB(Redis DataBase)
- 在指定的时间间隔内将内存中的数据集快照写入磁盘(Snapshot快照),恢复时是将快照文件直接读到内存里,保存dump.rdb文件
- Redis会单独创建fork一个子进程进行持久化,将数据写入到一个临时文件中,持久化过程结束了,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程不进行任何IO操作,确保了性能
- 如果要进行大规模数据的恢复,且对数据恢复的完整性不是非常敏感,RDB方式要比AOF方式更高效,RDB缺点是最后一次持久化的数据可能丢失
- Fork的作用是复制一个与当前进程一样的进程,新进程的所有数据都和原进程一致,但是一个全新进程,并作为原进程的子进程
如何出发RDB快照
配置文件中默认的快照配置
命令save或bgsave。save只管保存,其他全部阻塞。bgsave后台异步快照操作
执行flushall命令,也会产生dump.rdb文件
4.2 AOF(Append Only File)
- 以日志的形式来记录每个写从中,将Redis执行过所有写指令记录下来(读操作不记录),只许追击文件但不可以改写文件,Redis启动之初会读取该文件重新构建数据。Redis重启会根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作
- 保存的是appendonly.aof文件
- 异常恢复 redis-check-aof --fix
Rewrite重写
AOF采用文件追加的方式,文件会越来越大,避免这种情况,新增了重写机制,当AOF文件的大小超过设定的阈值时,Redis就会启动AOF文件的内容压缩,只保留可以恢复数据的最小指令集,命令bgrewriteaof
AOF文件持续增长时,会fork处一条新进程将文件重新(先写临时文件再rename),遍历新进程的内存中数据,每条记录有一条的set语句,重写aof文件的操作,并没有读取旧的aof文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件,这点和快照有点类似
Redis会记录上次重写时的AOF大小,默认配置时当AOF文件大小是上次rewrite后大小的一倍且文件大于64M时触发
5.Redis的事务
- 本质是一组命令的集合,一个事务中的所有命令都会序列化,按顺序串行化执行不会被其他命令插入
- 一个队列中,一次性、顺序性、排他性的执行一系列命令
- 单独的隔离操作,事务中所有命令都会序列化,按顺序执行。在执行过程中,不会被其他客户端发送来的命令请求打断
- 没有隔离级别的概念,队列中的命令没有提交前都不会实际被执行
- 不保证原子性,redis同一个事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚
multi #标记一个事务的开始
discard #取消事务
exec #执行所有事务内的命令
unwatch #取消watch命令对所有key的监视
watch key #监视一个或多个key,如果在事务执行前这个key被其他命令所改动,那么事务将被打断,类似于乐观锁
悲观锁、乐观锁、CAS(Check And Set)
悲观锁(Pessimistic Lock),每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到他拿到锁。传统关系型数据库中用到很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在操作之前先上锁
乐观锁(Optimistic Lock),每次去拿数据的时候都以为别人不会修改,所以不会上锁,但在更新时会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制,多用于多读的应用类型,可以提高吞吐量。乐观锁策略:提交版本必须大于记录当前版本才能执行更新
6.Redis的发布订阅
- 进程间的一种消息通讯模式:发送者(pub)发送信息,订阅者(sub)接受消息
psubscribe 订阅一个或多个给定模式的频道
pubsub 查看订阅与发布系统状态
publish 将消息发送到指定频道
punsubscribe 退订所有给定模式的频道
subscribe 订阅给定的一个或多个频道的信息
unsubscribe 指推定给定的频道
7.Redis主从复制(master/slave)
- 主机数据更新后根据配置和策略,自动同步到备机的master/slaver机制,master以写为主,slave以读为主
- 配置从库:slaveof 主库IP 主库端口,从机每次与master断开后,都需要重新连接主机,除非配置了redis.conf文件
- slaveof no one当前数据库停止与其他数据库同步,转成主数据库
复制原理
slave启动成功连接到master后会发送一个sync命令
master接到命令启动后台的存盘进程,同时收集所有接到的用于修改数据集命令,在后台进程执行完毕后,master将传送整个数据到slave,完成一次完全同步
全量复制:slave服务在接收到数据库文件数据后,将其存盘并加载到内存中
增量复制:master继续将新的所有收集的修改命令依次传给slave,完成同步
只要重新连接master,一次完全同步将被自动执行
哨兵模式(sentinel)
- 能后台自动监控主机是否故障,如果故障了根据投票自动将从库变为主库
#sentinel.conf
sentinel monitor host6379 127.0.0.1 6379 1
被监控IP 1代表票数
#启动哨兵
redis-sentinel /sentinel.conf
8. Jedis
public class TestPing{
public static void main(String[] args){
Jedis jedis = new Jedis("127.0.0.01,6379);
System.out.println(jedis.ping());
}
}
8.1 常用API
public class TestAPI{
public static void main(String[] args){
Jedis jedis = new Jedis("127.0.0.01,6379);
jedis.set("k1","v1");
jedis.set("k2","v2");
System.out.println(jedis.get("k1"));
Set<String> set = jedis.keys("*");
}
}
8.2 Jedis事务
public class TestTransaction{
public static void main(String[] args){
Jedis jedis = new Jedis("127.0.0.01,6379);
Transaction transaction =jedis.multi();
transaction.set("k4","v4");
transaction.set("k5","v5");
transaction.exec();
//transaction.discard();
}
}
8.3 主从复制
public class TestMS{
public static void main(String[] args){
Jedis jedisM = new Jedis("127.0.0.01,6379);
Jedis jedisS = new Jedis("127.0.0.01,6380);
jedisS.slaveof("127.0.0.01,6379);
jedisM.set("class","1122");
jedisS.get("class");
}
}
8.4 JedisPool
public class JedisPoolUtil{
private stataic volatile JedisPool jedisPool = null;
private JedisPoolUtil(){}
public static JedisPool getJedisPoolInstance(){
if(jedisPoool == null){
synchronized (JedisPoolUtil.class)
if(jesidPool == null){
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.sexMaxctive(1000);
poolConfig.setMaxIdle(32);
poolConfig.setMaWait(100*1000);
poolConfig.setTestOnBorrow(true);
jedisPool = new JedisPool(poolConfig,"127.0.0.1",6379);
}
}
return jedisPool;
}
public static void release(JedisPool jedisPool,Jedis jedis){
if(jedis != null){
jedisPool.returnResourceObject(jedis);
}
}
}
public class TestPool{
public static void main(String[] args){
JedisPool jedisPool = JedisPoolUtil.getJedisPoolInstance();
Jedis jedis = nulll;
try{
jedis = jedisPool.getResource();
jedis.set("k","v");
}catch(Exception e){
e.printStackTrace();
}finally{
JedisPoolUtil.release(jedisPool,jedis);
}
}
}