0%

文章字数:503,阅读全文大约需要2分钟

HyperLogLog

用来统计基数个数的算法,优点在于输入元素数量或体积非常大的情况下计算使用的空间总是固定的。
基数:集合中不重复的元素,即去重之后的元素。

  • PFADD key element [element...]添加指定元素到HyperLogLog中
  • PFCOUNT key [key...]返回给定HyperLogLog的基数估算值。
  • PFMERGE destkey sourcekey [sourcekey]将多个HyperLogLog合并成为一个

redis发布与订阅

消息通讯模式,又有两个角色。发送者(pub),订阅者(sub)。一个订阅者可以订阅多个频道。
频道即一个redis

  • PSUBSCRIBE pattern [pattern...]订阅一个或多个符合给定模式的频道
  • PUBSUB <subcommand> [argument [argument ...]]查看订阅与发布系统状态
  • PUBLISH channek message将消息发sing给指定频道。
  • PUNSUBSCRIBE pattern [pattern...]订阅改定的一个或者多个频道
  • SUBSCRIBE channel [channel...]订阅给定的一个或多个频道
  • UNSUBSCRIBE [chnanel [chnanel...]]退订指定的频道

redis事务

redis的弹条命令是原子性的,但是事务并没有原子性的特点。更像是一个命令打包。
过程:开启事务,输入若干命令,执行。
执行过程中不会插入其它命令
执行过程中命令出错不会影响到其它的命令

  • DISCARD 取消事务,放弃执行事务块中的所有命令
  • EXEC执行所有事务块内的命令
  • MULTI标记一个事务块的开始
  • UNWATCH取消WATCH命令对key的监视
  • WATCH key [key...]监视一个或多个key,如果事务执行前这些key有改变,那么事务将被打断

在java中的使用

环境:jedis.jar

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import redis.clients.jedis.Jedis;
public class RedisJava {
public static void main(String[] args) {
//连接本地的 Redis 服务
Jedis jedis = new Jedis("localhost");
System.out.println("服务正在运行: "+jedis.ping());//PONG
//string
jedis.set("xxx", "asfsf");
// 获取存储的数据并输出
System.out.println("redis 存储的字符串为: "+ jedis.get("xxx"));
//List
//存入
jedis.lpush("site-list", "xcascasc");
// 获取存储的数据并输出
List<String> list = jedis.lrange("site-list", 0 ,2);
//key
Set<String> keys = jedis.keys("*");
Iterator<String> it=keys.iterator() ;
//遍历所有的key
while(it.hasNext()){
String key = it.next();
System.out.println(key);
}
}
}

文章字数:870,阅读全文大约需要3分钟

Redis是一款开源免费(BSD协议)高性能的key-value数据库

特点

  1. 支持数据持久化
  2. 支持list,set,zset,hash等数据结构
  3. 支持数据备份,master-slave(主从)模式
  4. 其它如publish/subscribe,通知,key过期等
  5. 原子性操作
  6. 高性能

基本数据类型

Redis支持五种数据类型:string(字符串)、hash(哈希)、list(列表)、set(集合)、zset(sorted set有序集合)

  1. string字符串
    这是最基本的类型,一个key对应一个value。二进制安全,及string可包含任何数据(图片、对象等)。最大存储512MB。
    1
    2
    3
    4
    5
    # 键name 值zhangsan
    redis:0> SET name "zhangsan"
    OK
    redis:0> GET name
    "zhangsan"
  2. Hash哈希
    Redis hash是一个键值对组合,field->value映射表。适合存储对象

    先使用DEL keyName删除上一个测试的key,防止报错WRONGTYPE Operation against a key holding the wrong kind of value

    1
    2
    3
    4
    5
    6
    7
    redis:0> DEL name
    redis:0 > HMSET myhash field1 "value1" field2 "value2"
    OK
    redis:0> HGET myhash field1
    "value1"
    redis:0> HGET myhash field2
    "value2"
    HMSET设置 field->value对,HGET获取hash上的field对应的value
    每个hash可以存2^32 -1个键值对(40多亿)
  3. List列表
    字符串列表,按照插入顺序排序。可以在头部(左边)或者尾部(右边)添加。
    1
    2
    3
    4
    5
    6
    7
    redis:0> lpush myList string1 #开头插入string1
    (integer)1 #返回index
    redis:0> lpush myList string2 # 开头再插入一个
    (integer)2
    redis:0> lrange myList 0 5 # 返回0-5位置之间的元素
    1) "string1"
    2) "string2"
  4. Set集合
    string的无序集合,通过哈希表实现,删改查复杂度都是o(1)。
    1
    2
    3
    4
    5
    6
    redis:0> sadd mySet value1 #向集合mySet插入一条数据value1
    (integer)1 # 插入成功返回1
    redis:0> sadd mySet value1
    (integer)0 # 如果value存在则不插入,返回0
    redis:0> smembers mySet # 输出所有
    1) "value1"
  5. zset(sorted set)有序集合
    和set一样是string类型的元素集合,不能重复。但是每个元素都绑定一个double类型的分数,并通过分数排序。分数可重复。
1
2
3
4
5
6
redis:0> zadd myZset 0 value1 # 向myZset中插入分数为0的value1元素
(integer)1 # 成功插入一条
redis:0> zadd myZset 1 value2 # 插入分数为1的数据
redis:0>zrangebyscore myZset 0 100
1) value1
2) value2

连接数据库

打开本地客户端

1
2
3
$ redis-cli # 打开客户端
redis:0>ping # 检测服务是否启动
PONG #结果

远程服务器

1
2
# host地址 port端口 password密码
$ redis-cli -h host -p port -a password

键管理

命令格式:COMMAND命令 KEY_NAME键名

  1. DEL key存在key时删除key
  2. DUMP key序列化key,返回被序列化的值
  3. EXISTS key检测key是否存在
  4. EXPIRE key seconds设置过期时间(秒)
  5. EXPIREAT key timestamp设置过期时间(时间戳)
  6. PEXPIRE key milliseconds毫秒计过期时间
  7. PEXPIREAT key milliseconds-timestamp设置过期时间为毫秒的时间戳
  8. KEYS pattern查找符合规则的key
    1
    2
    3
    4
    KEYS * # 匹配所有
    KEYS h?llo # 匹配hello hallo 等
    KEYS h*llo # 匹配hello haallo hllo等
    KEYS h[ae]llo # 匹配hello或hallo
  9. MOVE key db将当前数据库的key移动到指定db中
  10. PERSIST key移除过期时间
  11. PTTL key 毫秒单位返回剩余过期时间
  12. TTL key以秒为单位返回剩余生存时间
  13. RANDOMKEY从当前数据库中随机返回一个key
  14. RENAME key newkey重命名
  15. RENAMENX key newkey不存在newkey时将key改名为newkey
  16. TYPE key返回key存储的值类型

文章字数:217,阅读全文大约需要1分钟

当内存不够时写入数据的策略

  1. noeviction:当内存不足以容纳新写入数据时,新写入操作会报错。

  2. allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key。

  3. allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个key。

  4. volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key。

  5. volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key。

  6. volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的key优先移除。


文章字数:238,阅读全文大约需要1分钟

原文

分库分表

  1. 开启多个redis服务
  2. 建立路由网关
  3. 在网关里设立规则,使不同的键分发到各个redis
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.util.ArrayList;
    import java.util.List;
    /**
    * @author Martin
    * @version 1.0
    * @date 2020/3/23 7:36 下午
    */
    public class RedisSpit {
    // 服务器列表
    public static List<String> servers = new ArrayList<>();
    static {
    servers.add("127.0.0.1:6380");
    servers.add("127.0.0.1:6381");
    servers.add("127.0.0.1:6382");
    }
    public static void main(String[] args) throws IOException {
    /**
    * redis服务器网关 客户端统一连接该网关
    */
    ServerSocket serverSocket = new ServerSocket(12530);
    Socket socket;
    while ((socket = serverSocket.accept())!= null){
    try {
    while (true){
    InputStream inputStream = socket.getInputStream();
    byte[] request = new byte[1024];
    inputStream.read(request);
    String req = new String(request);
    String[] params = req.split("\r\n");
    int keyLength = Integer.parseInt(params[3].split("\\$")[1]);
    // 根据键长选择服务器
    int mod = keyLength % servers.size();
    String[] serverInfo = servers.get(mod).split(":");
    // 连接实际的redis服务器
    Socket client = new Socket(serverInfo[0],Integer.parseInt(serverInfo[1]));
    // 数据转发
    client.getOutputStream().write(request);
    byte[] response = new byte[1024];
    client.getInputStream().read(response);
    client.close();
    // 数据回写
    socket.getOutputStream().write(response);
    }
    }catch (Exception e){
    e.printStackTrace();
    }
    }
    }
    }

读写分离

使用配置文件中的slaeof server port配置主节点,实现主从的读写分离


文章字数:1906,阅读全文大约需要7分钟

redis的五大基本类型操作

字符串(String)

命令格式: COMMAND命令 KEY_NAME键名 [值]

  1. SET key value指定key的值
  2. GET key获取key的值
  3. GETRANGE key start end获取key中字符串的子字符串
  4. GETSET key value指定key的新值,返回旧值。
  5. GETBIT key offset对key所存储的字符串指定偏移量上的位(bit) 0/1
  6. MGET key1 [key2]获取一个/多个key的值
    1
    2
    3
    redis:0>mget name name2
    1) asbx
    2) sgsg
  7. SETBIT key offset value对key所存储的字符串的值,设置或清除指定偏移量上的位(bit)
  8. SETEX key seconds value设置key的值为value,过期时间seconds(秒)
  9. SETNX key valuekey不存在时设置key的值。
  10. SETRANGE key offset value用value覆盖key存储的字符串,从偏移量offset开始。
  11. STRLEN key返回key所存储的字符串长度
  12. MSET key value [key value...]同时设置多个键值对
  13. MSETNX key value [key value...]key都不存在时设置对应的value值
  14. PSETEX key milliseconds value设置key的值为value并且生存时间milliseconds(毫秒)
  15. INCR keykey中存储的数字值+1(不是数字会报错)
  16. INCRBY key increment增加指定数量
  17. INCRBYFLOAT key increment增加指定浮点数值
  18. DECR key将key中存储的数字-1
  19. DECRBY key decrement将key中的值减去指定量
  20. APPEND key value如果key存在,将value追加到原有值末尾

哈希(Hash)

field=>value的映射表

  1. HDEL key field1 [field2]删除一个或多个哈希表字段。
  2. HEXISTS key field查看哈希表key中,指定的字段是否存在。
  3. HGET key field获取存储在哈希表中的字段的值。
  4. HGETALL key获取在哈希表中指定key的所有字段和值
  5. HINCRBY key field increment为哈希表key的指定字段的整数值加上增量increment
  6. HINCRBYFLOAT key field increment为哈希表key中指定字段的浮点数值加上increment
  7. HKEYS key获取所有哈希表中的字段
  8. HLEN key获取哈希表中字段的数量
  9. HMGET key field1 [field2]获取所有给定的值
  10. HMSET key field1 value1 [field2 value2]同时将多个field-value设置到哈希表key中
  11. HSET key field value将哈希表的key中的字段field的值设置为value
  12. HSETNX key field value只有字段field不存在时,设置哈希表字段的值。
  13. HVALS key从哈希表中获取所有的值
  14. HSCAN key cursor [MARCH pattern] [COUNT count]迭代器
    1
    2
    # 0参数位是游标位置,当游标为0的时候开始新的一轮迭代
    hscan key 0 match xxx* count 100

    列表(List)

  15. BLPOP key1 [key2] timeout移除并获取列表的第一个元素,如果列表没有元素会阻塞列表。直到等待超时或发现可弹出元素。
  16. BRPOP key1 [key2] timeout移除并获取列表最后一个元素,如果列表没有元素会阻塞直到超时或发现可弹出元素。
  17. BRPOPLPUSH source destination timeout从列表中弹出一个值,将弹出的元素插入到另一个列表并返回它;如果列表没有元素会阻塞明知道超时或有可弹出元素
  18. LINDEX key index通过索引获取列表中的元素
  19. LINSERT key BEFORE|AFTER pivot value在列表key的元素pivot前或后插入另一个元素。
  20. LLEN key获取列表长度
  21. LPOP key移除并获取列表的第一个元素
  22. LPUSH key value1 [value2]将一个值插入到已存在的列表头部
  23. LPUSHX key value插入到已存在的列表头,返回列表长度
  24. LRANGE key start stop获取列表指定范围的元素
  25. LREM key count value移除列表元素
  26. LSET key index value通过索引设置元素的值
  27. LTRIM key start stop对于一个列表进行修剪,删除不在指定区域的值
  28. RPOP key移除列表的最后一个元素,返回。
  29. RPOPLPUSH source destination移除列表的最后一个元素,并将该元素添加到另一个列表并返回
  30. RPUSH key value1 [value2]在列表中添加一个或多个值
  31. RPUSHX key value为已存在的列表添加值

    集合(Set)

    集合是基于hash的String类型的无序集合,集合成员是唯一的。
  32. SADD key member1 [member2]向集合添加一个或多个成员
  33. SCARD key获取集合的成员数
  34. SDIFF key1 [key2]返回差集,即第一个集合中其它集合没有的元素。
  35. SDIFFSTORE destination key1 [key2]返回给定的所有集合的差集存储在destination中
  36. SINTER key1 [key2]返回给定所有集合的交集
  37. SINTERSTORE destination key1 [key2]返回给定所有集合的交集存储在destination中
  38. SISMEMBER key member判断member集合元素是否是集合key的成员
  39. SMEMBERS key返回集合中的所有成员
  40. SMOVE source destination member将member元素从source集合移动到destination集合
  41. SPOP key移除并返回集合中的一个随机元素
  42. SRANDMEMBER key [count]返回集合中一个或多个随机数
  43. SREM key member1 [member2]移除集合中一个或多个成员
  44. SUNION key1 [key2]返回所有给定集合的并集
  45. SUNIONSTORE destination key1 [key2]所有给定集合的并集存储在destination集合中
  46. SSCAN key cursor [MARCH pattern] [COUNT count]迭代集合中元素
    1
    sscan myset1 0 match h*

    有序集合(sorted set)

    有顺序的set,通过分数排序
  47. ZADD key score1 member1 [score2 member2]向有序集合添加一个或多个成员,或者更新已存在成员的分数。
  48. ZCARD key获取有序集合成员数
  49. ZCOUNT key min max计算在有序集合中指定区间分数的成员数
  50. ZINCRBY key increment member有序集合中对指定成员的分数加上增量increment
  51. ZINTERSTORE destination numkeys key [key...]计算给定的有序集合交集,保存结果到新的有序集合key
  52. ZLEXCOUNT key min max有序集合中计算指定字典区间内成员数
  53. ZRANGE key start stop [WITHSCORES]通过索引区间返回有序集合指定区间内的成员
  54. ZRANGEBYLEX key min max [LIMIT offset count]通过字典区间返回有序集合成员,输出集合
  55. ZRANGEBYSCORE key min max [withscore] [limit]通过分数返回有序集合指定区间内的成员
  56. ZRANK key member返回有序集合中指定成员的索引
  57. ZREM key member [member...]移除有序集合中的一个或多个成员
  58. ZREMRANGEBYLEX key min max移除有序集合中给定的字典区间的所有成员。
  59. ZREMRANGEBYRANK key start stop移除有序集合中给定排名区间的所有成员
  60. ZREMRANGEBYSCORE key min max移除有序集合中给定的分数区间的所有成员
  61. ZREVRANGE key start stop [WITHSCORES]返回有序集中指定区间内的成员,通过索引,分数从高到底
  62. ZREVRANGEBYSCORE key max min [WITHSCORES]返回有序集中指定分数区间内的成员,分数从高到低排序
  63. ZREVRANK key member返回有序集合中指定成员的排名,有序集成员按分数值递减(从大到小)排序
  64. ZSCORE key member返回有序集合中,成员的分数
  65. ZUNIONSTORE destination numkeys key [key ...]计算给定的一个或多个有序集的并集,并存储在新的 key 中
  66. ZSCAN key cursor [MATCH pattern] [COUNT count]迭代有序集合中的元素(包括元素成员和元素分值)

文章字数:517,阅读全文大约需要2分钟

redis支持数据类型丰富的持久化操作,可以把数据保存在磁盘中,防止宕机之类的情况下数据丢失。

持久化方式

  1. RDB:不定期使用异步方式保存二进制数据到磁盘,速度快、效率高、文件小。但是缓存一致性低。即如果半个小时备份一次,宕机到上一次备份直接的数据可能会丢失。保存文件过大还会导致redis反应变慢(4.x之前默认)

  2. aof:保存的是每次操作的命令,可以选择每秒同步、每次修改同步、不同步。优点是缓存一致性好,数据完整。缺点是文件大,redis再次启动速度慢。会保存一些无用数据,比如已经被更改的数据。

配置

6379.conf(监听的端口.conf)
搜索save

RDB

1
2
3
save 900 1              #在900秒(15分钟)之后,如果至少有1个key发生变化,则dump内存快照。
save 300 10 #在300秒(5分钟)之后,如果至少有10个key发生变化,则dump内存快照。
save 60 10000 #在60秒(1分钟)之后,如果至少有10000个key发生变化,则dump内存快照。

快照在dump.rdb

AOF

1
2
3
appendfsync always     #每次有数据修改发生时都会写入AOF文件。
appendfsync everysec #每秒钟同步一次,该策略为AOF的缺省策略。
appendfsync no #从不同步。高效但是数据不会被持久化。

混合使用

  1. 开启混合模式
    1
    aof-use-rdb... yes
  1. 设置aof重写大小(也可以手动)
1
auto-aof-rewrite-...两个
  1. 触发AOFrewrite时,会清空AOF,直接保存当前redis数据(并不是真的重写)数据都在aof文件里,以二进制+命令的形式存储

文章字数:806,阅读全文大约需要3分钟

reids的集群搭建方式有三种主从master-slave、哨兵Sentinel、集群Cluster

主从master-slave

数据库分为主数据库master和从数据库slave

  1. 主数据库进行写操作,数据变化时自动同步到从数据库
  2. 从数据库一般都是只读,数据来源为主数据库
  3. 一个master对应多个slave,一个slave对应多个master
  4. slave挂了不影响其他的slave,重启之后会从master同步数据
  5. master挂了之后不影响slave的读,但是不再提供写的服务,重启之后重新提供。
  6. master挂了之后不会重新选举master
  7. master设置密码之后slave访问不需要密码

缺点
master挂了之后不会重新选举master

哨兵Sentinel

解决了主从模式的痛点,master挂了之后重新选举出master

特点

  1. master挂了之后Sentinel会选择一个slave作为master,并修改所有节点的配置文件
  2. master重启之后将会作为slave接收新的master的同步消息
  3. Sentinel也可能挂掉,可以形成集群
  4. Sentinel配置的时候,Sentinel之间也会自动监控
  5. 主从模式修改配置密码时Sentinel会将修改同步到配置文件
  6. SentinelSentinel集群可以管理多个主从Redis
  7. Sentinel最好不要和Redis放在同一台机器上,防止一起挂掉
  8. Sentinel模式下客户端应当直接连接Sentinel这样master挂掉之后Sentinel可以自动感知新的master

工作机制

  1. Sentinel每秒都向所知的masterslave、其它Sentinel发送pin
  2. 如果有一个实例距离上一次回复pin的时间超过down-after-milliseconds,实例就会被Sentinel标记成主观下线
  3. master标记成主观下线之后监视这个masterSentinel都要每秒一次的频率确认是否是主观下线
  4. 当足够数量Sentinel(配置)在指定时间内确认此master主观下线,则master的标记将改为客观下线
  5. mastersentinel标记为客观下线时,sentinel向下线的master的所有slave发送 INFO 命令的频率会从 10 秒一次改为 1 秒一次
  6. master重新向sentinelpin命令回复,则master的主观下线状态将被移除

集群Cluster

当一台电脑上放不下信息时就可以采用集群的方式,分散存储信息
通过cluster可以实现主从和master重选功能,所以如果配置两个副本三个分片的话,就需要六个Redis实例。因为Redis的数据是根据一定规则分配到cluster的不同机器的,当数据量过大时,可以新增机器进行扩容。

使用集群,只需要将redis配置文件中的cluster-enable配置打开即可。每个集群中至少需要三个主数据库才能正常运行,新增节点非常方便。


文章字数:267,阅读全文大约需要1分钟

redis一般是将数据保存在内存中,以提高读取写入的效率。但是一旦断电,数据也会全部丢失。数据持久化就是将内存中的数据写入到磁盘,保证数据的完整性。

RDB(默认方式)

定时将内存中的快照保存成二进制的副本
(bgsave命令触发,父进程创建子线程执行fork操作)
优点

  1. 二进制文件恢复速度快

缺点

  1. 开销大,不能做到实时

AOF

开启后,redis每执行一次修改数据的命令,都会把命令添加到AOF文件中,根据文件就能进行恢复。

优点

  1. 实时持久化

缺点

  1. 恢复速度慢
  2. 文件体积会无限制变大(时间越久执行的命令越多),需要定期整理

最佳实践

RDBAOF都开启,恢复时先用RDB,剩余部分再使用AOF恢复。AOF中的命令也只用保存上一次RDB之后进行的操作就行了


文章字数:529,阅读全文大约需要2分钟

定义

edis的集群脑裂是指因为网络问题,导致redis master节点跟redis slave节点和sentinel集群处于不同的网络分区,此时因为sentinel集群无法感知到master的存在,所以将slave节点提升为master节点。此时存在两个不同的master节点,就像一个大脑分裂成了两个。
集群脑裂问题中,如果客户端还在基于原来的master节点继续写入数据,那么新的master节点将无法同步这些数据,当网络问题解决之后,sentinel集群将原先的master节点降为slave节点,此时再从新的master中同步数据,将会造成大量的数据丢失。

解决

配置

1
2
3
4
# 连接到master的最少slave数
min-slaves-to-write 3
# slave连接master的最大延迟时间
min-slaves-max-lag 10

新版配置

1
2
min-replicas-to-write 3
min-replicas-max-lag 10

如上要求至少3个slave节点,且数据复制和同步的延迟不能超过10秒。否则master就拒绝读写,这样发生集群脑裂原先的master节点接收到写入请求就会拒绝


文章字数:570,阅读全文大约需要2分钟

redis数据量大的时候就需要使用多个redis实例去共同存储数据。

固定取模

  • 过程
  1. 先把redis若干个主节点编号

    1
    2
    3
    Master1 : 1
    Master2 : 2, 3
    Master3 : 4, 5
  2. 再用hash值和编号总数量取模

    1
    hash(key) % total_virtual_weight
  • 缺点:当新增或者删除节点时,数据几乎全部都要重新洗牌。消耗过大,只能当做缓存

一致性哈希Consistent Hashing

  • 过程
  1. 假设有一个HSAH环,范围为2^32-1(即一个32位的无符号整形范围)
  2. 计算集群节点的HASH(通过ip,主机名之类的),并标记在HASH环上
  3. 客户端请求数据时会通过数据的HASH值向后找,找到的第一个节点处理此请求
  • 优点:相对于固定取模,删除节点或节点宕机所影响的数据更少。
  • 缺点: 删除节点时还是需要计算所有该节点的key,另外算出来的节点Hash值也可能导致数据分布不均匀的现象。

预先分配PreSharding

  • 过程
  1. 部署redis时一个机器上部署多个实例
  2. 需要拓展时将一台机器上的实例分发到多台机器上,以提升性能与空间
  • 优点:增长过程中redis的实例数保持不变
  • 缺点:实例多,增加了运维成本