当前位置: 首页 > news >正文

Redis学习笔记

目录

Nosql概述

为什么用Nosql

什么是Nosql

Nosql四大分类

Redis入门

概述

Windows安装

Linux安装

 测试性能 

 基础知识

五大数据类型

Redis-Key

String(字符串)

List(列表)

Set(集合)

Hash(哈希)

Zset(有序集合)

命令汇总

三大特殊数据类型

geospatial(地理位置)

hyperloglog(基数统计) 

bitmap(位图)

命令汇总 

事务

Jedis

常用API测试

事务测试 

Springboot整合

Redis.conf详解

Redis持久化

Redis发布订阅

Redis主从复制

Redis缓存穿透和雪崩


Nosql概述

为什么用Nosql

单机时代

瓶颈 

1,数据量太大,一台机器放不下了

2,数据的索引太大了(B+Tree),一台机器也放不下

3,访问量太大(读写混合),一个服务器承受不了

缓存时代(读写分离)+垂直拆分

发展过程:优化数据结构索引---->文件缓存(IO)----->Memcached(缓存)

分库分表+水平拆分+mysql集群

本质:数据库(读写)

早些年MyISAM:表锁,十分影响效率,高并发会出现严重锁问题

转战Innodb:行锁

分库分表,解决写的压力

一个互联网基本架构模型

什么是Nosql

Not Only SQL(不仅仅是SQL)

泛指非关系型数据库

特点:

1,方便扩展(数据之间没有关系,很好扩展)

2,大数据量,高性能(Redis一秒可以写8W次,可以读取11W次,nosql的缓存是记录级,是一种细粒度的缓存)

3,数据类型是多样型的(不需要事先设计数据库,随取随用,量很大的表就无法设计)

4,传统的RDBMRDBMSS和NoSQL区别

        传统的:结构化组织;SQL;数据和关系都存在单独的表;操作,数据定义语言;严格的一致性;基础事务。。。。

        NoSQL:不仅仅是数据;没有固定查询语言;价值对存储,列存储,文档存储,图形存储(社交关系);最终一致性;CAP定理和BASE(异地多活);高性能,高可用,高可扩展性。。。。

Nosql四大分类

1,KV键值对

        Redis

2,文件型数据库(bson格式和json格式)

  • MongoDB
    • 基于分布式文件存储的数据库,C++编写,主要用来处理大量文档
    •  是介于关系和非关系型数据库中间的产品,是非关系型数据库功能最丰富,最像关系型数据库的

3,列存储数据库

        HBase

        分布式文件系统

4,图关系数据库

        他不是存图形,放的是关系,比如:朋友圈社交网络,广告推荐

        Neo4j,InfoGrid

Redis入门

概述

Redis是什么?

Remote Dictionary Server,即远程字典服务,C语言编写的、支持网络、可基于内存亦可持久化的日志型、key-value数据库,并提供多种语言的API

redis会周期性的把个更新的数据写入测i盘或者把修改操作写入追加的记录文件,并且在次基础上实现了master-slave(主从)同步。

免费开源,是当下热门的nosql技术之一,也被称之为结构化数据库

Redis能干吗?

1,内存存储,持久化,内存中式断电即失,所以持久化很重要(rdb、aof)

2,效率高,可以用于高速缓存

3,发布订阅系统

4,地图信息分析

5,计时器、计数器(浏览量)

.......

Redis特性

 1,多样的数据类型

2,持久化

3,集群

4,事务

.......

需求

官网 Redis - The Real-time Data Platform

中文网 Redis中文网

下载地址:

Windows安装

1,下载

2,解压:解压到自己的环境目录

3,双击启动服务即可:默认端口6379

4,使用redis客户端连接redis

ping之后显示pong就连接成功了

测试加入数据k-v

Redis 简介_redis教程

推荐使用linux开发

Linux安装

1,下载安装包

2,解压安装包,程序放/opt目录下 

 3,进入解压后的文件,可以看到配置文件

4, 安装gcc-c++

yum install gcc-c++
gcc -v
make
make
make install
yum install gcc-c++
#查看版本
[root@xhm redis-6.0.4]# gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-redhat-linux/8/lto-wrapper
OFFLOAD_TARGET_NAMES=nvptx-none
OFFLOAD_TARGET_DEFAULT=1
Target: x86_64-redhat-linux
Configured with: ../configure --enable-bootstrap --enable-languages=c,c++,fortran,lto --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-shared --enable-threads=posix --enable-checking=release --enable-multilib --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-gcc-major-version-only --with-linker-hash-style=gnu --enable-plugin --enable-initfini-array --with-isl --disable-libmpx --enable-offload-targets=nvptx-none --without-cuda-driver --enable-gnu-indirect-function --enable-cet --with-tune=generic --with-arch_32=x86-64 --build=x86_64-redhat-linux
Thread model: posix
gcc version 8.5.0 20210514 (Red Hat 8.5.0-4) (GCC) #配置环境  第一次执行会下载很多环境
[root@xhm redis-6.0.4]# make
[root@xhm redis-6.0.4]# make
cd src && make all
make[1]: Entering directory '/opt/redis-6.0.4/src'CC Makefile.depHint: It's a good idea to run 'make test' ;)make[1]: Leaving directory '/opt/redis-6.0.4/src'
[root@xhm redis-6.0.4]# make install
cd src && make install
make[1]: Entering directory '/opt/redis-6.0.4/src'Hint: It's a good idea to run 'make test' ;)INSTALL installINSTALL installINSTALL installINSTALL installINSTALL install
make[1]: Leaving directory '/opt/redis-6.0.4/src'
[root@xhm redis-6.0.4]# 

5,redis默认安装路径  /usr 

6,将redis配置文件复制到当前目录 

7, 默认不是后台启动,我们需要后台启动,修改配置文件

redis.conf 配置项说明:Redis 配置 - 菜鸟教程

[root@xhm xhmcopyredisconfig]# vim redis.conf 

8,启动,通过指定配置文件启动

9,使用redis客户端连接,测试set

[root@xhm bin]# redis-cli -p 6379
127.0.0.1:6379> ping
PONG
127.0.0.1:6379> set name xhm  #设置
OK
127.0.0.1:6379> get name   #获取
"xhm"
127.0.0.1:6379> keys *   #查看所有key
1) "name"
127.0.0.1:6379> 

 10,查看redis进程是否开启


11,如何关闭redis服务 shutdowm

开启进入流程

[root@xhm ~]# cd /usr/local/bin
[root@xhm bin]# ls
chardetect  cloud-init-per    jemalloc-config  jsondiff     jsonschema        luajit-2.1.0-beta3  redis-benchmark  redis-cli       xhmcopyredisconfig
cloud-id    dump.rdb          jemalloc.sh      jsonpatch    libmcrypt-config  mcrypt              redis-check-aof  redis-sentinel
cloud-init  easy_install-3.8  jeprof           jsonpointer  luajit            mdecrypt            redis-check-rdb  redis-server
[root@xhm bin]# redis-server xhmcopyredisconfig/redis.conf 
366374:C 24 Dec 2024 20:46:30.481 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
366374:C 24 Dec 2024 20:46:30.481 # Redis version=6.0.4, bits=64, commit=00000000, modified=0, pid=366374, just started
366374:C 24 Dec 2024 20:46:30.481 # Configuration loaded
[root@xhm bin]# redis-cli -p 6379
127.0.0.1:6379> 

 测试性能 

redis-benchmark可选参数:Redis 性能测试 - 菜鸟教程

 redis-benchmark是一个压力测试工具,官方自带测试性能

简单测试:如上图默认是50个并发,10000个请求,我想做100个并发,100000个请求

开启服务
[root@xhm bin]# redis-server xhmcopyredisconfig/redis.conf 测试
[root@xhm bin]# redis-benchmark -h localhost -p 6379 -c 100 -n 100000

查看分析如下图: 

 基础知识

有16个数据库,默认使用第0个(0号数据库)

vim xhmcopyredisconfig/redis.conf 

可以使用select进行切换:select 3

[root@xhm bin]# redis-cli -p 6379
127.0.0.1:6379> select 3
OK
127.0.0.1:6379[3]> 

查看数据库大小 :  dbsize

127.0.0.1:6379[3]> dbsize
(integer) 0
127.0.0.1:6379[3]> set name xhm
OK
127.0.0.1:6379[3]> get name
"xhm"
127.0.0.1:6379[3]> dbsize
(integer) 1
127.0.0.1:6379[3]> 

查看所有的key: keys *

127.0.0.1:6379[3]> keys *
1) "name"

清空全部:flushall

127.0.0.1:6379[3]> select 0   #进去0号数据库,查看所有key
OK
127.0.0.1:6379> keys *
1) "key:{tag}:__rand_int__"
2) "myhash:{tag}:__rand_int__"
3) "mylist:{tag}"
4) "counter:{tag}:__rand_int__"
127.0.0.1:6379> select 3   #进入3号数据库,清除所有
OK
127.0.0.1:6379[3]> flushall
OK
127.0.0.1:6379[3]> select 0    #返回0号数据库,数据就已经被清干净了
OK
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> 

清空当前库:flushdb

127.0.0.1:6379[3]> flushdb
OK
127.0.0.1:6379[3]> keys *
(empty array)
127.0.0.1:6379[3]> 

redis是单线程 ,为什么还那么快?

redis是基于内存操作得,cpu不是redis的性能瓶颈,redis的瓶颈是根据机器的内存和网络带宽

核心:redis是把所有数据全部放在内存中的,所以说使用单线程取操作效率就是最高的,多线程(CPU上下文会切换:这是个耗时的操作!!),对于内存来说,没有上下文切换效率就是最高的,多次读写都在cpu上,在内存情况下,这个就是最佳方案。

误区:不是高性能的服务器就一定是多线程的;不是多线程(CPU会上下文切换)一定比单线程效率高

五大数据类型

官方命令文档:redis命令手册

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

Redis是一个开源(BSD许可),内存存储的数据结构服务器,可用作数据库,高速缓存消息队列代理。它支持字符串、哈希表、列表、集合、有序集合,位图,hyperloglogs等数据类型。内置复制、Lua脚本、LRU收回、事务以及不同级别磁盘持久化功能,同时通过Redis Sentinel提供高可用,通过Redis Cluster提供自动分区。

Redis-Key

设置值:set name xhm

查看所有key:keys *

判断key是否存在:exists name

127.0.0.1:6379> set name xhm
OK
127.0.0.1:6379> set age 18
OK
127.0.0.1:6379> exists name 
(integer) 1
127.0.0.1:6379> exists name1 
(integer) 0

移除key:move name 1

127.0.0.1:6379> move name 1
(integer) 1
127.0.0.1:6379> keys *
1) "age"

设置过期时间:expire name 10

查看多久过期时间:ttl name

127.0.0.1:6379> expire name 10    #设置10秒过期
(integer) 1
127.0.0.1:6379> ttl name   #还有6秒过期
(integer) 6
127.0.0.1:6379> ttl name
(integer) 3
127.0.0.1:6379> ttl name  #已经过期
(integer) -2
127.0.0.1:6379> keys *   #查看已经没有name了
1) "age"

查看key的类型:type key 

127.0.0.1:6379> type name
string

String(字符串)

给某个键值里面最追加字符串:append key str,如果没有key会新建

127.0.0.1:6379> get name
"xhm"
127.0.0.1:6379> append name wp
(integer) 5
127.0.0.1:6379> get name
"xhmwp"#如果不存在key则相当于set key
127.0.0.1:6379> append key 01
(integer) 2
127.0.0.1:6379> keys *
1) "key"
2) "name"
3) "age"

字符串长度:strlen key

127.0.0.1:6379> strlen name
(integer) 5

i++/i--

自增:incr key

自减:decr key

127.0.0.1:6379> set views 0
OK
127.0.0.1:6379> incr views
(integer) 1
127.0.0.1:6379> incr views
(integer) 2
127.0.0.1:6379> incr views
(integer) 3
127.0.0.1:6379> decr views
(integer) 2
127.0.0.1:6379> decr views
(integer) 1
127.0.0.1:6379> decr views
(integer) 0

步长:i+=

一次自增n:incrby key n

一次自减n:decrby key n

127.0.0.1:6379> get views
"8"
127.0.0.1:6379> incrby views 10
(integer) 18
127.0.0.1:6379> incrby views 10
(integer) 28
127.0.0.1:6379> incrby views 10
(integer) 38
127.0.0.1:6379> decrby views 2
(integer) 36
127.0.0.1:6379> decrby views 2
(integer) 34
127.0.0.1:6379> decrby views 2
(integer) 32

字符串范围range

getrange key start end  #(下标默认从0开始)

127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> set key1 xhmandwp,helloword
OK
127.0.0.1:6379> get key1
"xhmandwp,helloword"
#获取下标0-7的字符
127.0.0.1:6379> getrange key1 0 7
"xhmandwp"#获取所有字符
127.0.0.1:6379> getrange key1 0 -1
"xhmandwp,helloword"
127.0.0.1:6379> 

 替换:setrange key offset value #(offset 从第几位开始(0开始的下标),value替换字符串)

127.0.0.1:6379> set key2 abcdefgh
OK
127.0.0.1:6379> get key2
"abcdefgh"
127.0.0.1:6379> setrange key2 1 xxx  #从下标1开始,替换2个字符为xx
(integer) 8
127.0.0.1:6379> get key2
"axxxefgh"

setex (set with expire)  #设置过期时间

        setex key time value

setnx (set if not exist) #如果不存在,再设置,再分布式锁中会常常使用

        setnx key value

127.0.0.1:6379> setex key01 20 qwe
OK
127.0.0.1:6379> ttl key01
(integer) 14
127.0.0.1:6379> keys *
1) "key01"
127.0.0.1:6379> ttl key01
(integer) 1
127.0.0.1:6379> ttl key01
(integer) -2
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> set key1 dfg
OK
127.0.0.1:6379> keys *
1) "key1"
127.0.0.1:6379> setnx key gfd   #不存在,则创建
(integer) 1
127.0.0.1:6379> setnx key1 gfd    #存在,则创建失败
(integer) 0
127.0.0.1:6379> keys *
1) "key"
2) "key1"
127.0.0.1:6379> 

 批量设置值

mset

        mset k1 v1 [k2 v2 k3 v3]

msetnx:原子性,要么一起成功,要么一起失败

mget

        mset k1 k2 k3

127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3
OK
127.0.0.1:6379> mget k1 k2 k3
1) "v1"
2) "v2"
3) "v3"#k1已经存在,则创建失败,k4也创建不成功,原子性,要么一起成功,要么一起失败
127.0.0.1:6379> msetnx k1 v1 k4 v4
(integer) 0

设置对象:set user 1 {name:xhm,age 1}  # 设置一个user:1对象值为json字符串来保存一个对象

user:{id}:{filed}

比如设置浏览量,set article:100:views 0

127.0.0.1:6379> mset user:1:name xhm user:1:age 2
OK
127.0.0.1:6379> mget user:1:name user:1:age
1) "xhm"
2) "2"

 getset :先get再set

 #如果不存在则,返回nil

#如果存在值,获取原来的值,并设置新的值

127.0.0.1:6379> getset db redis  #如果不存在则,返回nil
(nil)
127.0.0.1:6379> get db
"redis"
127.0.0.1:6379> getset db mongodb  #如果存在值,获取原来的值,并设置新的值
"redis"
127.0.0.1:6379> get db
"mongodb"
127.0.0.1:6379> 
  •  String类型使用场景:字符串和数字
  • 计数器
  • 统计多单位数量
  • 粉丝数
  • 对象缓存

Redis Setnx 命令_只有在 key 不存在时设置 key 的值。

List(列表)

redis里面,可以把list弄成栈,队列,阻塞队列

所有的list命令都是以 l 开头的

添加元素:lpush list 值   #将一个值或多个值插入列表头部(左边)

添加元素:rpush list 值   #将一个值或多个值插入列表尾部(右边边)

获取所有元素:lrange list 0 -1

获取指定元素:lrange 0 1   #这里的0是从最后一个插入元素往前的顺序来的(最后插入的在头部)

127.0.0.1:6379> lpush list one
(integer) 1
127.0.0.1:6379> lpush list two
(integer) 2
127.0.0.1:6379> lpush list three
(integer) 3
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> lrange list 0 1
1) "three"
2) "two"127.0.0.1:6379> rpush list right
(integer) 4
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "tow"
3) "one"
4) "right"

 移除元素:lpop list 元素/rpop list 元素

127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "tow"
3) "one"
4) "right"
127.0.0.1:6379> lpop list    #移除第一个元素
"three"
127.0.0.1:6379> lrange list 0 -1
1) "tow"
2) "one"
3) "right"
127.0.0.1:6379> rpop list   #移除最后一个元素
"right"
127.0.0.1:6379> lrange list 0 -1
1) "tow"
2) "one"

移除指定值: lrem list  1 one   #移除1个值为One

                      lrem list  2 three   #移除2个值为three

127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "three"
3) "tow"
4) "one"
5) "one"
127.0.0.1:6379> lrem list 1 one
(integer) 1
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "three"
3) "tow"
4) "one"
127.0.0.1:6379> lrem list 2 three
(integer) 2
127.0.0.1:6379> lrange list 0 -1
1) "tow"
2) "one"
127.0.0.1:6379> 

获取元素:lindex list 0   #通过下标获取值 

127.0.0.1:6379> lrange list 0 -1
1) "tow"
2) "one"
127.0.0.1:6379> lindex list 1
"one"
127.0.0.1:6379> lindex list 0
"tow"

获取长度:llen list

127.0.0.1:6379> llen list
(integer) 2

trim 修剪 --->list截断

通过下标截取指定长度:ltrim list start end   #下标为start-end的元素;截取之后,就是新下标了

127.0.0.1:6379> lrange list 0 -1
1) "h1"
2) "h2"
3) "h3"
4) "h4"
127.0.0.1:6379> ltrim list 0 2  #截取从0-2的元素
OK
127.0.0.1:6379> lrange list 0 -1
1) "h1"
2) "h2"
3) "h3"
127.0.0.1:6379> ltrim list 1 2
OK
127.0.0.1:6379> lrange list 0 -1
1) "h2"
2) "h3"
127.0.0.1:6379> 

移除列表最后一个元素,并且添加到新的列表中:rpoplpush  原列表 新列表

127.0.0.1:6379> lrange list 0 -1
1) "1"
2) "2"
3) "3"
127.0.0.1:6379> rpoplpush list mylist
"3"
127.0.0.1:6379> lrange list 0 -1
1) "1"
2) "2"
127.0.0.1:6379> lrange mylist 0 -1
1) "3"

判断是否存在列表:exists 列表名

指定下标值替换为另外一个值(只是修改原列表,不可新增元素):lset 列表名 下标 值

127.0.0.1:6379> exists lits
(integer) 0
127.0.0.1:6379> lpush list item
(integer) 1
127.0.0.1:6379> lrange list 0 0
1) "item"
127.0.0.1:6379> lset list 0 newitem
OK
127.0.0.1:6379> lrange list 0 0
1) "newitem"
#如果不存在这个下标则报错,没有这个列表也会报错
127.0.0.1:6379> lset list 1 newitem2
(error) ERR index out of range

插入值:linsert list before/after 原元素 新增元素  #在某个元素之前或之后插入新元素 

127.0.0.1:6379> lrange list 0 -1
1) "hello"
2) "world"
127.0.0.1:6379> linsert list before world new
(integer) 3
127.0.0.1:6379> lrange list 0 -1
1) "hello"
2) "new"
3) "world"
127.0.0.1:6379> 

 小结

  •  它实际上是一个链表,before node after,left right都可以插入
  • 如果key不存在,创建新的链表
  • 如果key存在,新增内容
  • 如果移除了所有值,空链表,也代表不存在
  • 在两边插入或者改动值,效率最高;中间元素,相对效率低些

消息排队,消息队列(lpush,rpop),栈(lpush lpop)

Set(集合)

set中的值不能重复,set的命令都是以s开头的

添加元素:sadd 集合名 元素值

查看元素:smembers 集合名

是否存在元素:sismember 集合名 元素名  #存在返回1  不存在fan

127.0.0.1:6379> sadd set hello
(integer) 1
127.0.0.1:6379> sadd set world
(integer) 1
127.0.0.1:6379> sadd set happy
(integer) 1
127.0.0.1:6379> smembers set
1) "hello"
2) "happy"
3) "world"
127.0.0.1:6379> sismember set hello
(integer) 1
127.0.0.1:6379> sismember set my
(integer) 0

 获取set集合中元素个数:scard 集合名

127.0.0.1:6379> scard set
(integer) 3

 移除元素:srem 集合名 元素值

127.0.0.1:6379> srem set hello
(integer) 1
127.0.0.1:6379> scard set
(integer) 2
127.0.0.1:6379> smembers set
1) "happy"
2) "world"

 set是无序不重复集合,抽随机

127.0.0.1:6379> SMEMBERS set
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
6) "6"
7) "7"
8) "8"
127.0.0.1:6379> SRANDMEMBER set  #随机抽取一个
"3"
127.0.0.1:6379> SRANDMEMBER set   #随机抽取一个
"3"
127.0.0.1:6379> SRANDMEMBER set    #随机抽取一个
"4"
127.0.0.1:6379> SRANDMEMBER set 3   #随机抽取3个
1) "6"
2) "7"
3) "2"
127.0.0.1:6379> SRANDMEMBER set 3
1) "7"
2) "2"
3) "1"

随机删除key:spop 集合名

127.0.0.1:6379> SMEMBERS set
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
6) "6"
7) "7"
8) "8"
127.0.0.1:6379> spop set
"6"
127.0.0.1:6379> spop set
"4"
127.0.0.1:6379> SMEMBERS set
1) "1"
2) "2"
3) "3"
4) "5"
5) "7"
6) "8"

将一个指定的值移动到另一个set集合中:smove 原集合 新集合 元素

127.0.0.1:6379> SMEMBERS set
1) "1"
2) "2"
3) "3"
4) "5"
5) "7"
6) "8"
127.0.0.1:6379> SMEMBERS set2
1) "b"
2) "a"
127.0.0.1:6379> smove set set2 8
(integer) 1
127.0.0.1:6379> SMEMBERS set2
1) "8"
2) "b"
3) "a"

场景:共同关注(并集)

数字集合类

差集:sdiff 集合1 集合2   #以集合1为参照,去掉跟集合2相同的元素,剩余的集合1中的元素

并集:sunion 集合1 集合2   

交集:sinter 集合1 集合2   #共同好友

127.0.0.1:6379> SMEMBERS set
1) "1"
2) "2"
3) "3"
127.0.0.1:6379> SMEMBERS set2
1) "3"
2) "4"
3) "5"
127.0.0.1:6379> SDIFF set set2
1) "1"
2) "2"
127.0.0.1:6379> SUNION set set2
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
127.0.0.1:6379> SINTER set set2
1) "3"

Hash(哈希)

map集合,key-Map  这个时候的值就是map集合

所有命令以h开头,本质和String类型没有太大区别,还是简单的key-value

设置值:hset 集合名 字段 值

获取值:hget 集合 字段

设置多个值:hmset 集合 字段1 值1 字段2 值2

获取多个值:hmget 集合 字段1 字段2

获取所有值+字段:hgetall 集合    #key和value都会获取

获取所有字段:hvals 集合名

获取所有值:hkeys 集合名

127.0.0.1:6379> hset hash filed1 value1  #设置单个值
(integer) 1
127.0.0.1:6379> hget hash filed1   #获取单个值
"value1"
127.0.0.1:6379> hmset hash filed2 value2 filed3 value3   #设置多个值
OK
127.0.0.1:6379> hmget hash filed1 filed2 filed3  #获取多个值
1) "value1"
2) "value2"
3) "value3"
127.0.0.1:6379> hgetall hash   #获取所有 键+值
1) "filed1"
2) "value1"
3) "filed2"
4) "value2"
5) "filed3"
6) "value3"
127.0.0.1:6379> hkeys hash   #获取所有键
1) "filed1"
2) "filed2"
3) "filed3"
127.0.0.1:6379> hvals hash   #获取所有值
1) "value1"
2) "value2"
3) "value3"

删除值:hdel 集合名 字段名   #删除hash指定的key字段,对应的value值就没有了

127.0.0.1:6379> hgetall hash
1) "filed1"
2) "value1"
3) "filed2"
4) "value2"
5) "filed3"
6) "value3"
127.0.0.1:6379> hdel hash filed1
(integer) 1
127.0.0.1:6379> hgetall hash
1) "filed2"
2) "value2"
3) "filed3"
4) "value3"

获取长度:hlen 集合名

127.0.0.1:6379> hlen hash
(integer) 3

判断key存在:hsxists 集合名 字段名

127.0.0.1:6379> hgetall hash
1) "filed2"
2) "value2"
3) "filed3"
4) "value3"
127.0.0.1:6379> HEXISTS hash filed1
(integer) 0
127.0.0.1:6379> HEXISTS hash filed2
(integer) 1
127.0.0.1:6379> 

指定自增:hincrby 集合 字段 加的数值(可正可负,负就相当于减了)

指定自减:hdecrby

127.0.0.1:6379> hmset hash num1 1 num2 2
OK
127.0.0.1:6379> HINCRBY hash num1 2    #num的值+2=3
(integer) 3
127.0.0.1:6379> hgetall hash
1) "filed2"
2) "value2"
3) "filed3"
4) "value3"
5) "num1"
6) "3"
7) "num2"
8) "2"
127.0.0.1:6379> HINCRBY hash num1 -3  #num1的值减3=0
(integer) 0
127.0.0.1:6379> hgetall hash
1) "filed2"
2) "value2"
3) "filed3"
4) "value3"
5) "num1"
6) "0"
7) "num2"
8) "2"

判断存在:hsetnx 集合 字段 值 #如果不存在则可以设置,如果存在则不能设置

127.0.0.1:6379> HSETNX hash num1 10   #存在则无法设置
(integer) 0
127.0.0.1:6379> hgetall hash
1) "filed2"
2) "value2"
3) "filed3"
4) "value3"
5) "num1"
6) "0"
7) "num2"
8) "2"
127.0.0.1:6379> HSETNX hash num3 10  #不存在则设置成功
(integer) 1
127.0.0.1:6379> hgetall hash1) "filed2"2) "value2"3) "filed3"4) "value3"5) "num1"6) "0"7) "num2"8) "2"9) "num3"
10) "10"
127.0.0.1:6379> 

应用:存变更数据   user-name:xhm age:18   尤其用户信息保存,经常变动的信息,更适合直接存对像

Zset(有序集合)

在set的基础上加了一个值,所有命令以z开头

set k v    ------>  zset k score v      zset多了一个计数位

新增:zadd 集合名 排序字段(scores) 值

查看:zrange 集合名 0 -1

127.0.0.1:6379> zadd salary 1000 xhm
(integer) 1
127.0.0.1:6379> zadd salary https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fimg-home.csdnimg.cn%2Fimages%2F20230724024159.png%3Forigin_url%3D200%26pos_id%3DCQVHWhGt&pos_id=CQVHWhGt0 wp
(integer) 1
127.0.0.1:6379> zadd salary 50 zs
(integer) 1
127.0.0.1:6379> zrange salary 0 -1
1) "zs"
2) "xhm"
3) "wp"

排序:zrangebyscore 集合 最小值(-inf) 最大值(+inf正无穷)   #就按中间值排序

排序:zrangebyscore 集合 最小值(-inf) 最大值(+inf正无穷)  withscores   #带上中间的值

排序:zrangebyscore 集合 最小值(-inf) 2500 withscores   #只差2500之内的

排序:zrevrange 集合 0 -1  #从高到底 大到小排序

127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf  #从小到大排序查看
1) "zs"
2) "xhm"
3) "wp"
4) "ls"
127.0.0.1:6379> ZRANGEBYSCORE salary -inf https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fimg-home.csdnimg.cn%2Fimages%2F20230724024159.png%3Forigin_url%3D200%26pos_id%3DCQVHWhGt&pos_id=CQVHWhGt0   #从小到大,查https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fimg-home.csdnimg.cn%2Fimages%2F20230724024159.png%3Forigin_url%3D200%26pos_id%3DCQVHWhGt&pos_id=CQVHWhGt0以内的
1) "zs"
2) "xhm"
3) "wp"
127.0.0.1:6379> ZRANGEBYSCORE salary -inf https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fimg-home.csdnimg.cn%2Fimages%2F20230724024159.png%3Forigin_url%3D200%26pos_id%3DCQVHWhGt&pos_id=CQVHWhGt0 withscores  #从小到大,查https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fimg-home.csdnimg.cn%2Fimages%2F20230724024159.png%3Forigin_url%3D200%26pos_id%3DCQVHWhGt&pos_id=CQVHWhGt0以内的,加上工资
1) "zs"
2) "50"
3) "xhm"
4) "1000"
5) "wp"
6) "https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fimg-home.csdnimg.cn%2Fimages%2F20230724024159.png%3Forigin_url%3D200%26pos_id%3DCQVHWhGt&pos_id=CQVHWhGt0"
127.0.0.1:6379> ZREVRANGE salary 0 -1   #从大到小排序查看
1) "ls"
2) "wp"
3) "xhm"
4) "zs"
127.0.0.1:6379> 

移除:zrem 集合 元素

127.0.0.1:6379> zrem salary ls
(integer) 1
127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf withscores
1) "zs"
2) "50"
3) "xhm"
4) "1000"
5) "wp"
6) "https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fimg-home.csdnimg.cn%2Fimages%2F20230724024159.png%3Forigin_url%3D200%26pos_id%3DCQVHWhGt&pos_id=CQVHWhGt0"
127.0.0.1:6379> zrem salary ls   #删掉李四
(integer) 1
127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf withscores
1) "zs"
2) "50"
3) "xhm"
4) "1000"
5) "wp"
6) "https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fimg-home.csdnimg.cn%2Fimages%2F20230724024159.png%3Forigin_url%3D200%26pos_id%3DCQVHWhGt&pos_id=CQVHWhGt0"

获取集合中的个数:zcard 集合名

区间变量:zcount 集合 最小 最大    #在这个区间的元素个数  #包含了边界值

127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf withscores
1) "zs"
2) "50"
3) "xhm"
4) "1000"
5) "wp"
6) "https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fimg-home.csdnimg.cn%2Fimages%2F20230724024159.png%3Forigin_url%3D200%26pos_id%3DCQVHWhGt&pos_id=CQVHWhGt0"
7) "ls"
8) "3000"
127.0.0.1:6379> zcard salary
(integer) 4
127.0.0.1:6379> ZCOUNT salary 100 https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fimg-home.csdnimg.cn%2Fimages%2F20230724024159.png%3Forigin_url%3D200%26pos_id%3DCQVHWhGt&pos_id=CQVHWhGt0  #包含了边界值
(integer) 2
127.0.0.1:6379> ZCOUNT salary 50 https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fimg-home.csdnimg.cn%2Fimages%2F20230724024159.png%3Forigin_url%3D200%26pos_id%3DCQVHWhGt&pos_id=CQVHWhGt0
(integer) 3

使用场景:

  • set排序,成绩表,工资排序 
  • 普通/紧急消息,带权重判断紧急程度
  • 排行榜实现

命令汇总

redis-key

设置值:set name xhm

查看所有key:keys *

判断key是否存在:exists name

移除key:move name 1

设置过期时间:expire name 10

查看多久过期时间:ttl name

查看key的类型:type key 

String

给某个键值里面最追加字符串:append key str,如果没有key会新建

字符串长度:strlen key

i++/i--

自增:incr key

自减:decr key

步长:i+=

一次自增n:incrby key n

一次自减n:decrby key n

字符串范围range

getrange key start end  #(下标默认从0开始)

替换:setrange key offset value #(offset 从第几位开始(0开始的下标),value替换字符串)

setex (set with expire)  #设置过期时间

        setex key time value

setnx (set if not exist) #如果不存在,再设置,再分布式锁中会常常使用

        setnx key value

批量设置值

mset

        mset k1 v1 [k2 v2 k3 v3]

msetnx:原子性,要么一起成功,要么一起失败

mget

        mset k1 k2 k3

设置对象:set user 1 {name:xhm,age 1}  # 设置一个user:1对象值为json字符串来保存一个对象

user:{id}:{filed}

比如设置浏览量,set article:100:views 0

 getset :先get再set

 #如果不存在则,返回nil

#如果存在值,获取原来的值,并设置新的值

list

添加元素:lpush list 值   #将一个值或多个值插入列表头部(左边)

添加元素:rpush list 值   #将一个值或多个值插入列表尾部(右边边)

获取所有元素:lrange list 0 -1

获取指定元素:lrange 0 1   #这里的0是从最后一个插入元素往前的顺序来的(最后插入的在头部)

移除元素:lpop list 元素/rpop list 元素

移除指定值: lrem list  1 one   #移除1个值为One

                      lrem list  2 three   #移除2个值为three

获取元素:lindex list 0   #通过下标获取值 (0 -1代表全部)

获取长度:llen list

trim 修剪 --->list截断

通过下标截取指定长度:ltrim list start end   #下标为start-end的元素;截取之后,就是新下标了

移除列表最后一个元素,并且添加到新的列表中:rpoplpush  原列表 新列表

判断是否存在列表:exists 列表名

指定下标值替换为另外一个值(只是修改原列表,不可新增元素):lset 列表名 下标 值

插入值:linsert list before/after 原元素 新增元素  #在某个元素之前或之后插入新元素 

set

添加元素:sadd 集合名 元素值

查看元素:smembers 集合名

是否存在元素:sismember 集合名 元素名  #存在返回1  不存在fan

获取set集合中元素个数:scard 集合名

移除元素:srem 集合名 元素值

set是无序不重复集合,抽随机 SRANDMEMBER set num(随机抽取,不写默认1,写了默认num个)

随机删除key:spop 集合名

将一个指定的值移动到另一个set集合中:smove 原集合 新集合 元素

场景:共同关注(并集)

数字集合类

差集:sdiff 集合1 集合2   #以集合1为参照,去掉跟集合2相同的元素,剩余的集合1中的元素

并集:sunion 集合1 集合2   

交集:sinter 集合1 集合2   #共同好友

hash

设置值:hset 集合名 字段 值

获取值:hget 集合 字段

设置多个值:hmset 集合 字段1 值1 字段2 值2

获取多个值:hmget 集合 字段1 字段2

获取所有值+字段:hgetall 集合    #key和value都会获取

获取所有字段:hvals 集合名

获取所有值:hkeys 集合名

删除值:hdel 集合名 字段名   #删除hash指定的key字段,对应的value值就没有了

获取长度:hlen 集合名

判断key存在:hexists 集合名 字段名

指定自增:hincrby 集合 字段 加的数值(可正可负,负就相当于减了)

指定自减:hdecrby

判断存在:hsetnx 集合 字段 值 #如果不存在则可以设置,如果存在则不能设置

zset

新增:zadd 集合名 排序字段(scores) 值

查看:zrange 集合名 0 -1

排序:zrangebyscore 集合 最小值(-inf) 最大值(+inf正无穷)   #就按中间值排序

排序:zrangebyscore 集合 最小值(-inf) 最大值(+inf正无穷)  withscores   #带上中间的值

排序:zrangebyscore 集合 最小值(-inf) 2500 withscores   #只差2500之内的

排序:zrevrange 集合 0 -1  #从高到底 大到小排序

移除:zrem 集合 元素

获取集合中的个数:zcard 集合名

区间变量:zcount 集合 最小 最大    #在这个区间的元素个数  #包含了边界值

三大特殊数据类型

geospatial(地理位置)

朋友定位,附近的人,打车距离计算

 添加地理位置:geoadd key 经度 纬度 member

规则:两极无法直接添加,我们一般会下载城市数据,直接通过java程序一次性导入

有效的经度从-180度到180度。

有效的纬度从-85.05112878度到85.05112878度。

当坐标位置超出上述指定范围时,该命令将会返回一个错误。

127.0.0.1:6379> geoadd china:city 104.04 30.39 chengdu
(integer) 1

获取指定城市的经纬度:geopos key member   

127.0.0.1:6379> geopos china:city chengdu
1) 1) "104.03999894857406616"2) "30.39000005171638463"

返回俩位置之间的距离:geodist key member1 member2 单位(km/m)   #

127.0.0.1:6379> GEODIST china:city chengdu suining km
"125.1850"

给定经纬度为中心,找某一般半径以内的地址:georadius key 经度 纬度 半径 单位(必须在当前集合中)

[WITHCOORD]:显示他人的定位信息

[COUNT count]:获取多少个

[WITHDIST]:显示到中心的距离

。。。参数如下图

附近的人?获得附近人的定位,通过半径查询

127.0.0.1:6379> GEORADIUS china:city 104.04 30.39 https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fimg-home.csdnimg.cn%2Fimages%2F20230724024159.png%3Forigin_url%3D200%26pos_id%3DCQVHWhGt&pos_id=CQVHWhGt km
1) "chengdu"
2) "suining"
3) "nanchong"
4) "zigong"
127.0.0.1:6379> GEORADIUS china:city 104.04 30.39 https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fimg-home.csdnimg.cn%2Fimages%2F20230724024159.png%3Forigin_url%3D200%26pos_id%3DCQVHWhGt&pos_id=CQVHWhGt km withcoord  #带经纬度
1) 1) "chengdu"2) 1) "104.03999894857406616"2) "30.39000005171638463"
2) 1) "suining"2) 1) "105.34000128507614136"2) "30.29999970751173777"
3) 1) "nanchong"2) 1) "106.04999810457229614"2) "30.47000092094743451"
4) 1) "zigong"2) 1) "104.46000069379806519"2) "29.21000117152468789"
127.0.0.1:6379> GEORADIUS china:city 104.04 30.39 https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fimg-home.csdnimg.cn%2Fimages%2F20230724024159.png%3Forigin_url%3D200%26pos_id%3DCQVHWhGt&pos_id=CQVHWhGt km withdist  #到中心的距离
1) 1) "chengdu"2) "0.0001"
2) 1) "suining"2) "125.1849"
3) 1) "nanchong"2) "192.9710"
4) 1) "zigong"2) "137.3643"
127.0.0.1:6379> GEORADIUS china:city 104.04 30.39 https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fimg-home.csdnimg.cn%2Fimages%2F20230724024159.png%3Forigin_url%3D200%26pos_id%3DCQVHWhGt&pos_id=CQVHWhGt km count 2  #获取两个
1) "chengdu"
2) "suining"

找出位于指定范围内的元素:GEORADIUSBYMEMBER key member radius 单位  #中心点是有给定的位置决定的

127.0.0.1:6379> GEORADIUSBYMEMBER china:city chengdu https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fimg-home.csdnimg.cn%2Fimages%2F20230724024159.png%3Forigin_url%3D200%26pos_id%3DCQVHWhGt&pos_id=CQVHWhGt km
1) "chengdu"
2) "suining"
3) "nanchong"
4) "zigong"

返回一个或多个位置元素的geohash:geohash key menmber1 menber2

将二维经纬度转换为以为的11位字符串,如果俩字符串越接近,距离越近

127.0.0.1:6379> geohash china:city chengdu suining
1) "wm3uzkrded0"
2) "wm6um5fnrc0"

Geo底层实现原理: Zset,我们可以使用Zset命令来操作Geo

比如

查看:zrange china:city 0 -1

 移除:zrem china:city zigong

127.0.0.1:6379> zrange china:city 0 -1
1) "huili"
2) "zigong"
3) "chengdu"
4) "suining"
5) "nanchong"
127.0.0.1:6379> zrem china:city zigong
(integer) 1
127.0.0.1:6379> zrange china:city 0 -1
1) "huili"
2) "chengdu"
3) "suining"
4) "nanchong"

hyperloglog(基数统计) 

简介

基数?一个集合中不重复的元素个数

A{1,3,5,7,8,7}  基数=5         B{1,3,5,7,8}

hyperloglog是基数统计的算法,优点:占用内存是固定的,放2^64不同的元素的基数,只需要费12kb内存,所以如果从内存角度比较的,此为首选

网页的UV(页面访问量),一个人访问一个网站多次,算一个人

传统方式,可使用set存用户id,然后统计set元素数量即可,但数量大之后会比较麻烦,我们的目的本是为了计数,不是为了存id

所有命令以p开头的

添加元素 pfadd key 元素1 元素2 。。

统计元素(不重复):pfcount key

求并集:pfmerge 生成的新集合3 原集合1 原集合2

如果允许容错,那就可以使用hyoerloglog(有0.83%的错误率),如果不允许容错,就使用set吧或者其他

127.0.0.1:6379> pfadd key1 a b c d e f g h  #添加元素
(integer) 1
127.0.0.1:6379> pfcount key1
(integer) 8
127.0.0.1:6379> pfadd key2 g h i j k l m n
(integer) 1
127.0.0.1:6379> pfcount key2
(integer) 8
127.0.0.1:6379> pfmerge key3 key1 key2  #求并集
OK
127.0.0.1:6379> pfcount key3
(integer) 14

bitmap(位图)

 场景:统计用户信息,活跃不活跃,登录未登录,打卡未打卡等

bitmap位图,数据结构, 都是操作二进制来进行记录的,只有0和1 两个状态

比如,打卡场景

添加打卡记录:setbit key offset value

查看打卡记录:getbit key offset

统计打卡天数:bitcount key

127.0.0.1:6379> SETBIT sign 0 1  #添加记录
(integer) 0
127.0.0.1:6379> SETBIT sign 1 0
(integer) 0
127.0.0.1:6379> SETBIT sign 2 0
(integer) 0
127.0.0.1:6379> SETBIT sign 3 1
(integer) 0
127.0.0.1:6379> SETBIT sign 4 1
(integer) 0
127.0.0.1:6379> SETBIT sign 5 0
(integer) 0
127.0.0.1:6379> SETBIT sign 6 0
(integer) 0 
127.0.0.1:6379> getbit sign 6   #查看记录
(integer) 0
127.0.0.1:6379> getbit sign 1
(integer) 0
127.0.0.1:6379> bitcount sign    #统计记录(1出现的次数)
(integer) 3

命令汇总 

geospatial 

1,添加地理位置:geoadd key 经度 纬度 member

2,获取指定城市的经纬度:geopos key member   

3,返回俩位置之间的距离:geodist key member1 member2 单位(km/m)   #

4,给定经纬度为中心,找某一般半径以内的地址:georadius key 经度 纬度 半径 单位(必须在当前集合中)

[WITHCOORD]:显示他人的定位信息

[COUNT count]:获取多少个

[WITHDIST]:显示到中心的距离

5,找出位于指定范围内的元素:GEORADIUSBYMEMBER key member radius 单位  #中心点是有给定的位置决定的

6,返回一个或多个位置元素的geohash:geohash key menmber1 menber2

将二维经纬度转换为以为的11位字符串,如果俩字符串越接近,距离越近

Geo底层实现原理: Zset,我们可以使用Zset命令来操作Geo

比如

查看:zrange china:city 0 -1

 移除:zrem china:city zigong

hyperloglog

所有命令以p开头的

添加元素 pfadd key 元素1 元素2 。。

统计元素(不重复):pfcount key

求并集:pfmerge 生成的新集合3 原集合1 原集合2

bitmap

添加打卡记录:setbit key offset value

查看打卡记录:getbit key offset

统计打卡天数:bitcount key

事务

redis事务本质:一组命令的集合,一个事务中的所有命令都会被序列化,在事务执行过程中,会按照顺序执行

一次性,顺序性,排他性,执行一些命令

-------------队列  set  set  set------------------

redis事务没有隔离级别的概念!

所有命令在事务中,并没有被执行,只有发起了执行命令(exec)才会被一次性执行

redis单条命令是保持原子性的,但事务不保证原子性

redis事务流程:

  • 开启事务(multi)
  • 命令入队(添加各种命令...)
  • 执行事务(exec)

正常事务

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 k1
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> exec    #执行事务
1) OK                   #此处可见,此时才把上面的所有命令挨个执行
2) OK
3) "v1"
4) OK

放弃事务 discard     (放弃了,事务中的所有命令都不会被执行)

127.0.0.1:6379> multi   #开启事务
OK
127.0.0.1:6379> set k4 v4   #设置K4
QUEUED
127.0.0.1:6379> discard   #放弃事务
OK
127.0.0.1:6379> get k4   #则k4就被放弃了,未执行(事务中的所有命令都不会被执行)
(nil)

编译型异常(代码有错,命令有错):这种情况会现场报错,如果还是执行,则事务中所有命令都不会被执行

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> getset k3   #此处明显命令错误,且现场报错
(error) ERR wrong number of arguments for 'getset' command
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> exec   #执行事务,就全部报错,所有命令都不会被执行
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get key3   
(nil)
127.0.0.1:6379> get key1
(nil)

运行时异常(类似java的1/0):如果事务队列中存在语法性错误,那么执行命令的时候,只错误的命令会抛出异常,其他命令正常执行(也就是上面说的单条语句保持原子性,事务不保证原子性)

127.0.0.1:6379> set key1 "v1"   #先设置一个string类型的key1
OK
127.0.0.1:6379> multi   #开启事务
OK
127.0.0.1:6379> incr key1   #对key1进行自增,命令没错,但key1是字符串,无法自增
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> mget k2 k3
QUEUED
127.0.0.1:6379> exec  #执行事务
1) (error) ERR value is not an integer or out of range   #发现只这条命令没执行成功,
2) OK                                                    #其他的都执行了
3) OK
4) 1) "v2"2) "v3"

监控 watch   (面试常问)使用watch,可以当作redis的乐观锁操作

悲观锁:

        悲观,认为什么时候都会出问题,无论做啥都加锁

乐观锁:

        乐观,认为什么时候都不会出问题,所以不会上锁,更新数据的时候判断,获取version,跟新的时候比较version

正常监控 watch

127.0.0.1:6379> set money 100   #钱包https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fimg-home.csdnimg.cn%2Fimages%2F20230724024159.png%3Forigin_url%3D200%26pos_id%3DCQVHWhGt&pos_id=CQVHWhGt
OK
127.0.0.1:6379> set out 0      #支出0元
OK
127.0.0.1:6379> watch money     #监视钱包
OK
127.0.0.1:6379> multi    #开启事务
OK 
127.0.0.1:6379> DECRBY money 20
QUEUED
127.0.0.1:6379> INCRBY out 20
QUEUED
127.0.0.1:6379> exec   #数据期没有发生变动,这时候就正常执行成功
1) (integer) 80
2) (integer) 20

多线程修改值,watch的操作

#第一个redis执行如下 127.0.0.1:6379> set money 100
OK
127.0.0.1:6379> set out 0
OK
127.0.0.1:6379> watch money   #此时就相当于乐观锁的查询version
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby money 20
QUEUED
127.0.0.1:6379> incrby out 20
QUEUED#开启第二个redis执行如下127.0.0.1:6379> get money
"100"
127.0.0.1:6379> set money 1000
OK
127.0.0.1:6379> #返回第一个redis执行如下127.0.0.1:6379> exec    #执行失败, 此时就相当于取验证version,则执行失败
(nil)

 执行失败之后,解锁 unwatch

127.0.0.1:6379> exec   #由于中途修改了数据,导致修改失败
(nil) 
127.0.0.1:6379> unwatch   #需要先解除锁
OK
127.0.0.1:6379> get money
"1000"
127.0.0.1:6379> get out
"0"
127.0.0.1:6379> watch money   #获取新的值,再重新加锁(select version)
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby money https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=200&pos_id=CQVHWhGt&pos_id=CQVHWhGt
QUEUED
127.0.0.1:6379> incrby out https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=200&pos_id=CQVHWhGt&pos_id=CQVHWhGt
QUEUED
127.0.0.1:6379> exec   #此时中途未改变数值,对比version未变,则可执行成功
1) (integer) 800
2) (integer) https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=200&pos_id=CQVHWhGt&pos_id=CQVHWhGt
127.0.0.1:6379> 

Jedis

要使用java来操作redis

常用API测试

1,项目创建

新建一个空项目新建modules

 注意统一   

2,导入依赖 

https://mvnrepository.com/

<dependencies><!-- https://mvnrepository.com/artifact/redis.clients/jedis --><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>5.2.0</version></dependency><!-- https://mvnrepository.com/artifact/com.alibaba.fastjson2/fastjson2 --><dependency><groupId>com.alibaba.fastjson2</groupId><artifactId>fastjson2</artifactId><version>2.0.53</version></dependency></dependencies>

3,测试连接

//本地连接   可直接连接测试
public static void main(String[] args) {Jedis jedis=new Jedis("127.0.0.1",6379);System.out.println(jedis.ping());}//远程连接   需修改配置
public static void main(String[] args) {Jedis jedis=new Jedis("远程服务器地址",6379);jedis.auth("设置的密码");System.out.println(jedis.ping());}

修改远程配置xhmcopyredisconfig/redis.config

1,注销掉bind 127.0.0.1

2,添加密码访问

3,注意端口哦

  • 远程服务器端口:
firewall-cmd --list-ports :查看防火墙端口
firewall-cmd --zone=public --add-port=6379/tcp --permanent  开放防火墙端口
systemctl restart firewalld.service 重启防火墙
  • 阿里云防火墙安全组端口

4,命令测试(跟服务器上命令一样 string set list hash zset)

就跟上面再服务器上使用的命令一样:比如

包括特殊类型的也有 

事务测试 

public static void main(String[] args) {Jedis jedis = new Jedis(ConstantUtil.ID_ADDRESS, ConstantUtil.PORT);jedis.auth(ConstantUtil.PASSWORD);jedis.flushDB();JSONObject jsonObject = new JSONObject();jsonObject.put("hello", "world");jsonObject.put("happy", "new year");//开启事务Transaction multi = jedis.multi();String result = jsonObject.toString();try {multi.set("user1", result);multi.set("user2", result);/*** 没有这行代码时正常执行输出*{"happy":"new year","hello":"world"}* {"happy":"new year","hello":"world"}* 有这行代码之后,事务放弃执行输出* null* null*/int i = 1 / 0;multi.exec();//执行事务} catch (Exception e) {multi.discard();//如果出错就放弃事务e.fillInStackTrace();} finally {System.out.println(jedis.get("user1"));System.out.println(jedis.get("user2"));jedis.close();//关闭连接}}

Springboot整合

Redis.conf详解

Redis持久化

Redis发布订阅

Redis主从复制

Redis缓存穿透和雪崩

相关文章:

Redis学习笔记

目录 Nosql概述 为什么用Nosql 什么是Nosql Nosql四大分类 Redis入门 概述 Windows安装 Linux安装 测试性能 基础知识 五大数据类型 Redis-Key String(字符串) List&#xff08;列表&#xff09; Set(集合) Hash&#xff08;哈希&#xff09; Zset&#xff08;有…...

mysql递归查询语法WITH RECURSIVE

WITH RECURSIVE 是 SQL 中用于执行递归查询的语法&#xff0c;特别适合于处理层级结构或递归数据&#xff08;如树形结构、图结构&#xff09;。递归查询可以反复引用自己来查询多层次的数据&#xff0c;而无需写多个嵌套查询。 基本语法结构&#xff1a; WITH RECURSIVE CTE…...

Go语言之十条命令(The Ten Commands of Go Language)

Go语言之十条命令 Go语言简介 Go语言&#xff08;又称Golang&#xff09;‌是由Google开发的一种开源编程语言&#xff0c;首次公开发布于2009年。Go语言旨在提供简洁、高效、可靠的软件开发解决方案&#xff0c;特别强调并发编程和系统编程‌。 Go语言的基本特征 ‌静态强类…...

Visual Studio 2022 C++ gRPC 环境搭建

文章目录 1、gRPC 安装2、创建项目2.1、创建 “空的解决方案”2.2、新建 gRPCServer 和 gRPCClient 项目2.3、创建 proto 文件 2、为 gRPC 服务端和客服端项目配置 protobuf 编译2.1、protobuf 配置2.2、gRPCServer 项目配置2.3、gRPCClient 项目配置 3、测试3.1、启动服务端程…...

2024AAAI SCTNet论文阅读笔记

文章目录 SCTNet: Single-Branch CNN with Transformer Semantic Information for Real-Time Segmentation摘要背景创新点方法Conv-Former Block卷积注意力机制前馈网络FFN 语义信息对齐模块主干特征对齐共享解码头对齐 总体架构backbone解码器头 对齐损失 实验SOTA效果对比Cit…...

【Java从入门到放弃 之 final 关键字】

final 关键字 final 关键字final 字段final 函数列表中的参数final 方法final 类 final 关键字 Java中里面有final这个关键字&#xff0c;这个关键字总体上是用来表达” 不能被改变“ 这个意思的。我们使用这个关键字表达不能被改变&#xff0c;有两种使用场景&#xff0c;有三…...

【U8+】用友U8软件中,出入库流水输出excel的时候提示报表输出引擎错误。

【问题现象】 通过天联高级版客户端登录拥有U8后&#xff0c; 将出入库流水输出excel的时候&#xff0c;提示报表输出引擎错误。 进行报表输出时出现错误&#xff0c;错误信息&#xff1a;找不到“fd6eea8b-fb40-4ce4-8ab4-cddbd9462981.htm”。 如果您正试图从最近使用的文件列…...

文本区域提取和分析——Python版本

目录 1. 图像预处理 2. 文本区域提取 3. 文本行分割 4. 文本区域分析 5. 应用举例 总结 文本区域提取和分析是计算机视觉中的重要任务&#xff0c;尤其在光学字符识别&#xff08;OCR&#xff09;系统、文档分析、自动化数据录入等应用中有广泛的应用。其目标是从图像中提…...

数据库介绍(不同数据库比较)

文章目录 **一、关系型数据库&#xff08;RDBMS&#xff09;****1. MySQL****优点**&#xff1a;**缺点**&#xff1a;**适用场景**&#xff1a; **2. PostgreSQL****优点**&#xff1a;**缺点**&#xff1a;**适用场景**&#xff1a; **3. Oracle Database****优点**&#xff…...

注意力的简单理解,有哪些注意力(Attention)

注意力(Attention) 目录 注意力(Attention)掩码注意力机制自注意力、交叉注意力、掩码注意力的不同点适应场景及举例多头注意分层注意力(Hierarchical Attention)协同注意力(Co - Attention)自注意力(Self - Attention) 简单理解:自注意力就像是一个句子(或序列)内…...

基于Python的投资组合收益率与波动率的数据分析

基于Python的投资组合收益率与波动率的数据分析 摘要&#xff1a;該文通过研究马科维茨的投资组合模型&#xff0c;并将投资组合模型应用到包含6只金融股票的金融行业基金中。首先通过开源的财经接口Tushare获取股票原始数据&#xff0c;接着利用数据分析的黄金组合库&#xf…...

《Opencv》图像的旋转

一、使用numpy库实现 np.rot90(img,-1) 后面的参数为-1时事顺时针旋转&#xff0c;为1时是逆时针旋转。 import cv2 import numpy as np img cv2.imread(./images/kele.png) """方法一""" # 顺时针90度 rot_1 np.rot90(img,-1) # 逆时针90度…...

Python 22:注释

1. 定义&#xff1a; 用熟悉的语言对代码进行解释说明。注释不会被执行。 2. 注释分类 单行注释&#xff1a;只能对一行代码进行注释。放在要注释的代码后面&#xff0c;用#进行分隔&#xff0c;中间至少空2个空格&#xff0c;保证代码规范。 print("hello world10"…...

python:利用神经网络技术确定大量离散点中纵坐标可信度的最高集中区间

当我们有许多离散点并想要确定纵坐标在某个区间内的可信度时&#xff0c;我们可以使用神经网络模型来解决这个问题。下面是一个使用Python编写的示例代码&#xff0c;展示了如何使用神经网络来确定大量离散点中纵坐标可信度的最高集中区间。 import numpy as np from sklearn.…...

计算机软件保护条例

(2001年12月20日中华人民共和国国务院令第339号公布 根据2011年1月8日《国务院关于废止和修改部分行政法规的决定》第一次修订 根据2013年1月30日《国务院关于修改〈计算机软件保护条例〉的决定》第二次修订) 第一章 总则 第一条 为了保护计算机软件著作权人的权益&#…...

CM3/4启动流程

CM3/4启动流程 1. 启动模式2. 启动流程 1. 启动模式 复位方式有三种&#xff1a;上电复位&#xff0c;硬件复位和软件复位。 当产生复位&#xff0c;并且离开复位状态后&#xff0c;CM3/4 内核做的第一件事就是读取下列两个 32 位整数的值&#xff1a; 从地址 0x0000 0000 处取…...

gaussdb中怎么查询一个表有多少GB

在 GaussDB 中&#xff0c;你可以通过多种方法查询一个表的大小&#xff0c;包括使用系统视图和内置函数。以下是几种常见的方法&#xff1a; 1. 使用 pg_total_relation_size 函数 pg_total_relation_size 函数返回一个表及其所有索引和 TOAST 数据的总大小。 示例查询 SE…...

2025-01-06 Unity 使用 Tip2 —— Windows、Android、WebGL 打包记录

文章目录 1 Windows2 Android2.1 横版 / 竖版游戏2.2 API 最低版本2.3 目标帧率2.3.1 targetFrameRate2.3.2 vSyncCount2.3.3 Unity 默认设置以及推荐设置2.3.4 Unity 帧率托管 3 WebGL3.1 平台限制3.2 打包报错记录 13.3 打包报错记录 2 ​ 最近尝试将写的小游戏打包&#xff…...

OP-TEE环境飞腾密码引擎编程指南

【写在前面】 飞腾开发者平台是基于飞腾自身强大的技术基础和开放能力&#xff0c;聚合行业内优秀资源而打造的。该平台覆盖了操作系统、算法、数据库、安全、平台工具、虚拟化、存储、网络、固件等多个前沿技术领域&#xff0c;包含了应用使能套件、软件仓库、软件支持、软件适…...

解密Navicat密码(Java)

最近从Navicat换到了DBeaver&#xff0c;导出配置文件发现配置文件里的密码都是加密的&#xff0c;看网上的都是给的PHP代码&#xff0c;因为环境问题&#xff0c;就算是在线上运行的PHP代码也会报错&#xff0c;所以就把这段代码改成Java了。 package com.unicdata.system.con…...

apex安装

安装过程复杂曲折&#xff0c;网上说的很多办法&#xff0c;貌似成功了&#xff0c;实际还是没起作用。 先说成功过程&#xff0c;执行下面命令&#xff0c;安装成功&#xff08;当然&#xff0c;前提是你要先配置好编译环境&#xff09;&#xff1a; &#xff08;我的环境&a…...

常见的开源网络操作系统

常见的开源网络操作系统有很多,它们通常用于路由器、交换机、网络设备和服务器等场景,具有灵活、可定制、易于扩展的特点。以下是一些常见的开源网络操作系统: OpenWRT 用途:主要用于路由器、无线接入点和网络设备。提供了广泛的定制选项和高级功能,如防火墙配置、VPN 支持…...

2024年6月英语六级CET6听力原文与解析

目录 0 序言 1.Long Conversation(长对话) 1.1 Blender 搅拌机 1.2 村庄的改造变化 2.Passage 2.1 micro robots 微型机器人 2.2 elite sleeper 睡眠精英 3.Lecture 3.1 对自身观念变化的低察觉度及相关研究发现 3.2 美国母亲群体数量变化及母亲节消费趋势分析 3.3 …...

力扣2-回文数

一.题目 给你一个整数 x &#xff0c;如果 x 是一个回文整数&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 回文数是指正序&#xff08;从左向右&#xff09;和倒序&#xff08;从右向左&#xff09;读都是一样的整数。 例如&#xff0c;121 是回文&…...

基于springboot的网上商城购物系统

作者&#xff1a;学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等 文末获取“源码数据库万字文档PPT”&#xff0c;支持远程部署调试、运行安装。 目录 项目包含&#xff1a; 开发说明&#xff1a; 系统功能&#xff1a; 项目截图…...

业务日志设计

当一个项目足够大的时候&#xff0c;我们需要将统计系统完全独立出去&#xff0c;那么就无法避免数据采集的问题&#xff0c;我们可以在业务触发处增加log日志来记录当前变化的原始数据&#xff0c;提供统计系统进行采集 设计一个统计系统的日志记录机制时&#xff0c;主要需要…...

梯度下降方法

2.5 梯度下降方法介绍 学习目标 掌握梯度下降法的推导过程知道全梯度下降算法的原理知道随机梯度下降算法的原理知道随机平均梯度下降算法的原理知道小批量梯度下降算法的原理 上一节中给大家介绍了最基本的梯度下降法实现流程&#xff0c;本节我们将进一步介绍梯度下降法的详细…...

javascript

引入方式 JavaScript 程序不能独立运行&#xff0c;它需要被嵌入 HTML 中&#xff0c;然后浏览器才能执行 JavaScript 代码。通过 script 标签将 JavaScript 代码引入到 HTML 中&#xff0c;有两种方式&#xff1a; 内部方式 通过 script 标签包裹 JavaScript 代码 <!DO…...

大语言模型训练所需的最低显存,联邦大语言模型训练的传输优化技术

联邦大语言模型训练的传输优化技术 目录 联邦大语言模型训练的传输优化技术大语言模型训练所需的最低显存大语言模型训练所需的最低显存 基于模型微调、压缩和分布式并行处理的方法,介绍了相关开源模型及技术应用 核心创新点 多维度优化策略:综合运用基于模型微调、模型压缩和…...

二叉树的二叉链表和三叉链表

在二叉树的数据结构中&#xff0c;通常有两种链表存储方式&#xff1a;二叉链表和三叉链表。这里&#xff0c;我们先澄清一下概念&#xff0c;通常我们讨论的是二叉链表&#xff0c;它用于存储二叉树的节点。而“三叉链表”这个术语在二叉树的上下文中不常见&#xff0c;可能是…...

api开发如何在代码中使用京东商品详情接口的参数?

选择编程语言和相关工具 以 Python 为例&#xff0c;你可以使用requests库来发送 HTTP 请求获取接口数据。如果是 Java&#xff0c;可以使用OkHttp等库。 Python 示例 假设你已经安装了requests库&#xff0c;以下是一个简单的代码示例来获取和使用京东商品详情接口参数&#…...

Quartz如何实现分布式调度

系列文章目录 任务调度管理——Quartz入门 Quartz如何实现分布式控制 系列文章目录一、持久化二、分布式调度1. 表信息2. 调度器的竞争3. 触发器的分配 三、 总结 我们都说Quartz是个分布式调度框架&#xff0c;那么在分布式环境上&#xff0c;如何使得各个服务器上的定时任务…...

JUC--线程池

线程池 七、线程池7.1线程池的概述7.2线程池的构建与参数ThreadPoolExecutor 的构造方法核心参数线程池的工作原理 Executors构造方法newFixedThreadPoolnewCachedThreadPoolnewSingleThreadExecutornewScheduledThreadPool(int corePoolSize) 为什么不推荐使用内置线程池&…...

以柔资讯-D-Security终端文件保护系统 logFileName 任意文件读取漏洞复现

0x01 产品简介 D-Security终端文件保护系统是一套专注于企业文件管理效率与安全的解决方案,统对文件进行全文加密,而非仅在文件表头或特定部分进行加密,从而大大提高了文件的安全性,降低了被破解的风险。D-Security终端文件保护系统是被政府和国安局等情报单位唯一认定的安…...

【JavaScript】Set,Map,Weakmap

以下来源&#xff1a;九剑科技。 weakmap WeakMap是 ES6 中新增的一种集合类型&#xff0c;叫做“弱映射”&#xff0c;由于他的键引用的对象是弱引用&#xff0c;键所指向的对象可以被垃圾回收&#xff0c;可以防止内存泄露。 map ①Map是键值对的集合&#xff0c;键值不限…...

idea小操作

idea 所边定位到你目前阅读的代码 AltF1 或者 选择定位图标...

[tesseract]Deserialize header failed: FIRC.lstmf

tesseract5.0训练时候会提示 [INFO]cd /d D:\program\tesseract-ocr-lstm-train\data [INFO]D:\program\tesseract-ocr-lstm-train\Tesseract-OCR\tesseract.exe xiangjiao.tif xiangjiao -l eng --psm 7 lstm.train [INFO]Page 1 [INFO]Page 2 [INFO]Deserialize header fail…...

深度学习知识点:RNN

文章目录 1.简单介绍2.网络结构3.应对梯度消失 1.简单介绍 循环神经网络&#xff08;RNN&#xff0c;Recurrent Neural Network&#xff09;是一类用于处理序列数据的神经网络。与传统网络相比&#xff0c;变化不是特别大&#xff0c;不如CNN的变化那么大。 为什么要有循环神经…...

【数据可视化-11】全国大学数据可视化分析

&#x1f9d1; 博主简介&#xff1a;曾任某智慧城市类企业算法总监&#xff0c;目前在美国市场的物流公司从事高级算法工程师一职&#xff0c;深耕人工智能领域&#xff0c;精通python数据挖掘、可视化、机器学习等&#xff0c;发表过AI相关的专利并多次在AI类比赛中获奖。CSDN…...

CSS:背景样式、盒子模型与文本样式

背景样式 背景样式用于设置网页元素的背景&#xff0c;包括颜色、图片等。 背景颜色 使用 background-color 属性设置背景颜色&#xff0c;支持多种格式&#xff08;颜色英文、十六进制、RGB等&#xff09;。 div {background-color: lightblue; }格式示例十六进制#ff5733R…...

学英语学压测:02jmeter组件-测试计划和线程组ramp-up参数的作用

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#xff1a;先看关键单词&#xff0c;再看英文&#xff0c;最后看中文总结&#xff0c;再回头看一遍英文原文&#xff0c;效果更佳&#xff01;&#xff01; 关键词 Functional Testing功能测试[ˈfʌŋkʃənəl ˈtɛstɪŋ]Sample样…...

环动科技平均售价波动下滑:大客户依赖明显,应收账款周转率骤降

《港湾商业观察》施子夫 2024年12月18日&#xff0c;浙江环动机器人关节科技股份有限公司&#xff08;以下简称&#xff0c;环动科技&#xff09;的上市审核状态变更为“已问询”&#xff0c;公司在11月25日科创板IPO获上交所受理&#xff0c;独家保荐机构为广发证券。 此次环…...

数据结构:LinkedList与链表—无头双向链表(二)

目录 一、什么是LinkedList&#xff1f; 二、LinkedList的模拟实现 1、display()方法 2、addFirst(int data)方法 3、addLast(int data)方法 4、addIndex(int index,int data)方法 5、contains(int key)方法 6、remove(int key)方法 7、removeAllKey(int key)方法 8、…...

『SQLite』解释执行(Explain)

摘要&#xff1a;本节主要讲解SQL的解释执行&#xff1a;Explain。 在 sqlite 语句之前&#xff0c;可以使用 “EXPLAIN” 关键字或 “EXPLAIN QUERY PLAN” 短语&#xff0c;用于描述表查询的细节。 基本语法 EXPLAIN 语法&#xff1a; EXPLAIN [SQLite Query]EXPLAIN QUER…...

计算机网络之---物理层的基本概念

物理层简介 物理层&#xff08;Physical Layer&#xff09; 是 OSI&#xff08;开放系统互联&#xff09;模型 中的第 1 层&#xff0c;它主要负责数据在物理媒介上的传输&#xff0c;确保原始比特&#xff08;0 和 1&#xff09;的传输不受干扰地从一个设备传送到另一个设备。…...

Elasticsearch:优化的标量量化 - 更好的二进制量化

作者&#xff1a;来自 Elastic Benjamin Trent 在这里&#xff0c;我们解释了 Elasticsearch 中的优化标量量化以及如何使用它来改进更好的二进制量化 (Better Binary Quantization - BBQ)。 我们的全新改进版二进制量化 (Better Binary Quantization - BBQ) 索引现在变得更强大…...

KBQA前沿技术

文章目录 KBQA面临的挑战基于模板的方法基于语义解析的方法基于深度学习的传统问答基于深度学习的端到端问答模型KBQA面临的挑战 目前还存在两个很大的困难阻碍着KBQA系统被广泛应用。一个困难是现有的自然语言理解技术在处理自然语言的歧义性和复杂性方面还显得比较薄弱。例如…...

patchwork++地面分割学习笔记

参考资料&#xff1a;古月居 - ROS机器人知识分享社区 https://zhuanlan.zhihu.com/p/644297447 patchwork算法一共包含四部分内容&#xff1a;提出了以下四个部分&#xff1a;RNR、RVPF、A-GLE 和 TGR。 1&#xff09;基于 3D LiDAR 反射模型的反射噪声消除 (RNR)&#xff…...

OSPF浅析

一、预习&#xff1a; 1、介绍&#xff1a; 是一种基于接口的典型的链路状态路由协议&#xff0c;协议号89&#xff0c;把大型网络分隔为多个较小、可管理的单元&#xff1a;Area&#xff0c;管理距离110&#xff1b;OSPF基于IP&#xff0c;使用了LSAck包来保证包数据的可靠性&…...

批量写入数据到数据库,卡顿怎么解决

在批量写入数据到数据库时,遇到卡顿或性能瓶颈是比较常见的问题。以下是一些可能的解决方案和优化策略,帮助你提高批量写入的性能: ### 1. **批量大小优化** - **调整批量大小**:尝试调整批量写入的数据量,找到一个平衡点。过大或过小的批量大小都可能影响性能。通常,批…...