redis——入门操作(一)

Redis

远程字典服务,redis,C语言编写,基于内存的可持久化kv数据库,支持多种语言api

作用:内存中的数据是断电即失的,还是要考虑持久化的(RDB、AOF),redis可用于高速缓存,也可以当作简单消息队列,也可做一些地图信息分析,计时器、计数器

特性:多样的数据类型、集群、持久化、事务

安装(linux)

# 官方下载安装包
https://redis.io/download

# 上次linux服务器解压
#进入redis目录
yum install gcc-c++
make
make install

# /usr/local/bin目录下默认会按安装一些redis的命令

# 当前目录新建文件夹my_redis_conf,把redis.cof拷贝到新建文件夹内

# 修改配置
vim redis.conf
修改 daemonize 为 yes(后台启动)

# 启动redis
redis-server /usr/local/soft/redis-6.2.6/my_redis_cofig/redis.conf

# 测试连接
redis-cli -p 6379
输入ping
出现pong则没有问题

# 关闭redis,在redis-cli输入如下命令
127.0.0.1:6379> SHUTDOWN
not connected> exit

性能测试

redis-benchmark,默认该命令在/usr/local/bin下

简单测试

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-48Kb4HSf-1638364259735)(F:/ZNV/%E7%AC%94%E8%AE%B0%E5%9B%BE%E7%89%87/redis/image-20211127205810456.png)]

# 测试100个并发连接 100000个请求
redis-benchmark -h localhost -p 6379 -c 100 -n 100000

# 测试完后如何分析输出的数据?
====== SET ======
  100000 requests completed in 1.28 seconds
  100 parallel clients
  3 bytes payload
  keep alive: 1
  host configuration "save": 3600 1 300 100 60 10000
  host configuration "appendonly": no
  multi-thread: no

Latency by percentile distribution:
0.000% <= 0.183 milliseconds (cumulative count 1)
......
......
Summary:
  throughput summary: 78003.12 requests per second
  latency summary (msec):
          avg       min       p50       p95       p99       max
        0.653     0.176     0.631     0.775     0.975     4.263

这是其中一部分的输出数据,可以看到10万请求花费了1.28s,100个并发的客户端,每次写入了三个字节,只有一台服务器来处理,每秒处理78003.12请求

基础知识

#redis的默认数据库有16个,可以进入redis.conf,可发下如下命令,默认的使用的第0个
databases 16

# 可在redis-cli进行切换。比如切换到第三个,不同的数据库数据是不通的
127.0.0.1:6379> select 3
OK
127.0.0.1:6379[3]>

# 查看数据库所有的key
127.0.0.1:6379[3]> keys *

# 清除当前数据库数据
127.0.0.1:6379[3]> FLUSHDB

# 清除所有数据库数据
127.0.0.1:6379> FLUSHALL

注意,redis是单线程的,redis是基于内存,cpu并不是一个瓶颈,redis的瓶颈是服务器的内存和网络带宽,所以在能使用单线程的情况不需要使用多线程,redis是C语言写的,而是redis官网提供数据为100000+的QPS(每秒查询率),性能不比Memecache差,该链接讲解了大致的区别redis和memcached的区别和使用场景_北北的博客-CSDN博客_redis和memcached的区别和使用场景

那问题来了,为什么单线程还这么快?

首先要明确一点,多线程不一定比单线程快,redis是将所有的数据放到内存的,所以使用单线程操作的效率是最高的,多线程会导致cpu上下文切换的操作耗时间,而对于依赖内存的系统,没有上下文切换效率就是最高的

五大数据类型

string、List、Set、Hash、Zset

redis中文官方命令大全链接Redis命令中心(Redis commands) – Redis中国用户组(CRUG)

Redis-key相关

127.0.0.1:6379> KEYS *
(empty array)
127.0.0.1:6379> set name xiaoyoupei # 设一个key为name,value为xiaoyoupei
OK
127.0.0.1:6379> KEYS * # 查看所有的key
1) "name"
127.0.0.1:6379> set age 21
OK
127.0.0.1:6379> KEYS *
1) "name"
2) "age"
127.0.0.1:6379> EXISTS name # 判断key是否存在,返回1表示存在,0表示不存在
(integer) 1
127.0.0.1:6379> EXISTS name1
(integer) 0
127.0.0.1:6379> EXISTS age
(integer) 1
127.0.0.1:6379> MOVE name 1 # 移除某个key
(integer) 1
127.0.0.1:6379> KEYS *
1) "age"
127.0.0.1:6379> set name xiaoyoupei
OK
127.0.0.1:6379> KEYS *
1) "name"
2) "age"
127.0.0.1:6379> get name # 通过某个key获取key所对应的value
"xiaoyoupei"
127.0.0.1:6379> EXPIRE name 10 # 设置某个key的过期时间(自动清除时间),单位是秒
(integer) 1
127.0.0.1:6379> KEYS *
1) "name"
2) "age"
127.0.0.1:6379> ttl name # 查看某个key剩余的过期时间,返回为-2表示已经清除,-1表示没有设置过期时间
(integer) -2
127.0.0.1:6379> set name xiaoyoupei
OK
127.0.0.1:6379> TYPE name # 查看某个key属于什么类型
string
127.0.0.1:6379> TYPE age
string

String

##### 追加、长度 #####
127.0.0.1:6379> set key1 v1
OK
127.0.0.1:6379> get key1
"v1"
127.0.0.1:6379> KEYS * # name,age是上文遗留
1) "name"
2) "age"
3) "key1"
127.0.0.1:6379> EXISTS key1
(integer) 1
127.0.0.1:6379> APPEND key1 HelloWorld # 追加,如果key不存在就相当于set key
(integer) 12
127.0.0.1:6379> get key1
"v1HelloWorld"
127.0.0.1:6379> STRLEN key1 # 获取字符串的长度
(integer) 12
127.0.0.1:6379> APPEND key1 ',youpei'
(integer) 19
127.0.0.1:6379> get key1
"v1HelloWorld,youpei"
127.0.0.1:6379> STRLEN key1
(integer) 19

##### 自增、自减、步长 #####
127.0.0.1:6379> set views 0 
OK
127.0.0.1:6379> get views
"0"
127.0.0.1:6379> INCR views # 加1
(integer) 1
127.0.0.1:6379> get views
"1"
127.0.0.1:6379> INCR views
(integer) 2
127.0.0.1:6379> get views
"2"
127.0.0.1:6379> DECR views # 减1
(integer) 1
127.0.0.1:6379> get views
"1"
127.0.0.1:6379> DECR views
(integer) 0
127.0.0.1:6379> get views
"0"
127.0.0.1:6379> DECR views
(integer) -1
127.0.0.1:6379> get views
"-1"
127.0.0.1:6379> INCRBY views 10 # 增加的步长为10
(integer) 9
127.0.0.1:6379> DECRBY views 5 # 减少的步长为5
(integer) 4

##### 通过下标截取、替换 #####
127.0.0.1:6379> set key1 hello,xiaoyoupei
OK
127.0.0.1:6379> get key1
"hello,xiaoyoupei"
127.0.0.1:6379> GETRANGE key1 0 4 # 通过下标截取(左右都是闭区间)
"hello"
127.0.0.1:6379> GETRANGE key1 0 -1  # 获取全部字符串,同get
"hello,xiaoyoupei"
127.0.0.1:6379> set key2 abcdefg
OK
127.0.0.1:6379> get key2
"abcdefg"
127.0.0.1:6379> SETRANGE key2 1 x # 替换指定位置的字符串
(integer) 7
127.0.0.1:6379> get key2
"axcdefg"
127.0.0.1:6379> SETRANGE key2 2 ii
(integer) 7
127.0.0.1:6379> get key2
"axiiefg"

#####  #####
# setex (set with expire)  # 设置过期时间
# setnx (set if not exist) # 不存在就修改,存在就不改变
127.0.0.1:6379> SETEX key3 30 hello # 设置key3的值并且30秒后过期
OK
127.0.0.1:6379> ttl key3
(integer) 25
127.0.0.1:6379> get key3
"hello"
127.0.0.1:6379> ttl key3
(integer) 8
127.0.0.1:6379> SETNX mykey redis # 如果mykey不存在,创建mykey,存在就创建失败
(integer) 1
127.0.0.1:6379> get mykey
"redis"
127.0.0.1:6379> keys *
1) "mykey"
2) "key2"
3) "key1"
127.0.0.1:6379> ttl key3
(integer) -2
127.0.0.1:6379> setnx mykey mongodb
(integer) 0
127.0.0.1:6379> get mykey
"redis"

##### 多值操作 #####
127.0.0.1:6379> FLUSHDB
OK
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> MSET k1 v1 k2 v2 k3 v3 # 同时设置多个值
OK
127.0.0.1:6379> keys *
1) "k3"
2) "k2"
3) "k1"
127.0.0.1:6379> MGET k1 k2 k3 # 同时获取多个值
1) "v1"
2) "v2"
3) "v3"
127.0.0.1:6379> MSETNX k1 v1 k4 v4 # 如果不存在就创建,但此操作结束后却发现没有k4,因为此操作是原子性的,要么一起成功,要么都失败
(integer) 0
127.0.0.1:6379> keys *
1) "k3"
2) "k2"
3) "k1"

##### 对象 #####
# set user:1 {name:xiao,age:21}
# user:{id}:{filed},redis支持这样的设计
127.0.0.1:6379> MSET user:1:name xiao user:1:age 21
OK
127.0.0.1:6379> MGET user:1:name user:1:age
1) "xiao"
2) "21"

##### 先get后set #####
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"

List

列表,redis可将list作为栈、队列、阻塞队列,list可重复

链表,在每个节点的前面后面都可以插入,链表左右也可以插入值

##### lpush、rpush #####
127.0.0.1:6379> keys *
(empty array)
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> LRANGE list 0 0
1) "three"
127.0.0.1:6379> RPUSH list right # 将一个值或者多个值,插入列表的尾部(右部)
(integer) 4
127.0.0.1:6379> LRANGE list 0 -1
1) "three"
2) "two"
3) "one"
4) "right"

上述所图所示

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VKhs3YV8-1638364259737)(F:/ZNV/%E7%AC%94%E8%AE%B0%E5%9B%BE%E7%89%87/redis/image-20211128135309198.png)]

##### 移除列表的值lpop、rpop;通过下边获取lindex #####
127.0.0.1:6379> LRANGE list 0 -1
1) "three"
2) "two"
3) "one"
4) "right"
127.0.0.1:6379> LPOP list # 移除头部一个(左部)
1) "three"
127.0.0.1:6379> LRANGE list 0 -1
1) "two"
2) "one"
3) "right"
127.0.0.1:6379> RPOP list # 移除尾部一个(右部)
"right"
127.0.0.1:6379> LRANGE list 0 -1
1) "two"
2) "one"
127.0.0.1:6379> LINDEX list 1 # 获取下标为1的值
"one"
127.0.0.1:6379> LINDEX list 0
"two"


##### LLEN:长度,LREM:移除 #####
127.0.0.1:6379> FLUSHDB
OK
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> LLEN list 
(integer) 3
127.0.0.1:6379> lpush list three # 这就表示列表可重复
(integer) 4
127.0.0.1:6379> LREM list 1 one # 移除指定数目的value,这里是移除1个one
(integer) 1
127.0.0.1:6379> LRANGE list 0 -1
1) "three"
2) "three"
3) "two"
127.0.0.1:6379> LREM list 2 three # 移除指定数目的value,这里是移除2个three
(integer) 2
127.0.0.1:6379> LRANGE list 0 -1
1) "two"


##### LTRIM:截取 #####
127.0.0.1:6379> FLUSHDB
OK
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> RPUSH mylist 'hello'
(integer) 1
127.0.0.1:6379> RPUSH mylist 'hello1'
(integer) 2
127.0.0.1:6379> RPUSH mylist 'hello2'
(integer) 3
127.0.0.1:6379> RPUSH mylist 'hello3'
(integer) 4
127.0.0.1:6379> LRANGE mylist 0 -1
1) "hello"
2) "hello1"
3) "hello2"
4) "hello3"
127.0.0.1:6379> LTRIM mylist 1 2 # 截取保留下标为1、2的值
OK
127.0.0.1:6379> LRANGE mylist 0 -1
1) "hello1"
2) "hello2"


##### RPOPLPUSH:移除列表尾部元素将其添加到另一列表 #####
127.0.0.1:6379> FLUSHDB
OK
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> RPUSH mylist 'hello'
(integer) 1
127.0.0.1:6379> RPUSH mylist 'hello1'
(integer) 2
127.0.0.1:6379> RPUSH mylist 'hello2'
(integer) 3
127.0.0.1:6379> RPOPLPUSH mylist myotherlist
"hello2" # 移除mylist尾部一个元素将其添加到myotherlist,如果myotherlist不存在会自动创建
127.0.0.1:6379> LRANGE mylist 0 -1
1) "hello"
2) "hello1"
127.0.0.1:6379> LRANGE myotherlist 0 -1
1) "hello2"


##### LSET:修改列表指定下标的值 #####
127.0.0.1:6379> FLUSHDB
OK
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> EXISTS list
(integer) 0
127.0.0.1:6379> LSET list 0 item # 不存在列表更新就会报错
(error) ERR no such key
127.0.0.1:6379> lpush list value1
(integer) 1
127.0.0.1:6379> LRANGE list 0 0
1) "value1"
127.0.0.1:6379> LSET list 0 item # 更新列表的下标值
OK
127.0.0.1:6379> LRANGE list 0 0
1) "item"


##### LINSERT:某个具体值的插入列表中 #####
127.0.0.1:6379> FLUSHDB
OK
127.0.0.1:6379> RPUSH list 'hello'
(integer) 1
127.0.0.1:6379> RPUSH list 'world'
(integer) 2
127.0.0.1:6379> LINSERT list before world xiao # 将xiao插入到world之前
(integer) 3
127.0.0.1:6379> LRANGE list 0 -1
1) "hello"
2) "xiao"
3) "world"
127.0.0.1:6379> LINSERT list after world youpei # 将youpei插入到world之后
(integer) 4
127.0.0.1:6379> LRANGE list 0 -1
1) "hello"
2) "xiao"
3) "world"
4) "youpei"

Set

set集合,无序且不能重复

##### SADD:set添加元素,SMEMBERS:查看全部元素,SISMEMBER:判断元素是否存在 #####
127.0.0.1:6379> SADD myset hello # set中添加元素
(integer) 1
127.0.0.1:6379> SADD myset xiaoyoupei
(integer) 1
127.0.0.1:6379> SADD myset liyan
(integer) 1
127.0.0.1:6379> SMEMBERS myset # 查看指定set的所有值
1) "liyan"
2) "xiaoyoupei"
3) "hello"
127.0.0.1:6379> SADD myset liyan # 相同值添加失败
(integer) 0
127.0.0.1:6379> SMEMBERS myset
1) "liyan"
2) "xiaoyoupei"
3) "hello"
127.0.0.1:6379> SISMEMBER myset liyan # 判断某个元素是不是在set中,返回为1是在,返回为0是不在
(integer) 1
127.0.0.1:6379> SISMEMBER myset liyan1
(integer) 0

##### scard:统计个数,srem:移除set中的某个元素 #####
127.0.0.1:6379> SCARD myset # 获取set集合中的个数
(integer) 3
127.0.0.1:6379> SREM myset hello # 移除set中的某个元素
(integer) 1
127.0.0.1:6379> SMEMBERS myset
1) "liyan"
2) "xiaoyoupei"

##### SRANDMEMBER:随机抽选,SPOP:随机移除元素 #####
127.0.0.1:6379> SRANDMEMBER myset # 随机抽选
"xiaoyoupei"
127.0.0.1:6379> SRANDMEMBER myset  
"liyan"
127.0.0.1:6379> SRANDMEMBER myset 2 # 随机抽选指定个数的元素
1) "liyan"
2) "xiaoyoupei"
127.0.0.1:6379> SPOP myset # 随机移除某个元素
"xiaoyoupei"
127.0.0.1:6379> SPOP myset
"liyan"
127.0.0.1:6379> SPOP myset
(nil)
127.0.0.1:6379> SMEMBERS myset
(empty array)

##### smove:将一个集合的某个元素移动到另外一个集合 #####
127.0.0.1:6379> SMOVE myset myset2 xiao
(integer) 1
127.0.0.1:6379> SMEMBERS myset
1) "world"
2) "hello"
127.0.0.1:6379> SMEMBERS myset2
1) "xiao"
2) "set2"

##### 差集,交集,并集 #####
127.0.0.1:6379> sadd key1 a
(integer) 1
127.0.0.1:6379> sadd key1 b
(integer) 1
127.0.0.1:6379> sadd key1 c
(integer) 1
127.0.0.1:6379> sadd key2 c
(integer) 1
127.0.0.1:6379> sadd key2 d
(integer) 1
127.0.0.1:6379> sadd key2 e
(integer) 1
127.0.0.1:6379> SDIFF key1 key2 # 差集
1) "b"
2) "a"
127.0.0.1:6379> SINTER key1 key2 # 交集
1) "c"
127.0.0.1:6379> SUNION key1 key2 # 并集
1) "b"
2) "a"
3) "c"
4) "e"
5) "d"

Hash

map集合,key-Map,可以存储经常变更的数据

127.0.0.1:6379> FLUSHDB
OK
127.0.0.1:6379> HSET myhash field1 xiaoyoupei # set一个具体的kv
(integer) 1
127.0.0.1:6379> HGET myhash field1
"xiaoyoupei"
127.0.0.1:6379> HMSET myhash field1 hello field2 word # set多个kv
OK
127.0.0.1:6379> HMGET myhash field1 field2 # 获取多个字段值
1) "hello"
2) "word"
127.0.0.1:6379> HGETALL myhash # 获取全部的数据
1) "field1"
2) "hello"
3) "field2"
4) "word"
127.0.0.1:6379> HDEL myhash field1 # 删除hash的指定的key字段
(integer) 1
127.0.0.1:6379> HGETALL myhash
1) "field2"
2) "word"
127.0.0.1:6379> HLEN myhash
(integer) 1
127.0.0.1:6379> HMSET myhash field1 hello field2 word
OK
127.0.0.1:6379> HGETALL myhash
1) "field2"
2) "word"
3) "field1"
4) "hello"
127.0.0.1:6379> HLEN myhash # 获取hash表的key值数量
(integer) 2
127.0.0.1:6379> HEXISTS myhash field1 # 判断hash中的指定字段是否存在
(integer) 1
127.0.0.1:6379> HEXISTS myhash field3
(integer) 0
127.0.0.1:6379> HKEYS myhash # 获取hash中所有key
1) "field2"
2) "field1"
127.0.0.1:6379> HVALS myhash # 获取hash中所有value
1) "word"
2) "hello"

##################################
127.0.0.1:6379> HSET hash field1 5
(integer) 1
127.0.0.1:6379> HINCRBY hash field1 1 # 自增1
(integer) 6
127.0.0.1:6379> HGETALL hash
1) "field1"
2) "6"
127.0.0.1:6379> HINCRBY hash field1 -1
(integer) 5
127.0.0.1:6379> HGETALL hash
1) "field1"
2) "5"
127.0.0.1:6379> HSET NX hash field4 hello # 如果不存在可以设置
(integer) 1
127.0.0.1:6379> HSETNX hash field4 world
(integer) 0

Zset

有序集合

127.0.0.1:6379> ZADD myset 1 one 2 two 3 three # 添加多个值
(integer) 3
127.0.0.1:6379> ZRANGE myset 0 -1 # 下标查看集合元素
1) "one"
2) "two"
3) "three"

# 排序实现
127.0.0.1:6379> ZADD salary 10000 xiao
(integer) 1
127.0.0.1:6379> ZADD salary 8000 youpei
(integer) 1
127.0.0.1:6379> ZADD salary 1000 can
(integer) 1
127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf # 显示用户,从小到大
1) "can"
2) "youpei"
3) "xiao"
127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf withscores# 显示全部用户和工资,从小到大
1) "can"
2) "1000"
3) "youpei"
4) "8000"
5) "xiao"
6) "10000"
127.0.0.1:6379> ZREVRANGE salary 0 -1# 从大到小排序
1) "xiao"
2) "youpei"
3) "can"
127.0.0.1:6379> ZRANGEBYSCORE salary -inf 8000 withscores # 显示工资小于8000(闭区间)的用户和工资
1) "can"
2) "1000"
3) "youpei"
4) "8000"


127.0.0.1:6379> zrange salary 0 -1
1) "can"
2) "youpei"
3) "xiao"
127.0.0.1:6379> zrem salary can # 移除某个元素
(integer) 1
127.0.0.1:6379> zrange salary 0 -1
1) "youpei"
2) "xiao"
127.0.0.1:6379> ZCARD salary # 获取有序集合的个数
(integer) 2


127.0.0.1:6379> zadd myset 1 hello
(integer) 1
127.0.0.1:6379> ZADD myset 2 world 3 xiaoyoupei
(integer) 2
127.0.0.1:6379> ZCOUNT myset 1 3 # 统计数量
(integer) 3

三大特殊数据类型

geospatial地理位置

# geoadd添加地理位置,南北极无法添加
# 参数 key 经纬度
127.0.0.1:6379> GEOADD china:city 118.99868 32.69124 tianchang
(integer) 1
127.0.0.1:6379> GEOADD china:city 118.767413 32.041544 nanjing
(integer) 1
127.0.0.1:6379> GEOADD china:city 121.472644 31.231706 shanghai
(integer) 1
127.0.0.1:6379> GEOADD china:city 120.153576 30.287459 hangzhou
(integer) 1
127.0.0.1:6379> GEOADD china:city 116.405285 39.904989 beijing
(integer) 1

#  GEOPOS 获取指定的城市的经度和纬度
127.0.0.1:6379> GEOPOS china:city beijing tianchang
1) 1) "116.40528291463851929"
   2) "39.9049884229125027"
2) 1) "118.99867862462997437"
   2) "32.69124044090514758"

# GEODIST 两地之前的距离,km为单位
127.0.0.1:6379> GEODIST china:city tianchang nanjing km
"75.4588"

# 获取给定位置为圆心,某一半径内的圈内元素
# 110 30是经纬度,1000km是半径
127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km
1) "hangzhou"
2) "nanjing"
3) "tianchang"
# withdist 与给定位置距离 withcoord 列出经纬度 count 限制统计次数
127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km withdist withcoord count 1
1) 1) "nanjing"
   2) "865.7012"
   3) 1) "118.76741319894790649"
      2) "32.04154324806454923"

# 找出指定元素周围的其他元素
127.0.0.1:6379> GEORADIUSBYMEMBER china:city tianchang 1000 km
1) "nanjing"
2) "tianchang"
3) "beijing"
4) "hangzhou"
5) "shanghai"

# 将二维经纬度转换为一维11位的hash值
127.0.0.1:6379> GEOHASH china:city tianchang nanjing
1) "wtu9gzk0q00"
2) "wtsqq8gz920"

GEO底层实际上就是一个Zset,所以可以使用Zset命令操作GEO

# 查看全部元素
127.0.0.1:6379> ZRANGE china:city 0 -1
1) "hangzhou"
2) "shanghai"
3) "nanjing"
4) "tianchang"
5) "beijing"
# 删除某个元素
127.0.0.1:6379> ZREM china:city hangzhou
(integer) 1
127.0.0.1:6379> ZRANGE china:city 0 -1
1) "shanghai"
2) "nanjing"
3) "tianchang"
4) "beijing"

Hyperloglog

引入基数(不重复的元素)

hyperloglog——>基数统计的算法

设想网页的uv量(指的是一个用户访问网站多次还是记为一位,不像pv)

传统的方式,用的是set,统计set中元素数量,但是占用太多内存

我们的存储是为了计数而不是保存某个冗长的用户id

优点

占用内存固定12kb,hyperloglog成为首选,但会有0.81%错误率,但我们可以忽略不计

# 添加元素
127.0.0.1:6379> PFADD mykey xiao you pei
(integer) 1

# 统计个数
127.0.0.1:6379> PFCOUNT mykey
(integer) 3
127.0.0.1:6379> PFADD mykey xiao li yan
(integer) 1

# 不会重复,因此是5个元素
127.0.0.1:6379> PFCOUNT mykey
(integer) 5
127.0.0.1:6379> PFADD mykey2 zhang san xiao
(integer) 1
127.0.0.1:6379> PFCOUNT mykey2
(integer) 3

# mykey mykey2合并为mykey3
127.0.0.1:6379> PFMERGE mykey3 mykey mykey2
OK
127.0.0.1:6379> PFCOUNT mykey3
(integer) 7

Bitmap

引入位存储

# 位存储,形象理解记录周一到周日打卡,1为打卡,0为非
127.0.0.1:6379> SETBIT sign 0 0
(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 0
(integer) 0
127.0.0.1:6379> SETBIT sign 4 1
(integer) 0
127.0.0.1:6379> SETBIT sign 5 1
(integer) 0
127.0.0.1:6379> SETBIT sign 6 1
(integer) 0

# 查看某一天是否打卡
127.0.0.1:6379> GETBIT sign 0
(integer) 0
127.0.0.1:6379> GETBIT sign 3
(integer) 0
127.0.0.1:6379> GETBIT sign 6
(integer) 1

# 统计0-6打卡次数
127.0.0.1:6379> BITCOUNT sign 0 6
(integer) 3

事务操作

redis单条命令保证原子性,但redis中事务没有原子性

redis事务的本质是一组命令的集合,有其一次性、顺序性、排他性,没有隔离级别的概念

所有的命令在事务中,并没有直接被执行,只有发起执行命令才会执行(想到了spark)

redis事务:
开启事务:MULTI
命令入队:具体命令
执行事务:EXEC

# 正常执行 测试案例
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> set k1 v1
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> get k2
QUEUED
127.0.0.1:6379(TX)> set k3 v3
QUEUED
127.0.0.1:6379(TX)> EXEC
1) OK
2) OK
3) "v2"
4) OK

# 放弃事务 测试案例
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> set k1 v1
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> set k4 v4
QUEUED
127.0.0.1:6379(TX)> DISCARD # 取消事务
OK
127.0.0.1:6379> get k4 # 事务中的命令都不会执行
(nil)

编译时异常,命令有错,事务中所有命令都不会执行

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> set k1 v2
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> GETSET k3 # 错误命令
(error) ERR wrong number of arguments for 'getset' command
127.0.0.1:6379(TX)> set k3 v3
QUEUED
127.0.0.1:6379(TX)> set k4 v4
QUEUED
127.0.0.1:6379(TX)> EXEC # 执行事务报错,所有命令都不会执行
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get k4
(nil)

运行时异常,如果事务队列中存在语法性,那么在执行命令的时候,其他命令是可以正常执行的,错误命令抛出异常

127.0.0.1:6379> FLUSHDB
OK
127.0.0.1:6379> set k1 "v1"
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> INCR k1
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> get k2
QUEUED
127.0.0.1:6379(TX)> EXEC
1) (error) ERR value is not an integer or out of range
2) OK
3) "v2"

实现乐观锁

悲观锁:无论做什么都会加锁,影响性能

乐观锁:不会上锁,更新数据的时候判断在此期间是否有人修改过数据,(类似mysql加version)

# 正常执行
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 # 监视money
OK
127.0.0.1:6379> MULTI # 事务正常,没有出错
OK
127.0.0.1:6379(TX)> DECRBY money 20
QUEUED
127.0.0.1:6379(TX)> INCRBY out 20
QUEUED
127.0.0.1:6379(TX)> exec
1) (integer) 80
2) (integer) 20

# 失败执行 监控成功
# 第一个执行事务时候被第二个修改,watch监控直接表示事务必会失败
127.0.0.1:6379> watch money
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> DECRBY money 10  # 第一个
QUEUED
127.0.0.1:6379(TX)> INCRBY out 10
QUEUED
127.0.0.1:6379(TX)> exec # 由于watch监控,所以此处执行必会失败
(nil)

127.0.0.1:6379> get money 
"80"
127.0.0.1:6379> set money 1000 # 第二个
OK

# 那后面事务如何执行,关锁再上锁
127.0.0.1:6379> UNWATCH # 如何发现事务执行失败就先解锁,然后再次上锁
OK
127.0.0.1:6379> watch money
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> DECRBY money 1
QUEUED
127.0.0.1:6379(TX)> INCRBY out 1
QUEUED
127.0.0.1:6379(TX)> exec
1) (integer) 999
2) (integer) 21

Jedis

官方推荐java连接redis的开发工具

导入依赖

    <dependencies>
        <!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>3.3.0</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.62</version>
        </dependency>
    </dependencies>
package com.xyp.redis;

import redis.clients.jedis.Jedis;

/**
 * Description:
 *
 * @Author: xiaoyoupei
 * @CreateTime: 2021/12/1 20:24
 */
public class TestPing {
    public static void main(String[] args) {
        //创建对象
        Jedis jedis = new Jedis("172.16.6.14",6379);

        //测试是否连接成功
        System.out.println(jedis.ping());
    }
}

注意,这里可能有些配置没改,无法连接,尤其是在服务器上,需要修改

1、需要注释
# bind 127.0.0.1 -::1
2、改为no
protected-mode no

常用api

Jedis常用API整理-详细_学无止境-CSDN博客

事务

package com.xyp.redis;

import com.alibaba.fastjson.JSONObject;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;

/**
 * Description:
 *
 * @Author: xiaoyoupei
 * @CreateTime: 2021/12/1 20:55
 */
public class TestTX {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("172.16.6.14", 6379);

        JSONObject jsonObject = new JSONObject();
        jsonObject.put("name", "xiao");
        jsonObject.put("age", 21);

        jedis.flushDB();

        //开启事务
        Transaction multi = jedis.multi();
        String result = jsonObject.toJSONString();
        try {
            multi.set("user1", result);
            // int i = 1 / 0; //打开注释就可事务失败
            //执行事务
            multi.exec();
        } catch (Exception e) {
            //失败抛弃事务
            multi.discard();
            e.printStackTrace();
        } finally {
            System.out.println(jedis.get("user1"));
            jedis.close();
        }


    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

友培

数据皆开源!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值