redis笔记

1、Redis是什么

什么是redis?
redis(Remote Dictionary Server)远程字典服务。
开源的、支持网络、可以基于内存也可以持久化的日志型K-V数据库,支持多语言API。

redis能干什么?

  • 1、内存存储、持久化,内存是断即失、所以持久化很重要(rdb、aof)
  • 2、效率高,可以用于高速缓存
  • 3、发布订阅系统
  • 4、地图信息分析
  • 5、计数器、计时器(浏览量等)
  • 6、等等

特性

  • 1、多样的数据类型
  • 2、持久化
  • 3、集群
  • 4、事务

官网:https://redis.io/
官网中文:http://redis.cn/

mac版本安装:make->make install(好像需要下载c++环境,因为之前下载的xcode)
配置:daemonize yes
连接:redis-server redis.conf
访问:redis-cli -p 6379
查看进程:ps -ef|grep redis
lsof -i:进程id

2、测试性能

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

选项描述
-h主机名(127.0.0.1)
-p端口号(6379)
-s指定服务器socket
-c并发连接数(50)
-n指定请求数(10000)
-d指定set/get数据的大小
-kkeep alive
# 测试:100个并发连接   每个并发10w个请求
redis-benchmark -h localhost -p 6379 -c 100 -n 100000

====== PING_INLINE ======                                                   
  100000 requests completed in 1.33 seconds 
  100 parallel clients    # 100个并发客户端
  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.367 milliseconds (cumulative count 1)
50.000% <= 0.815 milliseconds (cumulative count 51114)
75.000% <= 0.991 milliseconds (cumulative count 75015)
87.500% <= 1.167 milliseconds (cumulative count 87736)
93.750% <= 1.327 milliseconds (cumulative count 93825)
96.875% <= 1.479 milliseconds (cumulative count 96898)
98.438% <= 1.671 milliseconds (cumulative count 98466)
99.219% <= 1.895 milliseconds (cumulative count 99219)
99.609% <= 2.087 milliseconds (cumulative count 99612)
99.805% <= 2.335 milliseconds (cumulative count 99805)
99.902% <= 2.687 milliseconds (cumulative count 99903)
99.951% <= 3.079 milliseconds (cumulative count 99952)
99.976% <= 3.439 milliseconds (cumulative count 99976)
99.988% <= 3.679 milliseconds (cumulative count 99988)
99.994% <= 3.815 milliseconds (cumulative count 99994)
99.997% <= 3.879 milliseconds (cumulative count 99997)
99.998% <= 3.911 milliseconds (cumulative count 99999)
99.999% <= 3.927 milliseconds (cumulative count 100000)
100.000% <= 3.927 milliseconds (cumulative count 100000)

Cumulative distribution of latencies:
0.000% <= 0.103 milliseconds (cumulative count 0)
0.045% <= 0.407 milliseconds (cumulative count 45)
4.079% <= 0.503 milliseconds (cumulative count 4079)
19.337% <= 0.607 milliseconds (cumulative count 19337)
34.188% <= 0.703 milliseconds (cumulative count 34188)
49.863% <= 0.807 milliseconds (cumulative count 49863)
64.306% <= 0.903 milliseconds (cumulative count 64306)
76.563% <= 1.007 milliseconds (cumulative count 76563)
84.077% <= 1.103 milliseconds (cumulative count 84077)
89.621% <= 1.207 milliseconds (cumulative count 89621)
93.126% <= 1.303 milliseconds (cumulative count 93126)
95.672% <= 1.407 milliseconds (cumulative count 95672)
97.208% <= 1.503 milliseconds (cumulative count 97208)
98.088% <= 1.607 milliseconds (cumulative count 98088)
98.598% <= 1.703 milliseconds (cumulative count 98598)
98.952% <= 1.807 milliseconds (cumulative count 98952)
99.237% <= 1.903 milliseconds (cumulative count 99237)
99.478% <= 2.007 milliseconds (cumulative count 99478)
99.633% <= 2.103 milliseconds (cumulative count 99633)
99.952% <= 3.103 milliseconds (cumulative count 99952)
100.000% <= 4.103 milliseconds (cumulative count 100000)

Summary:
  throughput summary: 75131.48 requests per second
  latency summary (msec):
          avg       min       p50       p95       p99       max
        0.855     0.360     0.815     1.383     1.823     3.927
====== PING_MBULK ======                                                   

3、基本知识

默认有16个数据库

databases 16

默认使用第0个,select进行切换数据库

127.0.0.1:6379> select 3 # 切换数据库
OK
127.0.0.1:6379[3]> DBSIZE # 数据库大小
(integer) 0
127.0.0.1:6379> flushall # 清空数据库(全部)非全部用flushdb
OK
127.0.0.1:6379> keys * # 查看所有的keys
(empty array)
127.0.0.1:6379> expire name 10 # 过期时间(单位是s)
(integer) 1
127.0.0.1:6379> ttl name # 剩余时间
(integer) -2
127.0.0.1:6379> move name 1 # 移除当前的key
(integer) 1
127.0.0.1:6379> type name # 查看当前key的类型
string

https://redis.io/commands/

redis是单线程的
redis是基于内存操作的,CPU不是redis性能瓶颈,Redis的瓶颈是根据机器内存和网络宽带,既然可以使用单线程来实现,就使用单线程了。
redis是C语言写的,官方提供的数据为100000+QPS。
redis为什么单线程还这么快?
1、误区1:高性能的服务器一定是多线程的?
2、误区2:多线程(CPU上下文会切换)一定比单线程效率高!
CPU、内存、硬盘的速度要有所了解!
核心:redis是将所有的数据放到内存中的,所以说使用单线程去操作效率是更高的,多线程(CPU上下文会切换,耗时间),对于内存系统来说,如果没有上下文切换,效率就是最高的。
多次读写都是在一个CPU上的,在内存情况下,这个就是最佳方案。

4、五大数据类型

  1. String
  2. List
  3. Set
  4. Hash
  5. ZSet

4-1、String(字符串)

狂神说90%的java程序员只会一个string类型(我不信)

127.0.0.1:6379> set key1 v1 # 设置值
OK
127.0.0.1:6379> get key1 # 获得值
"v1"
127.0.0.1:6379> keys * # 查看全部key值
1) "key1"
127.0.0.1:6379> EXISTS key1 # 判断某个key是否存在
(integer) 1
127.0.0.1:6379> APPEND key1 "hello" # 追加字符串(key不存在,相当于set key)
(integer) 7
127.0.0.1:6379> get key1 
"v1hello"
127.0.0.1:6379> strlen key1 # 字符长度
(integer) 7
####################################################################
# i++操作
127.0.0.1:6379> set view 0
OK
127.0.0.1:6379> get view
"0"
127.0.0.1:6379> incr view # 加1操作
(integer) 1
127.0.0.1:6379> incr view
(integer) 2
127.0.0.1:6379> get view
"2"
127.0.0.1:6379> decr view # 减1操作
(integer) 1
127.0.0.1:6379> incrby view 10 # 设定加X
(integer) 11
127.0.0.1:6379> decrby view 2 # 设定减X
(integer) 9
####################################################################
# 字符串范围range
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> set key1 "hello xiaojiang"
OK
127.0.0.1:6379> get key1
"hello xiaojiang"
127.0.0.1:6379> GETRANGE key1 0 4 #截取[0,4]字符串
"hello"
127.0.0.1:6379> GETRANGE key1 0 -1 #获取全部的字符串
"hello xiaojiang"

# 替换
127.0.0.1:6379> set key2 "jiangxiaohan"
OK
127.0.0.1:6379> get key2
"jiangxiaohan"
127.0.0.1:6379> setrange key2 1 xx # 替换指定位置开始的字符串
(integer) 12
127.0.0.1:6379> get key2 
"jxxngxiaohan"

####################################################################
# setex(set with expire)  # 设置过期时间
# setnx(set if not) # 不存在设置(在分布式锁中会常常使用!)

127.0.0.1:6379> setex key3 60 "hello" # 设置key3 值1分钟过期
OK
127.0.0.1:6379> ttl key3
(integer) 54
127.0.0.1:6379> get key3
"hello"
127.0.0.1:6379> setnx mykey "redis" # 如果mykey不存在,创建mykey
(integer) 1
127.0.0.1:6379> keys *
1) "key3"
2) "key2"
3) "key1"
4) "mykey"
127.0.0.1:6379> setnx mykey "mongodb"# 如果mykey存在,创建失败
(integer) 0
127.0.0.1:6379> get mykey
"redis"

####################################################################
# 批量处理
# mset
# mget
# 同时设置多个值是一个原子性操作,要么成功要么失败

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"
127.0.0.1:6379> msetnx k1 v1 k4 v4
(integer) 0

# 对象
set user:1 {name:zhangsan,age:3} #设置一个user:1 对象值为json字符来保存一个对象!
# 也可以这么操作!user:{id}:{feild}
127.0.0.1:6379> mset user:1:name zhangsan user:1:age 2
OK
127.0.0.1:6379> mget user:1:name user:1:age
1) "zhangsan"
2) "2"

####################################################################
# getset 先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"

string类似的使用场景:value除了是我们的字符串还可以是计数器

  • 计数器
  • 统计多单位的数量 uid:1:flower 0
  • 粉丝数
  • 对象缓存存储

4-2、List (列表)

基本的数据类型,列表
在redis里面,我们可以把list完成,栈、队列、堵塞队列!

# 插入
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 zero # 在尾部开始插入
(integer) 4
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "two"
3) "one"
4) "zero"

# 移除
127.0.0.1:6379> rpop list # 右移除
"zero"
127.0.0.1:6379> lpop list # 左移除
"three"
127.0.0.1:6379> lrange list 0 -1
1) "two"
2) "one"
####################################################################
# lindex获取下标

127.0.0.1:6379> lindex list 1 # 通过下标获得list的某一个值
"one"

####################################################################
#llen 获取长度

127.0.0.1:6379> llen list
(integer) 2
####################################################################
# 移除指定的值 Lrem

127.0.0.1:6379> lpush list tow
(integer) 3
127.0.0.1:6379> lpush list one
(integer) 4
127.0.0.1:6379> lrange list 0 -1
1) "one"
2) "tow"
3) "two"
4) "one"
127.0.0.1:6379> lrem list 1 one # 移除list集合中指定的value,精确匹配
(integer) 1
127.0.0.1:6379> lrange list 0 -1
1) "tow"
2) "two"
3) "one"
####################################################################
# trim 修剪操作

127.0.0.1:6379> rpush mylist "hello" "hello1" "hello2" "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 3 # 截取指定的长度(通过下标)
OK
127.0.0.1:6379> lrange mylist 0 -1
1) "hello1"
2) "hello2"
3) "hello3"
####################################################################
# rpoplpush 移除列表最后一个元素,在新的列表中插入第一个元素

127.0.0.1:6379> rpoplpush mylist myotherlist
"hello3"
127.0.0.1:6379> lrange mylist 0 -1
1) "hello1"
2) "hello2"
127.0.0.1:6379> lrange myotherlist 0 -1
1) "hello3"
####################################################################
#lset 将表中某个值修改为另一个值

127.0.0.1:6379> lset myotherlist 0 hello
OK
127.0.0.1:6379> lrange myotherlist 0 -1
1) "hello"
# 越界会报错
####################################################################
# linsert 

127.0.0.1:6379> linsert myotherlist after "hello" "world" # after/before [已存在的key] [新增的值]
(integer) 2
127.0.0.1:6379> lrange myotherlist 0 -1
1) "hello"
2) "world"
####################################################################

小结

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

4-3、Set(集合)

set中的值是不能重复的

# 不能插入重复的数据
127.0.0.1:6379> sadd myset "hello" # 插入set值
(integer) 1
127.0.0.1:6379> sadd myset "jiangxiaohan"
(integer) 1
127.0.0.1:6379> sadd myset "xiaojiang"
(integer) 1
127.0.0.1:6379> smembers myset  # 遍历set
1) "xiaojiang"
2) "jiangxiaohan"
3) "hello"
127.0.0.1:6379> sismember myset hello # 判断set中存不存在某一个值
(integer) 1
127.0.0.1:6379> sismember myset world
(integer) 0
127.0.0.1:6379> scard myset # 查看set的个数
(integer) 3
####################################################################
# 移除 srem

127.0.0.1:6379> srem myset hello # 移除set集合中的指定元素
(integer) 1
127.0.0.1:6379> smembers myset
1) "xiaojiang"
2) "jiangxiaohan"
####################################################################
# set 无序不重复集合,抽随机

127.0.0.1:6379> srandmember myset [num] #随机抽出指定个数的元素
"xiaojiang"
127.0.0.1:6379> srandmember myset
"jiangxiaohan2"
####################################################################
# 随机删除的key

127.0.0.1:6379> smembers myset
1) "jiangxiaohan1"
2) "xiaojiang"
3) "jiangxiaohan2"
4) "jiangxiaohan3"
5) "jiangxiaohan"
127.0.0.1:6379> spop myset
"jiangxiaohan1"
127.0.0.1:6379> spop myset
"jiangxiaohan2"
####################################################################
# 将一个指定的值,移动到另外一个集合

127.0.0.1:6379> smembers myset
1) "xiaojiang"
2) "jiangxiaohan3"
3) "jiangxiaohan"
127.0.0.1:6379> smove myset myotherset xiaojiang
(integer) 1
127.0.0.1:6379> smembers myotherset
1) "xiaojiang"
####################################################################
# 微博、B站共同关注(交集)

127.0.0.1:6379> smembers myset
1) "xiaojiang"
2) "jiangxiaohan3"
3) "jiangxiaohan"
127.0.0.1:6379> smembers myotherset
1) "xiaojiang"
127.0.0.1:6379> sdiff myset myotherset #差集
1) "jiangxiaohan"
2) "jiangxiaohan3"
127.0.0.1:6379> sinter myset myotherset # 交集
1) "xiaojiang"
127.0.0.1:6379> sunion myset myotherset # 并集
1) "jiangxiaohan"
2) "jiangxiaohan3"
3) "xiaojiang"
####################################################################

set类似的使用场景:
微博,A用户将所有关注的人放到一个set集合中!将它的粉丝也放到一个集合中。找共同好友,二度好友、推荐好友等。

4-4、Hash(哈希)

Map集合,key-Map集合! 本质和String没有太大的区别,还是一个简单的key-value!
set myhash field “jiangxiaohan”

127.0.0.1:6379> hset myhash field1 jiangxiaohan # set一个具体的key-value
(integer) 1
127.0.0.1:6379> hget myhash field1
"jiangxiaohan"
127.0.0.1:6379> hmset myhash field1 hello field2 world # set多个key-value
OK
127.0.0.1:6379> hmget myhash field1 field2 # 获取多个字段值
1) "hello"
2) "world"
127.0.0.1:6379> hgetall myhash # 获取全部的字段值
1) "field1"
2) "hello"
3) "field2"
4) "world"
####################################################################
# 删除hash指定的key

127.0.0.1:6379> hdel myhash field1 # 删除hash指定的key值
(integer) 1
127.0.0.1:6379> hgetall myhash
1) "field2"
2) "world"
####################################################################
# 获取hash值里面的长度

127.0.0.1:6379> hlen myhash # 获取hash值里面的长度
(integer) 1
####################################################################
# 判断hash的某个key存不存在

127.0.0.1:6379> hexists myhash field1
(integer) 1
####################################################################
# 只获取hash里面所有的key值
127.0.0.1:6379> hkeys myhash
1) "field2"
2) "field1"
3) "field3"

# 值获取hash里面所有的valuee值
127.0.0.1:6379> hvals myhash
1) "world"
2) "hello"
3) "jianhxiaohan"

####################################################################
# hash值的incrby decrby

127.0.0.1:6379> hset myhash field4 3
(integer) 1
127.0.0.1:6379> hincrby myhash field4 1
(integer) 4
127.0.0.1:6379> hincrby myhash field4 -1
(integer) 3
####################################################################
#. hsetnx(同上)

hash类似的使用场景:
hset user:1 name jiangxiaohan(类似这样的方式设置键值队),尤其是经常变动的信息。hash适合于对象的存储,String更适合一个字符串的存储。

4-5、Zset(有序集合)

在set的基础上,增加了一个值,set k1 v1,zset k1 score1 v1

127.0.0.1:6379> zadd myset 1 one 2 tow 3 three # 添加多个值
(integer) 3
127.0.0.1:6379> zrange myset 0 -1 #  遍历
1) "one"
2) "tow"
3) "three"
####################################################################
#  排序如何实现 zrangebyscore
#  min max字段可以通过(选择区间

127.0.0.1:6379> zadd salary 2500 xiaoming
(integer) 1
127.0.0.1:6379> zadd salary 5000 zhangsan 10000 jiangxiaohan
(integer) 2
127.0.0.1:6379> zrangebyscore salary -inf +inf
1) "xiaoming"
2) "zhangsan"
3) "jiangxiaohan"

# 开闭区间的区别
127.0.0.1:6379> zrangebyscore salary (2500 5000 withscores
1) "zhangsan"
2) "5000"
127.0.0.1:6379> zrangebyscore salary 2500 5000 withscores
1) "xiaoming"
2) "2500"
3) "zhangsan"
4) "5000"

####################################################################
# zrem删除set某个值

127.0.0.1:6379> zrange salary 0 -1
1) "xiaoming"
2) "zhangsan"
3) "jiangxiaohan"
127.0.0.1:6379> zrem salary xiaoming
(integer) 1
127.0.0.1:6379> zrange salary 0 -1
1) "zhangsan"
2) "jiangxiaohan"
127.0.0.1:6379> zcard salary # 显示zset的个数
(integer) 2

####################################################################
# zrevrange 从大到小进行查询 

127.0.0.1:6379> zrevrange salary 0 -1
1) "jiangxiaohan"
2) "zhangsan"

案例思路:set排序 存储班级成绩表,工资表排序!
普通消息,1,重要消息,2,带权重添加判断
排行榜应用实现

5、特色数据类型

5-1、geospatial地理位置

朋友的定位,附近的人,打车距离计算?
redis的geo在redis3.2版本就推出了!这个功能可以推算地理位置的信息,两地之间的距离,方圆几里的恶人!
可以查询一些测试数据!http://www.jsons.cn/lngcodeinfo/13C49BA26E7E84CC/
只有六个命令

  1. GEOADD

添加地理位置

# 规则:两级无法添加,我们一般会下载城市的数据,通过java的程序一次性导入!
# 参数:key (经度(180)、纬度(90)、名称)
127.0.0.1:6379> geoadd china:city 116.40 39.90 beijing
(integer) 1
127.0.0.1:6379> geoadd china:city 121.47 31.23 shanghai
(integer) 1
127.0.0.1:6379> geoadd china:city 106.50 29.53 chongqing
(integer) 1
127.0.0.1:6379> geoadd china:city 114.05 22.54 shenzhen 117.19 39.12 tianjin
(integer) 2
  1. GEODIST

返回两个给定位置之间的距离

127.0.0.1:6379> geodist china:city beijing tianjin
"110100.9036"
127.0.0.1:6379> geodist china:city beijing tianjin km
"110.1009"

单位

  • m
  • km
  • mi 英里
  • ft 英尺
  1. GEOHASH

该命令将返回11个字符的Geohash字符串

# 将经纬度转换为字符串,如果两个字符串越接近,距离越近
127.0.0.1:6379> geohash china:city tianjin beijing
1) "wwgqd9x4sb0"
2) "wx4fbxxfke0"
  1. GEOPOS

从key里返回所有给定位置元素的位置

# 获取指定的城市的经度和纬度
127.0.0.1:6379> geopos china:city beijing chongqing tianjin
1) 1) "116.39999896287918091"
   2) "39.90000009167092543"
2) 1) "106.49999767541885376"
   2) "29.52999957900659211"
3) 1) "117.19000071287155151"
   2) "39.12000048819218279"
  1. GEORADIUS

获取我附近的人?获得附近的人的地址,定位!通过半径来查询!
georadius key 经度 纬度 半径 单位 [withdist 距离][withcoord 经纬度] [count 个数] num

127.0.0.1:6379> georadius china:city 110 30 1000 km 
1) "chongqing"
2) "shenzhen"
127.0.0.1:6379> georadius china:city 110 30 1000 km withcoord
1) 1) "chongqing"
   2) 1) "106.49999767541885376"
      2) "29.52999957900659211"
2) 1) "shenzhen"
   2) 1) "114.04999762773513794"
      2) "22.53999903789756587"
# 获得指定数量的人
127.0.0.1:6379> georadius china:city 110 30 1000 km withdist withcoord count 1
1) 1) "chongqing"
   2) "341.9374"
   3) 1) "106.49999767541885376"
      2) "29.52999957900659211"
  1. GEORADIUSBYMEMBER
# 找出位于指定元素周围的其他元素
127.0.0.1:6379> georadiusbymember china:city shanghai 400 km
1) "shanghai"
127.0.0.1:6379> georadiusbymember china:city beijing 400 km
1) "tianjin"
2) "beijing"

geo底层的实现原理其实就是Zset!我们可以使用Zset命令来操作geo!

# soga 真的有耶!
127.0.0.1:6379> zrange china:city 0 -1
1) "chongqing"
2) "shenzhen"
3) "shanghai"
4) "tianjin"
5) "beijing"
# zrem 移除指定的元素

5-2、hyperloglog

优点,占用内存是固定的,只需要废12kb内存,如果要从内存角度来比较的话这个是首选。
什么是基数?
A{1,3,5,7,8,7}
B{1,3,5,7,8}
基数(不重复的元素)= 5,可以接受误差

简介:
Redis hyperloglog基数统计的算法
网页的UV(一个人访问一个网站多次,但是还是算作一个人)
传统的方式,set保存用户的id,然后就可以统计set中的元素数量作为标准判断!
这个方式如果大量的用户id保存,就会比较麻烦!我们的目的是为了计数,而不是保存用户id
容错率81%

127.0.0.1:6379> PFadd mykey a b c d e f g h i j # 设置
(integer) 1
127.0.0.1:6379> PFcount mykey # 获取长度
(integer) 10
127.0.0.1:6379> PFadd mykey2 i j k l m n o p 
(integer) 1
127.0.0.1:6379> PFcount mykey2
(integer) 8
127.0.0.1:6379> PFmerge mykey3 mykey mykey2 # 合并mykey
OK
127.0.0.1:6379> PFcount mykey3
(integer) 16

5-3、Bitmaps

位图,也是数据结构,用二进制位来操作记录,就只有0和1两个状态。
365天 = 365bit
1字节 = 8 bit

# 周一到周日的打卡
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 1
(integer) 0
127.0.0.1:6379> getbit sign 3
(integer) 1

127.0.0.1:6379> bitcount sign # 统计打卡时间
(integer) 3

6、redis基本的事务操作

事务:redis单条命令是保存原子性的,但是事务是不保证原子性的! 也没有隔离级别的概念
Redis事务本质:一组命令的集合!一个事务中所有的命令都会被序列化,在事务执行的过程中,会按照顺序执行。
一次性、顺序性、排他性!执行一系列的命令。

redis的事务阶段

  • 开启事务(multi)
  • 命令入队(…)
  • 执行事务(exec)

正常执行事务

127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> mset k1 v1 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) "v2"
3) OK
################################################################
# 放弃事务(discard)

127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> mset k1 v1 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)

# 一旦放弃事务,以上所有事务队列的命令都不会被执行

如果事务有错误怎么办?

  • 编译型异常(代码有问题!命令有错误!),事务中所有的命令都不会被执行!
  • 运行时异常(1/0)如果事务队列中存在语法性错误,那么在执行命令的时候,其他命令是可以正常执行的,命令错误抛出异常。
#编译型异常
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 k3 v3
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 k4 v4
QUEUED
127.0.0.1:6379(TX)> set k5 v5
QUEUED
127.0.0.1:6379(TX)> exec # 执行事务保存,所有的命令都不能执行
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get k5
(nil)

# 运行时异常(不影响其他)
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)> set k3 v3
QUEUED
127.0.0.1:6379(TX)> get k3
QUEUED
127.0.0.1:6379(TX)> exec # 不保证原则性的
1) (error) ERR value is not an integer or out of range
2) OK
3) OK
4) "v3"

监控

悲观锁:

  • 很悲观,什么时候都会出问题,无论做什么都加锁!

乐观锁:

  • 很乐观,认为什么时候都不会出问题,所以不会上锁!更新数据的时候去判断一下,在此期间判断是否有人修改过这个数据。
  • 获取version
  • 更新的时候比较version
  • watch可以当作redis的乐观锁
  • 注:redis在事务执行的时候,加了watch,事务执行失败的话会自动unwatch,不用去加unwatch

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 # 监视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

# 测试效果 
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> get money
"80"
127.0.0.1:6379> set money 1000
OK
###################################################
127.0.0.1:6379(TX)> exec # 执行之前另外一个线程修改money,修改失败
(nil)

7、Jedis

使用java来操作redis
官方推荐的java连接开发工具!使用java操作redis中间件。

  1. 导入对应的依赖
		<dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>4.4.0-m2</version>
        </dependency>
  1. 编码测试
  • 连接数据库
public class TestPing {
    public static void main(String[] args) {
        // 1 new Jedis对象
        Jedis jedis = new Jedis("127.0.0.1", 6379);
        // jedis 所有的命令都是之前所有的指令
        System.out.println(jedis.ping());
    }
}

/*
PONG

Process finished with exit code 0
**/
  • 操作命令

使用上述大部分命令

public class TestPing {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1", 6379);
        System.out.println(jedis.mset("k1","v1","k2","v2"));
        System.out.println(jedis.get("k1"));
    }
}
/*
OK
v1

Process finished with exit code 0
**/
  • 断开链接

事务

public class TestPing {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1", 6379);

        // 开启事务
        Transaction multi = jedis.multi();

        try{
            multi.set("user1", "jiangxiaohan");
            multi.set("user2", "xiaohan");

            multi.exec();
        }catch (Exception e){

        }finally {
            System.out.println(jedis.get("user1"));
            System.out.println(jedis.get("user2"));
            jedis.close();
        }
    }
}
/*
jiangxiaohan
xiaohan

Process finished with exit code 0
**/

8、SpringBoot整合

  1. 引入依赖
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <version>3.0.2</version>
</dependency>

  1. 配置
# redis配置
spring.redis.host=127.0.0.1
spring.redis.port=6379
  1. 测试
@SpringBootTest
public class TestPingTest {

    @Autowired
    private RedisTemplate redisTemplate;

    @Test
    void contextLoads(){
        // RedisTemplate 连接redis实例
        // opsForValue() 操作字符串
        // opsForList()  操作List
        // opsForSet()
        // opsForHash()
        // opsForZSet()
        // opsForGeo()
        // opsForHyperLogLog()

        // 除了基本的操作,我们常用的方法都可以直接通过redisTemplate来操作
        // 比如说事务,和基本的CRUD

        // 获取redis的连接对象
        RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
        connection.flushDb();

        redisTemplate.opsForValue().set("mykey", "jiangxiaohan");
        System.out.println(redisTemplate.opsForValue().get("mykey"));
    }
}

客户端有问题,需要序列化(需要自己定义redis配置类)

127.0.0.1:6379> keys *
1) "\xac\xed\x00\x05t\x00\x05mykey"
  1. 自定义配置类
@Configuration
public class RedisConfig {

    // 编写redisconfig
    @Bean
    @SuppressWarnings("all")
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory)
        throws UnknownHostException {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);

        // 配置自己的序列化操作
        // Json序列化配置
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        // String的序列化
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();

        // key采用的string的序列化方法
        template.setKeySerializer(stringRedisSerializer);
        //hash的key
        template.setHashKeySerializer(stringRedisSerializer);
        //value序列化采用jackson
        template.setValueSerializer(jackson2JsonRedisSerializer);
        //hash的value
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();

        return template;
    }
}



    @Test
    public void test() throws JsonProcessingException {// 直接传对象会报错误,所以对象要序列化(或者对象实现序列化)
        User user = new User("jiangxiaohan", 25);
        String jsonUser = new ObjectMapper().writeValueAsString(user);//需要web包
        redisTemplate.opsForValue().set("user", jsonUser);
        System.out.println(redisTemplate.opsForValue().get("user"));
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值