Redis学习笔记

Redis

第一章、NoSQL数据库简介

1、技术发展

技术的分类:

  1. 解决功能性的问题:Java、Jsp、RDBMS、Tomcat、HTML、Linux、JDBC、SVN
  2. 解决扩展性问题:Struts、Spring、SpringMVC、Hibernate、MyBatis
  3. 解决性能问题:NoSQL、Java线程、Hadoop、Nginx、MQ、ElasticSearch

2、NoSQL数据库

1.NOSQL数据库概述

NoSQL(NoSQL = Not Only SQL),意思是“不仅仅是SQL”,泛指非关系型数据库。NoSQL不依赖业务逻辑方式存储,而以简单的key-value模式存储。因此大大的增加了数据库的扩展能力。

  • 不遵循SQL标准
  • 不支持ACID
  • 远超于SQL的性能
2.NoSQL适用场景
  • 对数据高并发的读写
  • 海量数据的读写
  • 对数据高扩展性的
3.NoSQL不适用的场景
  • 需要事务支持
  • 基于sql的结构化查询存储,处理复杂的关系,需要即席查询
  • 用不着sql和用了sql也不行的情况,请考虑用NoSql
4.常见的NoSQL数据库
  • Redis

    • 几乎覆盖了Memcached的绝大部分功能
    • 数据都在内存中,支持持久化,主要用于备份恢复
    • 除了支持简单的key-value模式,还支持多种数据结构的存储,比如list、set、hash、zset等
    • 一般是作为缓存数据库辅助持久化的数据库
  • MongoDB

    • 高性能、开源、模式自由的文档型数据库
    • 数据都在内存中,如果内存不足,把不常用的数据保存到硬盘
    • 虽然是key-value模式,但是对value(尤其是JSON)提供了丰富的查询功能
    • 支持二进制数据以及大型对象
    • 开源根据数据的特点替代RDBMS,成为独立的数据库,或者配合RDNMS,存储特定的数据

第二章、Redis概述安装

1、Redis概述
  • Redis是一个开源key-value存储系统
  • 和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set-- 有序集合)和hash(哈希类型)
  • 这些数据类型都支持push/pop、add/remove以及取交集和差集和其他更丰富的操作,而且这些操作都是原子性
  • 在此基础上,Redis支持各种不同方式的排序
  • 与Memcached一样,为了保证效率,数据都是缓存在内存
  • 区别是Redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件
  • 并且在此基础上实现了master-slave(主从)同步
2、Redis安装
  1. 到http://redis.io.com下载redis的压缩文件包

  2. 用Xftp上传到Linux操作系统上的opt目录

  3. 查看当前LInux系统是否安装了gcc编译器

    gcc --version;
    
  4. 进入opt目录

    cd /
    cd /opt/
    
  5. 解压redis压缩文件

    tar -zxvf redis-6.2.7.tar.gz
    
  6. 进入redis-6.2.7目录编译redis文件

    cd redis-6.2.7/
    
    make
    
  7. 安装

    make install
    
3、安装路径:/usr/local/bin

查看默认安装目录:

redis-benchmark:性能测试工具

redis-check-aof:修复有问题的AOF文件,rdb和aof后面讲

redis-check-dump:修复有问题的dump.rdb文件

redis-sentinel:Redis集群使用

redis-server:Redis服务器启动命令

redis-cli:客户端,操作入口

4、Redis启动

前台启动(不推荐)

  • 前台启动,命令行窗口不能关闭,否则服务器停止
#启动命令
[root@bogon bin]# redis-server

7476:C 27 Oct 2022 09:33:23.040 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
7476:C 27 Oct 2022 09:33:23.040 # Redis version=6.2.7, bits=64, commit=00000000, modified=0, pid=7476, just started
7476:C 27 Oct 2022 09:33:23.040 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
7476:M 27 Oct 2022 09:33:23.041 * Increased maximum number of open files to 10032 (it was originally set to 1024).
7476:M 27 Oct 2022 09:33:23.041 * monotonic clock: POSIX clock_gettime
7476:M 27 Oct 2022 09:33:23.042 # A key '__redis__compare_helper' was added to Lua globals which is not on the globals allow list nor listed on the deny list.
                _._                                                  
           _.-``__ ''-._                                             
      _.-``    `.  `_.  ''-._           Redis 6.2.7 (00000000/0) 64 bit
  .-`` .-```.  ```\/    _.,_ ''-._                                  
 (    '      ,       .-`  | `,    )     Running in standalone mode
 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 6379
 |    `-._   `._    /     _.-'    |     PID: 7476
  `-._    `-._  `-./  _.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |           https://redis.io       
  `-._    `-._`-.__.-'_.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |                                  
  `-._    `-._`-.__.-'_.-'    _.-'                                   
      `-._    `-.__.-'    _.-'                                       
          `-._        _.-'                                           
              `-.__.-'                                               

7476:M 27 Oct 2022 09:33:23.042 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
7476:M 27 Oct 2022 09:33:23.042 # Server initialized
7476:M 27 Oct 2022 09:33:23.042 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
7476:M 27 Oct 2022 09:33:23.042 * Ready to accept connections

  • 后台启动

    • 拷贝一份redis.conf到其他目录

      cp /opt/redis-6.2.7/redis.conf /etc/redis.conf
      
    • 将后台启动设置daemonize no改为yes

      即:将redis.conf文件将里面的daemonize no改为yes,让如无在后台启动

      vim redis.conf
      
    • 进入/usr/local/bin/目录,重新启动redis

      cd /usr/local/bin/
      #使用指定的配置文件启动
      redis-server /etc/redis.conf
      
    • 查看redis的进程信息

      [root@bogon bin]# ps -ef | grep redis
      
      root       7679      1  0 09:49 ?        00:00:00 redis-server 127.0.0.1:6379
      root       7685   2829  0 09:49 pts/0    00:00:00 grep --color=auto redis
      
    • 测试访问

      #用客户端访问
      [root@bogon bin]# redis-cli
      #测试验证
      127.0.0.1:6379> ping
      PONG
      127.0.0.1:6379> 
      
    • Redis关闭

      单实例关闭

      redis-cli shutdown
      

      也可以进入终端后再关闭

      127.0.0.1:6379>shutdown
      not connected>
      

      多实例关闭,指定端口号再关闭

      redis-cli -p 6379 shutdown
      

      用Linux的kill命令杀死对应的进程号也可以

      #-9强制删除
      [root@linuxcool ~]# kill -9 1518
      
5、Redis介绍相关知识
  • 端口号:6379

  • 默认16个库,类似数组下标从0开始,初始默认使用0号库

  • 使用命令select 来切换数据库。如:select 8

  • 统一密码管理,所有数据库同样密码

  • dbsize查看当前数据库的key的数量

  • flushdb清空当前库

  • flushall通杀全部库

  • Redis是单线程+多路IO复用技术

    	多路复用是指用一个线程来检查多个文件描述符(Socket)的就绪状态,比如调用select的poll函数,传入多个文件描述符,如果有一个文件描述符就绪,则返回,否则阻塞直到超时。得到就绪状态后进入真正的操作可以在同一个线程里执行,也可以启动线程执行(比如使用线程池)
    

第三章、常用五大数据类型

哪里区获得redis常用数据类型操作命令:http://www.redis.cn/commands.html

1、Redis键(key)


#插入一些键
set key1 jack
set key2 lucy
set key3 john

keys * 查看当前库所有key

127.0.0.1:6379> keys *
1) "key2"
2) "key3"
3) "key1"

exists key:判断某个key是否存在

127.0.0.1:6379> exists key1
(integer) 1
127.0.0.1:6379> exists key4
(integer) 0

type key:查看你的key是什么类型的

127.0.0.1:6379> type key1
string

del key:删除指定的key数据

127.0.0.1:6379> del key1
(integer) 1

unlink key:根据value选择非阻塞删除(异步删除)

#仅将keys从keyspace元数据中删除,真正的删除会在后续异步操作
127.0.0.1:6379> unlink key2
(integer) 1

expire key 10 :为给定的key设置过期时间

127.0.0.1:6379> expire key1 5
(integer) 1

ttl key:查看还有多少秒过期,-1表示永不过期,-2表示已经过期

127.0.0.1:6379> ttl key1
(integer) -2

select 命令切换数据库

127.0.0.1:6379> select 1
OK
127.0.0.1:6379[1]> 

dbsize查看当前数据库的key的数量

127.0.0.1:6379> dbsize
(integer) 3

flushdb 清空当前库

127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> dbsize
(integer) 0

2、Redis字符串(String)

1.简介

String是Redis最基本的类型,你可以理解成与Memcached一模一样的类型,一个key对应一个value

String类型时二进制安全的。意味着Redis的string可以包含任何数据比如jpg图片或者序列化的对象

String类型是Redis最基本的数据类型,一个Redis中字符串value最多可以是512M

2.命令

  • set 添加键值对

    > set key value [EX seconds | PX milliseconds | KEEPTTL] [NX | XX]
    
    • NX:当前数据库中key不存在时,可以将key-value添加数据库
    • XX:当数据库中key存在时,可以将key-value添加到数据库,与NX参数互斥
    • EX:key的超时秒数
    • PX:key的超时毫秒数,与EX互斥
  • get 查询对应的键值

  • append 将给定的追加到原值的末尾

  • strlen 获取值的长度

  • setnx 只有在key不存在时,设置key值

  • incr

    • 将key中存储的数字值增1

    • 只能对数字值操作,如果为空,新增值为1

      127.0.0.1:6379> set k2 1
      OK
      #类似i++
      127.0.0.1:6379> incr k2 
      (integer) 2
      127.0.0.1:6379> get k2
      "2"
      
  • decr

    • 将key中存储的数字减1
    • 只能对数字值操作,如果为空,新增值为-1
  • incrby / decrby <步长>将key中存储的数字值增减。自定义步长,可以不是1

原子性

所谓原子操作时指不会被线程调度机制打断的操作

这种操作一旦开始,就一直运行到结束,中间不会有任何context Switch(切换到另一个线程)

  • 在单线程中,能够在单条指令中完成的操作都可以认为是“原子操作”,因为中断只能发生于指令之间
  • 在多线程中,不能被其他进程(线程)打断的操作就叫做原子操作

Redis单命令的原子性主要得益于Redis的单线程

  • mset …

    同时设置一个或多个key-value对

    127.0.0.1:6379> mset k1 val1 k2 val2 k3 val3
    OK
    127.0.0.1:6379> keys *
    1) "k3"
    2) "k2"
    3) "k1"
    
  • mget

    同时获取一个或多个value

    127.0.0.1:6379> mget k1 k2 k3
    1) "val1"
    2) "val2"
    3) "val3"
    
  • msetnx < value2>…

    同时设置一个或多个key-value对,当且仅当所有 给定的key都 不存在

    原子性,有一个失败都失败

    127.0.0.1:6379> msetnx k4 val4 k5 val5
    (integer) 1
    127.0.0.1:6379> keys *
    1) "k2"
    2) "k1"
    3) "k3"
    4) "k5"
    5) "k4"
    
  • getrange <起始位置> <结束位置>

    获取值得范围,类似于java中的substring,前闭后开

    127.0.0.1:6379> get key1
    "jack and rose"
    127.0.0.1:6379> getrange key1 0 4
    "jack "
    
  • setrange <起始位置> < value>

    用覆盖所存储的字符串的值,从<起始位置>开始(索引从0开始)

    127.0.0.1:6379> get k1
    "jack"
    127.0.0.1:6379> setrange k1 3 rose
    (integer) 7
    127.0.0.1:6379> get k1
    "jacrose"
    
  • setex 过期时间

    设置键值的同时,设置过期时间,单位:秒

    127.0.0.1:6379> setex k1 10 val
    OK
    127.0.0.1:6379> ttl k1
    (integer) 7
    127.0.0.1:6379> ttl k1
    (integer) 6
    
  • getset

    以旧换新,设置了新值的同时获得旧值

    127.0.0.1:6379> getset k2 value2
    "val2"
    127.0.0.1:6379> get k2
    "value2"
    
  • 底层数据结构:

    	String的数据结构为简单动态字符串(Simple Dynamic String,缩写为SDS)。是可以修改的字符串,内部结构实现上类似于Java的ArrayList,采用预分配冗余空间的方式来减少内存的频繁分配。
    

3、Redis列表(List)

  • 简介

    	单键多值
    	Redis列表是简单的字符串列表,按照插入顺序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。
    	它的底层是一个双向链表,对两端的操作性能很高,通过索引下标的操作中间的结点性能会较差。
    
  • 常用命令

    • lpush / rpush …

      从左边/右边插入一个或多个值

      127.0.0.1:6379> lpush key1 v1 v2 v3 v4
      (integer) 4
      127.0.0.1:6379> lrange key1 0 -1
      1) "v4"
      2) "v3"
      3) "v2"
      4) "v1"
      
    • lpop / rpop

      从左边/右边吐出一个值。值在键在,值光键亡

      127.0.0.1:6379> rpop key1 
      "v1"
      
    • rpop lpush

      从列表右边吐出一个值,插入到列表的左边

      127.0.0.1:6379> rpush key1 rightvalue1
      (integer) 4
      
    • lrange

      按照索引下标获取元素(从左到右)

      127.0.0.1:6379> lrange key1 0 -1
      1) "v4"
      2) "v3"
      3) "v2"
      4) "rightvalue1"
      
    • lindex

      按照索引下标获取元素(从左到右)

      127.0.0.1:6379> lindex key1 0
      "v4"
      
    • llen

      获得列表长度

      127.0.0.1:6379> llen key1
      (integer) 4
      
    • linsert before

      在的后面插入插入值

      127.0.0.1:6379> linsert key1 after rightvalue1 hello
      (integer) 5
      
    • lrem

      从左边删除n个value(从左到右)

      127.0.0.1:6379> lrange key1 0 -1
      1) "v3"
      2) "v2"
      3) "rightvalue1"
      4) "hello"
      
      127.0.0.1:6379> lrem key1 1 v3
      (integer) 1
      
      127.0.0.1:6379> lrange key1 0 -1
      1) "v2"
      2) "rightvalue1"
      3) "hello"
      
    • lset

      将列表key下标为index的值替换为value

      127.0.0.1:6379> lset key1 0 v0
      OK
      127.0.0.1:6379> lindex key1 0
      "v0"
      
  • 数据结构

    	List的数据结构为快速链表quickList
    	首先在列表元素较少的情况下会使用一块连续的内存存储,这个结构是ziplist,也就是压缩列表。
    	它将所有的元素紧挨着一起存储,分配的是一块连续的内存。
    	当数据量比较多时才会改成quicklist
    	因为普通链表需要附加指针空间太大,会比较浪费空间
    	Redis将链表和ziplist结合起来组成了quicklist。也就是将多个ziplist使用双向指针串起来使用。这样既满足了快速的插入删除性能,又不会出现太大的空间冗余。
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DWWiVlY8-1683813026084)(C:\Users\12086\AppData\Roaming\Typora\typora-user-images\image-20221028105241342.png)]

4、Redis集合(Set)

  • 简介

    	Redis set对外提供了的功能与list类似是一个列表的功能,特殊之处在于set是可以自动排重的,当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择。并且set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list不能提供的。
    	Redis的set是string类型的无序集合,它底层其实是一个value为null的hash表,所以添加、删除、查找的复杂度都是O(1)
    	一个算法,随着数据的增加,执行时间的长短,如果是O(1),数据增加,查找数据的时间不变
    
  • 常用命令

    • sadd

      将一个或多个member元素加入集合key中,已经存在的member元素将会忽略

      127.0.0.1:6379> sadd set1 v1 v2 v3 v4
      (integer) 4
      
  • smembers

    取出该集合的所有值

      127.0.0.1:6379> smembers set1
      1) "v2"
      2) "v1"
      3) "v4"
      4) "v3"
    
    • sismember

      判断集合key中是否含有value值,有返回1,没有0

    127.0.0.1:6379> sismember set1 v1
    (integer) 1
    127.0.0.1:6379> sismember set1 v5
    (integer) 0

    
    
    
    
  • scard 返回该集合的元素个数

      127.0.0.1:6379> scard set1
    (integer) 4
    
    • srem …

    删除集合中的某个元素

      127.0.0.1:6379> srem set1 v1
    (integer) 1
      127.0.0.1:6379> smembers set1
      1) "v2"
      2) "v4"
      3) "v3"
    
    • spop

    随机从该集合吐出一个值

      127.0.0.1:6379> spop set1 1
    1) "v4"
    
    • srandmember

    随机从该集合取出n个元素,但并不会删除元素

      127.0.0.1:6379> srandmember set1 3
    1) "v2"
      2) "v3"
    
    • smove value把集合中一个值移动到另一个集合

    • sinter 返回两个集合的交集元素

    • sunion 返回两个集合的并集元素

    • sdiff 返回两个集合的差集元素(key1中的,不包含key‘2)

  • 数据结构

    	Set数据结构是dict字典,字典是用哈希表实现的
    	Java中的HashSet的内部实现使用的是HashMap,只不过所有的value都指向同一个对象。Redis的set结构也是一样,它的内部也使用Hash结构,所有的value都指向同一个内部值
    

5、Redis哈希(Hash)

  • 简介

    	Redis hash 是一个键值对集合
    	Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。
    	类似于Java里面的Map
    
  • 常用命令

    • hset

      给key集合中的field键赋值value

      127.0.0.1:6379> hset user:1001 id 1
      (integer) 1
      
    • hget

      从集合key1的field中取出value

      127.0.0.1:6379> hget user:1001 id
      "1"
      
    • hmset < value1> …

      批量设置hash值

      127.0.0.1:6379> hmset user:1002 id 1 name zhangsan 
      OK
      127.0.0.1:6379> hget user:1002 name
      "zhangsan"
      
    • hexists

      查看哈希表key中,给定field是否存在

      127.0.0.1:6379> hexists user:1002 id
      (integer) 1
      
    • hkeys

      列出该hash集合的所有field

      127.0.0.1:6379> hkeys user:1002
      1) "id"
      2) "name"
      
    • hvals 列出该hash集合的所有value

      127.0.0.1:6379> hvals user:1002
      1) "1"
      2) "zhangsan"
      
    • hincrby 为哈希表的域field的值加上常量1

    • hsetnx

      将哈希表key中的域field的值设置为value,当且仅当域field不存在

      127.0.0.1:6379> hsetnx user:1002 name jack
      (integer) 0
      127.0.0.1:6379> hsetnx user:1002 age 10
      (integer) 1
      
  • 数据结构

    	Hash类型对应的数据结构是两种:ziplist(压缩列表),hashtable(哈希表),当field-value的长度较短且个数较少时,使用ziplist,否则使用hashtable
    

6、Redis有序集合Zset(sorted set)

  • 简介

    	Redis有序集合zset与普通集合set非常相似,是一个没有重复元素的字符串集合
    	不同之处是有序集合的每个成员都关联了一个评分(score),这个评分被用来从最低分到最高的方式排序集合中的成员。集合的成员是唯一的,但是评分可以是重复的。
    	因为元素是有序的,所以你也可以很快的根据评分或者次序(position)来获取一个范围的元素
    	访问有序集合的中间元素也是非常快的,因此你能够使用有序集合作为一个没有重复成员的智能列表
    
  • 常用命令

    • zadd …

      将一个或多个member元素及其score值加入到有序集合key当中

      127.0.0.1:6379> zadd lang 200 java 300 cpp 400 python
      (integer) 3
      
    • zrange [withscores]

      返回有序集key中,下标在 < stop > 之间的元素,带withscopes,可以让分数一起和值返回到结果集合

      127.0.0.1:6379> zrange lang 0 -1
      1) "java"
      2) "cpp"
      3) "python"
      
    • zrangebyscope key minmax [withscores] [limit offset count]
      返回有序集key中,所有score值介于min和max之间(包括min和max)的成员。

      有序集成员按scope值递增(从小到大)次序排序

      127.0.0.1:6379> zrange lang 0 -1 withscores
      1) "java"
      2) "200"
      3) "cpp"
      4) "300"
      5) "python"
      6) "400"
      
    • zrevrangescore key maxmin [withscopes] [limit offset count]

      同上,改为从小到大排列

      127.0.0.1:6379> zrangebyscore lang 200 400
      1) "java"
      2) "cpp"
      3) "python"
      
    • zincrby 为元素的score加上增量

    • zrem 删除该集合下指定的元素

    • zcount < min> 统计该集合,分数区间的元素个数

    • zrank 返回该值在集合中的排名,从0开始

  • 数据结构

    	SortedSet是Redis提供的一个非常特别的数据结构,一方面它等价于Java的数据结构Map,可以给每一个元素value赋予一个权重score,另一方面它又类似于TreeSet,内部的元素会按照权重scope进行排序,可以得到每个元素的名次,还可以通过scope的范围来获取元素的列表
    	zset底层使用了两个数据结构:
    	1)hash,hash的作用就是关联元素value和权重score,保障元素value的唯一性,可以通过元素value找到对应的scope值
    	2)跳跃表,跳跃表的目的在于给元素value排序,根据scope的范围获取元素列表
    

第四章、Redis的配置文件


第五章、Redis的发布和订阅

1、什么是发布和订阅

Redis发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送信息,订阅者(sub)接受信息

Redis客户端可以订阅任意数量的频道

2、发布订阅命令行实现

  1. 打开一个客户端订阅channell

    redis> subscribe channell
    
  2. 打开另一个客户端,给channell发布消息hello

    publish channell hello
    
  3. 打开第一个客户端可以看到发送的消息

  • 注意:只有订阅了该频道才能看到消息

第六章、Redis的新数据类型


1、Bitmaps


  • 简介
	现代计算机用二进制作为信息的基本单位,1个字节等于8位,例如“abc”字符串是由三个字节组成,但实际计算机存储时将其用二进制来表示。
	合理使用操作位能够有效的提高内存使用率和开发效率
	Redis提供了Bitmaps这个“数据类型”可以实现对位的操作
	1)Bitmaps本事不是一种数据类型,实际上它就是字符串(key-value),但是它可以对字符串的位进行操作
	2)Bitmaps单独提供了一套命令,所以在Redis中使用Bitmaps和使用字符串的方法不太相同。可以把Bitmaps想		象成一个以位为单位的数组,数组的每个单元只能存储0和1,数组下标在Bitmaps中叫偏移量
  • 常用命令

    • setbit

      • 格式:setbit 设置Bitmaps中某个偏移量的值0或者1

        • offset:偏移量从0开始
        127.0.0.1:6379> setbit lang 1 1 2 1 3 0
        (error) ERR wrong number of arguments for 'setbit' command
        127.0.0.1:6379> setbit lang 1 1
        (integer) 0
        127.0.0.1:6379> setbit lang 2 1
        (integer) 0
        
      • 注意:在第一次初始化Bitmaps时,加入偏移量非常大,那么整个初始化过程执行会比较慢,可能会造成Redis的阻塞

    • getbit

      • 格式: 获取Bitmaps中某个偏移量的值

      • 获取键的第offset位的值(从0开始计算)

        127.0.0.1:6379> getbit lang 2 
        (integer) 1
        
    • bitcount

      统计字符串被设置为1的bit数。一般情况下,给定的整个字符串都会被进行计数,通过指定额外的start或者end参数,可以让计数只在特定的位上进行。start和end参数的设置,都可以使用负数值:比如-1表示最后一个位,而-2表示倒数第二位,start和end表示bit组的字节的下标的数,二者皆包含。

      • 格式:bit count [start end] 统计字符串从star字节到end字节比特值为1的数量
    • bitop

      • 格式:bitop and (or/not/xor) [key…]
      • bitop是一个复合操作,它可以做多个Bitmaps的and(交集)、or(并集)、not(非)、xor(异或)操作并将结果保存在destkey中

2、HyperLogLog

  • 简介

    	在工作当中,我们经常遇到与统计相关的功能需求,比如统计网站PV(pageview页面访问量),可以使用Redis的incr、incrby轻松实现。
    	但像UV(UniqueVisiter,独立访客)、独立IP数、搜索记录数等需要去重和计数的问题如何解决?这种求集合中不重复元素的个数问题称为基数问题
    	常见的解决方案:
    	1)数据存储在MySQL表中,使用distince count计算不重复的个数
    	2)使用Redis提供的hash、set、bitmaps等数据结构来处理
    	以上方案结果精确,但随着数据不断增加,导致占用空间越来越大,对于非常大的数据集是不切实际的
    	能否能够降低一定的精度来平衡存储空间?Redis推出了HyperLogLog
    	Redis HyperLogLog是用来做基数统计的算法,HyperLogLog的优点是,在输入元素的数量或者体积非常大非常大时,计算基数所需要的空间总是固定的、并且是很小的。
    	在Redis里面,每个HyperLogLog键只需要花费12KB内存,就可以计算接近2^64个不同的素数的基数。这和计算基数时,元素越多越耗费内存就越多的集合形成了鲜明对比。
    	但是,因为HyperLogLog只会根据输入元素来计算基数,而不会存输入元素本身,所以HyperLogLog不能像集合那样,返回输入的各个元素。
    	什么是基数?
    	比如数据集合{1,3,5,7,8},那么这个数据集的基数集为{1,3,5,7,8}基数为5。基数估计就是在误差可接受的范围内,快速计算基数。
    
  • 常用命令

    • pfadd

      • 格式:pfadd [element…]添加指定元素到HyperLogLog中
    • pfcount

      • 格式: pfcount [key…]计算HLL的近似基数,可以计算多个HLL,比如使用HLL存储每天的UV,计算一周的UV可以使用7天的UV合并计算即可
    • pfmerge

      • 格式:pfmerge [sourcekey…]
      • 将一个或多个HLL合并后的结果存储到另一个HLL中

第七章、Jedis操作Redis


1、Jedis所需要的jar包


<dependency>
	<groupId>redis.clientsgroupId>
    <artifactId>jedisartifactId>
    <version>3.2.0version>
dependency>

2、连接Redis注意事项

  • 禁用Linux的防火墙:systemctl stop/disable firewalld.service

  • 简单的测试

    import org.junit.Test;
    import redis.clients.jedis.Jedis;
    
    public class AppTest
    {
        @Test
        public void test01() {
            Jedis jedis = new Jedis("192.168.200.128", 6379);
            String ping = jedis.ping();
            System.out.println(ping);
            //打印输出PONG
        }
    }
    

3、一些常用数据类型的测试


  • String类型的测试

    /*测试常用数据类型string*/
        @Test
        public void test01() {
            jedis.set("username", "jack");
            jedis.set("password", "hello");
            List<String> list = jedis.mget("username", "password");
            list.forEach(System.out::println);
            Long username = jedis.strlen("username");
            System.out.println(username);
            jedis.append("username", " love rose!");
            String username1 = jedis.get("username");
            System.out.println(username1);
        }
    

第八章、SpringBoot整合Redis


  • 在pom.xml文件中引入redis相关的依赖

    <denpendency>
    	<groupId>org.springframeword.bootgroupId>
        <artifactId>spring-boot-starter-data-redisartifactId>
    denpendency>
    
    <dependency>
    	<groupId>org.apache.commonsgroupId>
    	<artifactId>commons-pool2artifactId>
        <version>2.6.0version>
    dependency>
    
  • application.preperties中对redis的一些配置

    image-20221031232455735
  • 创建Redis的配置类

  • 测试

第九章、Redis_事务


1、事务的定义


Redis事务是一个单独的隔离操作。事务的所有命令都会反序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送过来的额命令请求打断。

Redis事务的主要作用就是串联多个命令防止别的命令插队

2、Multi、Exec、discard


从输入Multi命令开始,输入的命令都会依次进入命令队列中,但不会执行,知道输入Exec后,Redis会将之前的命令队列中的命令依次执行。

组队的过程可以通过discard来放弃组队。

image-20221101155025574

代码示例:

127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set key1 value1
QUEUED
127.0.0.1:6379(TX)> set key2 value2
QUEUED
127.0.0.1:6379(TX)> set key3 value3
QUEUED
127.0.0.1:6379(TX)> exec
1) OK
2) OK
3) OK
127.0.0.1:6379> keys * 
1) "key2"
2) "password"
3) "username"
4) "key1"
5) "key3"

3、事务的错误处理


  • 如果组队时发生错误,则该队列中的所有指令都不会成功

  • 如果在执行过程中发生了错误,则在该错误语句执行失败,其余执行成功的指令仍然成功。

  • 代码示例:

    #组队过程出现错误,则该队列中的所有指令都不会成功
    127.0.0.1:6379> multi
    OK
    127.0.0.1:6379(TX)> set key1 value1
    QUEUED
    127.0.0.1:6379(TX)> set key2 value2
    QUEUED
    127.0.0.1:6379(TX)> set key3
    (error) ERR wrong number of arguments for 'set' command
    127.0.0.1:6379(TX)> exec
    (error) EXECABORT Transaction discarded because of previous errors.
    
    #执行过程出现错误
    127.0.0.1:6379> multi
    OK
    127.0.0.1:6379(TX)> set key1 value1
    QUEUED
    127.0.0.1:6379(TX)> incr key1
    QUEUED
    127.0.0.1:6379(TX)> set key2 value2
    QUEUED
    127.0.0.1:6379(TX)> exec
    1) OK
    2) (error) ERR value is not an integer or out of range
    3) OK
    127.0.0.1:6379> mget key1 key2
    1) "value1"
    2) "value2"
    

4、事务冲突


  • 悲观锁

    image-20221101161108743

悲观锁,顾名思义就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次去拿数据的时候都认为别人会修改,所以每次在那数据的时候都会上锁,这样别人想拿这个数据就会锁定它直到拿到锁。传统的关系型数据库里面就用到了这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前就先上了锁。

  • 乐观锁

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bmEYgNiJ-1683813026085)(C:\Users\12086\AppData\Roaming\Typora\typora-user-images\image-20221101161752743.png)]

乐观锁(Optimistic Lock),顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样就可以提高吞吐量Redis就是利用这种check-and-set机制实现事务的。

  • WATCH key [key…]

    在执行multi之前,先执行watch key1 [key2],可以监视一个(或者多个)key,如果事务执行之前这个(或这些)key被其他命令所改动,那么事务将会打断。

  • unwatch

    取消watch命令对所有key的监视

    如果在执行watch命令之后exec命令或者discard命令先被执行了的话,那么就不需要再执行unwatch了。

  • Redis中不能显示调用悲观锁,只能使用乐观锁

5、Redis事务三特性


  • 单独的隔离操作
    • 事务中的所有命令都会序列化、按顺序执行。事务在执行过程中,不会被其他客户端发送来的命令请求所打断。
  • 没有隔离级别的概念
    • 队列中的命令没有提交之前都不会实际被执行,因为事务提交前任何指令都不会被实际执行
  • 不保证原子性
    • 事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚

6、其他


  • Lua脚本

    Lua是一个小巧的脚本语言,Lua脚本可以很容易的被C/C++代码调用,也可以反过来调用C/C++的函数,Lua并没有提供强大的库,一个完整的Lua解释器也不过200k,所以Lua不适合作为开发独立应用程序的语言,而是作为嵌入式脚本语言。

    很多应用程序、游戏使用Lua作为自己的嵌入式脚本语言,以此来实现可配置性、可扩展性。

  • Lua脚本在Redis中的优势

    将复杂的或者多步的redis操作,写为一个脚本,一次提交给redis执行,减少反复连接redis的次数,提升性能。

    Lua脚本是类似redis事务,有一定的原子性,不会被其他命令插队,可以完成一些redis事务性的操作。

    但是注意redis的lua脚本功能,只有在redis2.6版本以上才可以使用

    利用lua脚本淘汰用户,解决超卖问题。

    redis2.6版本以后,通过lua脚本解决争抢问题,实际上就是redis利用其单线程的特性,用任务队列的方式解决多任务并发问题。

第十章、Redis持久化之RDB


在指定的时间间隔将内存中的数据集快照写入磁盘,也就是行话讲的Snapshot快照,它恢复时是将快照文件直接读到内存中。

1、备份是如何执行的


Redis会单独创建(fork)一个子进程来进行持久化,会将数据入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。整个进程中,主进程是不进行任何IO操作的,这就保证了极高的性能,如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。RDB的缺点最后一次持久化后的数据可能丢失

2、Fork

  • Fork的作用是复制一个与当前进程一样的进程。新进程的所有数据(变量、环境变量、程序计数器等)数值都和原进程一致,但是是一个全新的进程,并作为原进程的子进程
  • 在Linux程序中,fork()会产生一个和父进程完全相同的子进程,但子进程在此后多会exec系统调用,处于效率考虑,Linux中引入了“写时复制技术”
  • 一般情况父进程和子进程会共用一段物理内存,只有进程空间的各段的内容要发生变化时,才会将父进程的内容复制一份给子进程。

3、RDB持久化过程


image-20221101195251987

4、优势和劣势


  • 优势
    • 适合大规模的数据恢复
    • 对数据完整性和一致性要求不高更适合使用
    • 节省磁盘空间
    • 恢复速度快
  • 劣势
    • Fork的时候,内存中的数据被克隆了一份,大致2倍的膨胀性需要考虑
    • 虽然Redis在fork时使用了写时拷贝技术,但是如果数据庞大时还是比较消耗性能
    • 在备份周期在一定间隔时间做一次备份,所以如果Redis意外down掉的话,就会丢失最后一次快照后的所有修改

5、RDB备份


先通过config get dir查询rdb文件的目录

将*.rdb的文件拷贝到别的地方

rdb的恢复

  1. 关闭Redis
  2. 先把备份的文件拷贝到工作目录下 cp dump2.rdb dump.rdb
  3. 启动Redis,备份数据会直接加载

第十一章、Redis持久化之AOF


AOF(Append Only File)

以日志的形式来记录每个写操作(增量保存),将Redis执行过的所有写指令记录下来(读操作不记录),只许追加文件但不可以改写文件,Redis启动之初会读取该文件重新构建数据,换言之,redis重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。

1、AOF持久化流程


  • 客户端的请求写命令会被append追加到AOF缓冲区内
  • AOF缓冲区根据AOF持久化策略[always, ererysec, no]将操作sysc同步到磁盘的AOF文件中
  • AOF文件大小超过重写策略或手动重写时,会对AOF文件rewrite重写,压缩AOF文件容量
  • Redis服务重启时,会重新加载AOF文件中的写操作达到数据恢复的目的

2、AOF的一些问题


1.AOF默认不开启

可以在redis.conf中配置文件的名称,默认为appendonly.aof

AOF文件的保存路径,同RDB的路径一致

2.AOF和RDB同时开启,redis听谁的?

AOF和RDB同时开启,系统默认读取AOF的数据(数据不会存在丢失)

3、AOF启动/修复/恢复


AOF的备份机制和性能虽然和RDB不同,但是备份和恢复的操作同RDB一样,都是拷贝备份文件,需要恢复时再拷贝到Redis工作目录下,启动系统即加载配置文件

  • AOF的启动

    1. 修改redis.config文件 将appendonly修改为yes
    2. 重启redis服务 先杀死进程再启动即可
  • AOF的修复

    • 插入数据
    • 复制appendonly.aof文件
    • 杀死redis进程
    • 删除原有appendonly.aof文件
    • 启动redis进程即可恢复
  • 异常恢复(修改AOF文件)

    • 修改默认的appendonly no 改为yes
    • 如果遇到AOF文件损坏,通过 /usr/local/bin/redis-check-aof–fix appendonly.aof进行恢复
    • 备份被写坏的AOF文件
    • 恢复:重启redis,然后重新加载

4、AOF同步频率的设置


  • appendsync always

​ 始终同步,每次redis的写入都会立刻计入日志;性能较差但数据完整性比较好

  • appendsync everysec

    每秒同步,每秒计入日志一次,如果宕机,本秒的数据可能丢失

  • appendsync no

    redis不主动进行同步,把同步时机交给操作系统

5、优势和劣势


优势:

  • 备份机制更稳健,丢失数据概率更低
  • 可读的日志文本,通过操作AOF稳健,可以处理误操作

劣势:

  • 比起RDB占用更多的磁盘空间
  • 恢复备份速度要慢
  • 每次读写都同步的话,有一定的性能压力
  • 存在个别BUG,导致无法恢复

6、建议


官方推荐两个都启用

如果对数据不敏感,可以选择单独使用RDB

不建议单独使用AOF,因为可能会出现BUG

如果只是做纯内存缓存,可以都不用

  • RDB持久化方式能够在指定的时间间隔能对你的数据进行快照存储
  • AOF持久化方式每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF命令以redis协议追加保存每次写的操作到文件末尾
  • Redis还可以对AOF文件进行后台重写,使得AOF文件体积不至于过大
  • 只做缓存:如果你只希望你的数据再服务器运行的时候存在,你也可以不使用任何持久化方式
  • 同时开启两种持久化方式,当redis重启的时候会优先加载AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整

第十二章、主从复制


主机数据更新后根据配置和策略,自动同步到备机的master/slaver机制,Master以写为主,Slave以读为主

作用:读写分离,性能扩展。容灾快速恢复

1、简单搭建一主两从

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vnQaS5bE-1683813026085)(C:\Users\12086\AppData\Roaming\Typora\typora-user-images\image-20221101214403384.png)]

  • info replication可以查看当前是主机从机情况

2、一些特殊情况

  • 从服务器挂掉之后,主服务器插入数据。重新启动从服务器,会发现从服务器的主从关系消失了,从服务器成为另一个主服务器,若要恢复,需要重新执行slave of操作
  • 主服务器挂掉之后,重新启动从服务器,会发现从服务器的主从关系没有发生变化,仍然作为主服务器存在

3、复制原理


  • Slave启动成功连接到master后会发送一个sync命令
  • Master接到命令启动后台的存盘进程,同时收集所有接收到的用于修改数据集命令,在后台进程执行完毕之后,master将传送整个数据文件到slave,以完成一次完全同步
  • 全量复制:而slave服务在接收到数据库文件数据后,将其存盘并加载到内存中
  • 增量复制Master继续将新的所有收集到的修改命令依次传送给slave,完成同步
  • 但是只要是重新连接master,一次完全同步(全量复制)将被自动执行

4、常见三招

  • 一主二仆

    上同一主两从

  • 薪火相传

    主服务器 - > 从服务器 - > 从服务器

    从服务器后面还作为其他从服务器的主服务器

  • 反客为主

    当一个master宕机后,后面的slave可以立刻升为master,其后面的slave不用做任何修改

    当主服务挂掉之后,使用slave no one可以将slave升为master(手动模式)

5、哨兵模式(sentinel)


反客为主的自动版,能够后台监控主机是否故障,如果故障了根据投票数自动将库转换为主库

步骤:

  • 自定义的/myredis目录下新建sentinel.conf文件,名字决不能错
  • 配置哨兵,填写内容 sentinel monitor mymaster 127.0.0.1 6379 1
    • 其中mymaster为监控对象起的服务器名称,1为至少有多少个哨兵同意迁移的数量
  • 启动哨兵
    • redis-sentinel /myredis/sentinel.conf

当主机挂掉,从机选举中产生新的主机

(大概10秒左右可以看到哨兵窗口日志,切换了新的主机)

哪个从机会被选举为主机呢?根据优先级别:slave-priority

原主机重启之后会变为从机

复制延迟

由于所有的写操作都是先在Master上操作,然后同步更新到Slave上,所以从Master同步到Slave机器有一定的延迟,当系统很繁忙的时候,延迟问题会更加严重,Slave机器数量的增加也会使得这个问题更加严重

选择主机的策略

image-20221101223911363

优先级在redis.conf中默认:replica-priority 100,值越小优先级越高

偏移量是指获得原主机数据最全的

每个redis实例启动后都会随机生成一个40位的runid

第十三章、Redis集群


Redis集群实现了对Redis的水平扩容,即启动N个redis节点,将整个数据库分布存储在这N个节点中,每个节点存储总数据的 1 / N。

Redis集群通过分区(partition)来提供一定程序的可用性:即使集群中有一部分节点失效或者无法进行通讯,集群也可以继续处理命令请求。

1、问题


容量不够,redis如何进行扩容?

并发写操作,redis如何分摊?

另外,主从模式,薪火相传模式,主机宕机,导致ip地址发生变化,应用程序中配置需要修改对应的主机地址、端口号等信息

之前通过代理主机来解决,但是redis3.0中提供了解决方案。就是无中心集群配置。

2、创建集群


  • 修改配置文件

    cluster-enabled yes 打开集群模式

    cluster-config-file nodes-6379.conf 设定节点配置文件名

    cluster-node-timeout 15000 设定节点失联时间,超过该时间(毫秒),集群自动进行主从切换

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T1iTsMu6-1683813026085)(C:\Users\12086\AppData\Roaming\Typora\typora-user-images\image-20221102083623847.png)]

下面还少了2条上面提到的配置

3、集群相关的操作


  • 访问集群

    redis-cli -c -p 端口号

  • 查看集群信息

    cluster nodes

  • Redis cluster如何分配这六个节点?

    一个集群至少有三个主节点
    选项 --cluster-replicas 1表示我们希望为集群中非每个主节点创建一个从节点
    分配原则尽量保证每个主数据库运行在不同的IP地址,每个从库和主库不在同一个IP地址上
    
  • 什么是slots?

    image-20221102085149280
(1)一个Redis集群包含16384个插槽(hash slot),数据库中的每个键都属于这16384个插槽中的其中一个。
(2)集群使用公式crc16(key) % 16384来计算键key属于哪一个插槽,其中crc16(key) 语句用于计算键key的crc16校验和。
(3)集群中的每个节点负责处理一部分插槽。举个例子,如果一个集群可以有主节点,其中:
	节点A负责处理0号至5460号插槽
	节点A负责处理5461号至10922号插槽
  • 如果所有某一段插槽的主节点都宕掉,redis服务是否还能继续?
image-20221102090525858

4、Redis集群的不足


多键操作时不被支持的。

多键的Redis事务是不被支持的。Lua脚本不被支持。

由于集群方案出现较晚,很多公司都已经采用了其他的集群方案,而代理或者客户端分片的方案想要迁移到redis cluster,需要整体迁移而不是逐步过渡,复杂度较大。

第十四章、Redis应用问题解决


1、缓存穿透


  • 问题描述

    key对应的数据在数据源(数据库),每次针对此key的请求从缓存中获取不到,请求都会压到数据源,从而可能压垮数据源。比如用一个不存在的用户id获取用户信息,不论缓存还是数据库都没有,若黑客利用此漏洞进行攻击可能压垮数据库。

    image-20221102091551630
  • 解决方案

    一个一定不存在缓存以及查询不到的数据,由于缓存是不命中时被动写入的,并且处于容错考虑,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层区查询,失去了缓存的意义。

    (1)对空值进行缓存:如果一个查询返回的数据为空(不管数据是否存在),我们仍然把这个空结果(null)进行缓存,设置空结果的过期时间会很短,最多不超过5分钟。

    (2)设置可访问的名单:使用bitmaps类型定义一个可以访问的名单,名单id作为bitmaps的偏移量,每次访问和bitmap里面的id进行比较,如果访问id不在bitmaps里面,进行拦截,不允许访问。

    (3)采用布隆过滤器:布隆过滤器是1970年由布隆提出的,它实际上就是一个很长的二进制向量和一系列随机映射函数(哈希函数),原理类似于bitmaps。

    (4)进行实时监控:当发现Redis的命中率开始急速降低,需要排查访问对象和访问的数据,和运维人员配合,可以设置黑名单限制服务。

2、缓存击穿


  • 问题描述

    key对应的数据存在,但在redis中过期,此时若有大量的并发请求过来,这些请求发现缓存过期一般都会从后端数据库加载数据并设为到缓存,这个时大并发的请求可能会瞬间把后端数据库压垮。

    image-20221102092441897

  • 解决方案

    key可能会在某些时间点被超并发地访问,是一种非常“热点”的数据。这个时候,需要考虑一个问题:缓存被“击穿”的问题。

    (1)预先设置热门数据:在redis高峰访问之前,把一些热门数据提前存入到redis里面,加大这些热门数据key的时长。

    (2)实时调整:现场监控哪些数据热门,实时调整key的过期时长

    (3):使用锁

    • 就是在缓存失效的时候(判断拿出来的值为空),不是立即去load db。
    • 先使用缓存工具的某些带成功操作返回值的操作,比如Redis的setnx去set一个mutex key
    • 当操作返回成功时,再进行load db的操作,并回设缓存,最后删除mutex key
    • 当操作返回失败,证明有线程在load db。当前线程睡眠一段时间再重试整个get缓存的方法

3、缓存雪崩


  • 问题描述

    key对应的数据存在,但在redis中过期,此时若有大量的并发请求,这些请求发现缓存过期一般都会从后端数据库加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端数据库压垮。

    缓存雪崩与缓存击穿的区别在于这里针对很多key缓存,前者是某一个key。

    image-20221102093119422
  • 解决方案

    缓存失效时的雪崩效应对底层系统的冲击非常可拍!

    (1):构建多级缓存架构:Nginx缓存 + redis缓存 + 其他缓存(ehcache等)

    (2):使用锁或者队列:用加锁或者队列的方式来保证不会有大量的线程对数据库一次性进行读写,从而避免失效时大量的并发请求落到底层存储系统上,不适用高并发情况

    (3):设置过期标志更新缓存:记录缓存数据是否过期(设置提前量),如果过期会触发通知另外的线程区后台区更新实际key的缓存

    (4):将缓存失效时间分散开:比如我们可以在原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件

4、分布式锁


4.1问题描述

随着业务发展的需要,原单体机部署的系统被演化为分布式集群系统后,由于分布式系统多线程、多进程并且分布在不同及其上,这将使得原单机部署情况下的并发控制锁策略失效,单纯的Java API并不能提供分布式锁的能力。为了解决这个问题就需要一种跨JVM的互斥机制来控制共享资源的访问,这就是分布式锁要解决的问题!

分布式锁主流的实现方案:

  1. 基于数据库实现的分布式锁
  2. 基于缓存(Redis等)
  3. 基于Zookeeper

每一种分布式锁解决方案都有各自的优缺点:

  1. 性能:redis最高
  2. 可靠性:zookeeper最高

这里,我们基于redis实现分布式锁

4.2一些基本操作

  • 使用setnx上锁,通过del释放锁

  • 锁一直没有释放,设置key过期时间,自动释放

    setnx users 10
    
    expire users 10 
    
  • 上锁之后突然出现异常,无法设置时间了

    #上锁时候同时设置时间就可以了
    set users 10 nx ex 12
    
  • 通过UUID防止锁误删的情况

image-20221102104003839
  • 原子性操作造成问题image-20221102104407743

​ 需要通过Lua脚本保证原子性

4.3总结

为了保证分布式锁可用,我们至少要确保锁的实现同时满足一下四个条件:

  • 互斥性:在任意时刻,只有一个客户端能持有锁
  • 不会发生死锁。即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也不能保证后续其他客户端能加锁
  • 解铃还须系铃人。加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了
  • 加锁和解锁必须具有原子性。

第十五章、Redis6新特性


1、ACL


  • Redis ACL是Access Control List (访问控制列表)的缩写,该功能允许根据可以执行的命令和可以访问的键来限制某些连接

  • 在Redis5 版本之前,Redis安全规则只有密码控制还有通过rename来调整高危命令比如flushdb、keys* 等。Redis6则提供ACL的功能对用户进行更细粒度的权限控制。

    • 接入权限:用户名和密码
    • 可以执行的命令
    • 可以操作的key
  • 参考网站:https://redis.io/topics/acl

2、IO多线程


  • 简介

    	Redis6 终于支撑多线程了,告别单线程了吗?
    	IO多线程其实指客户端交互部分的网络I0交互处理模块多线程,而非执行命令多线程。Redis6执行命令依然是单线程。
    
  • 原理架构

    	Redis 6加入多线程,但跟 Memcached 这种从I0 处理到数据访问多线程的实现模式有些差异。Redis的多线程部分只是用来处理网络数据的读写和协议解析,执行命令仍然是单线程。之所以这么设计是不想因为多线程而变得复杂,需要去控制 key、lua、事务,LPUSH/LPOP等等的并发问题。整体的设计大体如下:
    
  • 另外,多线程IO默认是不开启的,需要在配置文件中配置

    io-threads-do-reads yes

    io-threads 4

3、工具支持cluster


​ 之前老版 Redis想要搭集群需要单独安装ruby环境,Redis 5将redis-trib.rb的功能集成到redis-cli。另外官方redis-benchmark工具开始支持cluster模式了,通过多线程的方式对多个分片进行压测。
分布式锁解决方案都有各自的优缺点:

  1. 性能:redis最高
  2. 可靠性:zookeeper最高

这里,我们基于redis实现分布式锁

4.2一些基本操作

  • 使用setnx上锁,通过del释放锁

  • 锁一直没有释放,设置key过期时间,自动释放

    setnx users 10
    
    expire users 10 
    
  • 上锁之后突然出现异常,无法设置时间了

    #上锁时候同时设置时间就可以了
    set users 10 nx ex 12
    
  • 通过UUID防止锁误删的情况

image-20221102104003839
  • 原子性操作造成问题image-20221102104407743

​ 需要通过Lua脚本保证原子性

4.3总结

为了保证分布式锁可用,我们至少要确保锁的实现同时满足一下四个条件:

  • 互斥性:在任意时刻,只有一个客户端能持有锁
  • 不会发生死锁。即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也不能保证后续其他客户端能加锁
  • 解铃还须系铃人。加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了
  • 加锁和解锁必须具有原子性。

第十五章、Redis6新特性


1、ACL


  • Redis ACL是Access Control List (访问控制列表)的缩写,该功能允许根据可以执行的命令和可以访问的键来限制某些连接

  • 在Redis5 版本之前,Redis安全规则只有密码控制还有通过rename来调整高危命令比如flushdb、keys* 等。Redis6则提供ACL的功能对用户进行更细粒度的权限控制。

    • 接入权限:用户名和密码
    • 可以执行的命令
    • 可以操作的key
  • 参考网站:https://redis.io/topics/acl

2、IO多线程


  • 简介

    	Redis6 终于支撑多线程了,告别单线程了吗?
    	IO多线程其实指客户端交互部分的网络I0交互处理模块多线程,而非执行命令多线程。Redis6执行命令依然是单线程。
    
  • 原理架构

    	Redis 6加入多线程,但跟 Memcached 这种从I0 处理到数据访问多线程的实现模式有些差异。Redis的多线程部分只是用来处理网络数据的读写和协议解析,执行命令仍然是单线程。之所以这么设计是不想因为多线程而变得复杂,需要去控制 key、lua、事务,LPUSH/LPOP等等的并发问题。整体的设计大体如下:
    
  • 另外,多线程IO默认是不开启的,需要在配置文件中配置

    io-threads-do-reads yes

    io-threads 4

3、工具支持cluster


​ 之前老版 Redis想要搭集群需要单独安装ruby环境,Redis 5将redis-trib.rb的功能集成到redis-cli。另外官方redis-benchmark工具开始支持cluster模式了,通过多线程的方式对多个分片进行压测。

你可能感兴趣的:(学习笔记,redis,学习,笔记)