Reids 5.08 版本
1.概述
1.1 Redis是什么
Redis是一个开源的,内存中的数据结构存储系统,它可以作用**数据库**、**缓存**、**消息中间件**。它支持多种数据结构,如字符串(String)、散列(hashes)、集合(lists)、有序集合(sorted sets)与范围查询、butnaos、hyperloglogs和地理空间(geospatial)索引版经常哈寻,Redis内置了 复制(replication),LUA脚本(luascripting),LRU驱动事件(LRU eviction),事务(transactiobns)和不同级别的磁盘持久化(persistence),并通过Redis哨兵(Sentienl)和自动分区(Cluster)提供高可用性(high availability)
1.2 Reids能做什么
1.内存存储,持久化。内存是断电就失去,所以说持久化很重化(rdb、aof)
2.效率高,可以用于告诉缓存
3.发布订阅系统
4.吸毒信息分析
5.计时器,计数器(如浏览量)
6…
1.3 特性
1.多样化数据类型
2.集群
3.持久化
4.事务
2.安装
2.1 Windows下安装
1.下载安装包:https://github.com/dmakic/redis/releases
2.解压
3.执行redis-server.exe启动服务
4.使用reids-cli.exe客户端连接reids
输入指令ping 如果返回个pong就代表连接成功
Reid文件介绍
redis-server.exe : redis启动服务
redis-cli.exe : redis客户端
redis-check-aof.exe : redis检查持久化文件是否正确
redis-benchmark.exe: redis测试性能
Redis推荐在Linux下使用
2.2 linux下的安装
参考Linux安装Redis的笔记
3.基本语法
Redis 命令手册: http://redis.cn/commands.html
查看是什么类型
type 键
查看剩余过期时间
ttl 键
设置数据过期时间 # 设置10秒后过期
expire name 10
从当前数据库移除此键
move 键 1
判断某个键是否存在,如果存在返回1 不存在返回0
exists 键
redis默认有16个数据库,在redis.conf中有个databases 16
默认使用的是第0个 ,可以使用select 切换数据库
select 2
查看当前数据库容量大小 当我们存入一个值 容量就+1
DBSIZE
查看数据库所有的key
keys *
清空当前库
flushdb
清空所有库
flushall
4.基础知识
4.1 Redis是单线程的
Redis是基于内存操作的,CPU不是Redis的性能瓶颈,Redis的瓶颈是根据机器的内存和网络的带宽,既然可以使用单线程实现就可以使用单线程了。
为什么单线程还这么快?
核心:redis是将所有的数据全部放在了内存中,所以使用单线程效率是最高的。
多线程会产生CPU上下文切换,这是一个很耗时的操作,对于内存系统来说秒如果没有上下文切换效率就是最高的。多次读写都是在一个CPU上的,在内存的情况来看就是最佳方案。
5.常见五大数据类型
51 String(字符串类型)
设置值
set 键名 值
取值
get 键名
追加
append 键值 追加的内容
当我们追加成功会返回字符串的追加后的长度
如果我们的键不存在就会创建一个新的,就相当于set key
获取字符串长度
strlen 键
自增
127.0.0.1:6379> set num 0 # 设置num的值为0
OK
127.0.0.1:6379> get num # 获取num的值
"0"
127.0.0.1:6379> incr num # num自增1
(integer) 1
127.0.0.1:6379> get num # 获取num的值.
"1"
自减
127.0.0.1:6379> set num 1 # 设置num为0
OK
127.0.0.1:6379> get num # 获取num的值
"1"
127.0.0.1:6379> decr num # 使得num自减1
(integer) 0
127.0.0.1:6379> get num # 获取num的值
"0"
步长 自增
127.0.0.1:6379> set num 1 # 设置num值为1
OK
127.0.0.1:6379> incrby num 9 # 让num加9
(integer) 10
127.0.0.1:6379> get num # 获取num的值
"10"
步长 自减
127.0.0.1:6379> set num 10 # 设置num值为10
OK
127.0.0.1:6379> DECRBY num 5 # 使num自减5
(integer) 5
获取范围字符串
127.0.0.1:6379> set str1 "hello world" # 设置值
OK
127.0.0.1:6379> get str1 # 取值
"hello world"
127.0.0.1:6379> GETRANGE str1 0 4 # 获取0-4的范围字符串
"hello"
127.0.0.1:6379> GETRANGE str1 0 -1 # 获取全部的字符串
"hello world"
替换字符串
127.0.0.1:6379> set str2 abcdefg # 设置值
OK
127.0.0.1:6379> get str2 # 获取值
"abcdefg"
127.0.0.1:6379> SETRANGE str2 1 xx # 替换键值为str2的下标为1替换为xx,如果替换值的长度大于1,比如替换值长度为2那么就替换下标为1的位置数2位进行替换
(integer) 7
127.0.0.1:6379> get str2
"axxdefg"
创建键并且设置过期时间
127.0.0.1:6379> SETEX str4 300 "hello" # 创建键设置过期时间为300秒
OK
127.0.0.1:6379> get str4 # 获取键
"hello"
127.0.0.1:6379> ttl str4 # 查看过期时间
(integer) 276
创建减 如果该键不存在则创建 如果存在则创建失败
127.0.0.1:6379> setnx mykey "dazui" # 创建键 并且判断该键是否存在 如果创建失败
(integer) 1
127.0.0.1:6379> get mykey
"dazui"
127.0.0.1:6379> setnx mykey "dazu666i" # 创建键 该键已经存在创建失败
(integer) 0
127.0.0.1:6379> get mykey
"dazui"
一次性设置多个值 批量设置 | 获取多个值 批量获取多个值
127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3 k4 v4 # 设置多个值
OK
127.0.0.1:6379> keys *
1) "k4"
2) "k1"
3) "k2"
4) "k3"
####################################################################
127.0.0.1:6379> mget k1 k2 k3 k4 # 获取多个值
1) "v1"
2) "v2"
3) "v3"
4) "v4"
####################################################################
127.0.0.1:6379> mset k1 v2 k2 v3
OK
127.0.0.1:6379> msetnx k1 v3 k2 v4 # 如果设置的键存在就创建失败
(integer) 0
创建对象
127.0.0.1:6379> mset user:1:name "dazui" user:1:age 18 #创建user对象1 名字为dazui 年龄为18
OK
127.0.0.1:6379> mget user:1:name user:1:age #获取user对象1 名字和 age信息
1) "dazui"
2) "18"
127.0.0.1:6379
getset 使用 可以用于更新
127.0.0.1:6379> getset name dazuizui #设置name值 如果该值没值就获取null 如果有值就获取之前的值并且替换
(nil)
127.0.0.1:6379> get name
"dazuizui"
127.0.0.1:6379> getset name wasaidazuihaoshuai #获取之前的值 并且将新值覆盖
"dazuizui"
5.2 list(列表)
list插入值 查询值
127.0.0.1:6379> rpush list 1 # 右插入一个数据
(integer) 1
127.0.0.1:6379> rpush list 2 # 右插入一个数据
(integer) 2
127.0.0.1:6379> lpush list 0 # 左插入一个数据
(integer) 3
127.0.0.1:6379> lrange list 0 -1 # 查询所有数据
1) "0"
2) "1"
3) "2"
移除值
127.0.0.1:6379> lpop list # 移除左侧第一个值
"0"
127.0.0.1:6379> rpop list # 移除右侧第一个值
"2"
127.0.0.1:6379> lrange list 0 -1 # 查询所有数据
1) "1"
127.0.0.1:6379>
获取指定下标的数据
127.0.0.1:6379> lindex list 1 # 获取指定下标为1的数据
"2"
127.0.0.1:6379> lindex list 0 # 获取指定下标为0的数据
"1"
127.0.0.1:6379> lindex list 5 # 获取指定下标为5的手机壳
(nil)
获取队列的长度
127.0.0.1:6379> llen list # 获取长度
(integer) 3
移除指定的值
127.0.0.1:6379> lrange list 0 -1 # 获取值
1) "1"
2) "2"
3) "3"
4) "4"
5) "4"
6) "4"
7) "4"
127.0.0.1:6379> lrem list 1 "4" # 移除一个元素并且值为4
(integer) 1
127.0.0.1:6379> lrem list 2 "4" # 移除凉饿元素并且值为4
(integer) 2
保留指定下标区间的数据
127.0.0.1:6379> lrange list 0 -1 # 获取所有数据
1) "1"
2) "2"
3) "3"
4) "4"
127.0.0.1:6379> ltrim list 1 2 # 只保留指定下标区间的数据 其他数据都被截取
OK
127.0.0.1:6379> lrange list 0 -1 # 获取所有数据
1) "2"
2) "3"
移动列表最后一个元素,将他移动到其他列表中
127.0.0.1:6379> lrange list 0 -1 # 获取list表所有数据
1) "2"
2) "3"
127.0.0.1:6379> rpoplpush list list1 # 将list列表右侧第一个数据移至到新的列表中
"3"
127.0.0.1:6379> lrange list1 0 -1 # 查询list1列表所有数据
1) "3"
指定下标添加值
127.0.0.1:6379> lpush list 0 # 左插入一个数据
(integer) 1
127.0.0.1:6379> exists list # 判断键是否存在
(integer) 1
127.0.0.1:6379> exists list1 # 判断键是否存在
(integer) 0
127.0.0.1:6379> lset list 0 dazui # 向list集合下标为0的地方插入值,如果下标0的位置已有值就会被替换
OK # 插入成功
127.0.0.1:6379> lset list1 0 dazui # 向list1集合下标为0的地方插入值
(error) ERR no such key # 插入失败,因为list1键值不存在
127.0.0.1:6379> lrange list 0 -1 # 查询数
1) "dazui"
在指定值得前方或者后方插入一个值
127.0.0.1:6379> lrange list 0 -1 # 查询所有制
1) "dazui"
127.0.0.1:6379> LINSERT list before "dazui" "hello" # 在dazui字符串前方插入一个字符
(integer) 2
127.0.0.1:6379> lrange list 0 -1 # 查询所有字符
1) "hello"
2) "dazui"
127.0.0.1:6379> linsert list after "dazui" "haoshuai" # 在dazui字符串后方插入一个字符
(integer) 3
127.0.0.1:6379> lrange list 0 -1 # 查看所有字符
1) "hello"
2) "dazui"
3) "haoshuai"
5.3 set集合
set的值不可以重复,也是无序的
添加元素
127.0.0.1:6379> sadd sei "hello" # 添加值
(integer) 1
127.0.0.1:6379> sadd sei "dazui" # 添加值
(integer) 1
查询所有值
127.0.0.1:6379> smembers sei
1) "dazui"
2) "hello"
查看元素是否存在
127.0.0.1:6379> sismember sei hello # 查看元素是否存在,如果存在返回1
(integer) 1
127.0.0.1:6379> sismember sei hello1 # 查看元素知否存在,如果存在返回0
(integer) 0
查看集合个数
127.0.0.1:6379> scard sei # 查询是否存在
(integer) 2
删除指定元素
127.0.0.1:6379> srem sei hello # 移除指定元素
(integer) 1
随机获取元素
127.0.0.1:6379> smembers sei # 查询所有元素
1) "hello"
2) "dazui"
127.0.0.1:6379> SRANDMEMBER sei # 随机获取一个元素
"dazui"
随机删除指定的key
127.0.0.1:6379> spop sei # 随机删除一个值
"dazui"
127.0.0.1:6379>
将一个值引用到另一个集合
127.0.0.1:6379> smove sei myset "dazui"# 将sei里的dazui元素移至到myset元素
(integer) 1
127.0.0.1:6379> SMEMBERS myset # 查询myset集合
1) "dazui"
127.0.0.1:6379> smembers sei # 查询sei集合
1) "hello"
127.0.0.1:6379>
查询两个集合之间的差集
127.0.0.1:6379> smembers sei # 查询sei集合
1) "dazui"
2) "hello"
127.0.0.1:6379> smembers myset # 查询myset集合
1) "dazui"
127.0.0.1:6379> sdiff sei myset # 查询他们的差集
1) "hello"
查询两个集合的交集
127.0.0.1:6379> sinter myset sei # 查询交集
1) "dazui
查询两个集合的合集
127.0.0.1:6379> SUNION sei myset # 查询合集
1) "hello"
2) "dazui"
127.0.0.1:6379>
5.4 Hash (哈希)
添加 获取
127.0.0.1:6379> hset hash1 k1 12 # hash名叫hash1 键值叫k1 值为12
(integer) 1
127.0.0.1:6379> hset hash1 k2 13 k3 14 # 设置多个值
(integer) 2
127.0.0.1:6379> hget hash1 k1 # 获取指定值
"12"
127.0.0.1:6379> hmget hash1 k2 k3 # 获取多个指定值
1) "13"
2) "14"
删除指定值 获取全部之
127.0.0.1:6379> hdel hash1 k1 # 删除指定值
(integer) 1
127.0.0.1:6379> hgetall hash1 # 获取全部键值对
1) "k2"
2) "13"
3) "k3"
4) "14"
获取hash表的长度
127.0.0.1:6379> hlen hash1 # 获取长度
(integer) 2
获取指定键是否存在
127.0.0.1:6379> HEXISTS hash1 k2 # 判断指定键是否存在
(integer) 1
获取所有的键 获取所有的值
127.0.0.1:6379> hkeys hash1 # 查看所有键
1) "k2"
2) "k3"
127.0.0.1:6379> HVALS hash1 # 查看所有值
1) "13"
2) "14"
自增自减
127.0.0.1:6379> hincrby hash1 k2 2
(integer) 15
127.0.0.1:6379> hincrby hash1 k3 -2
(integer) 12
127.0.0.1:6379> hvals hash1
1) "15"
2) "12"
5.5 Zset(有序集合)
127.0.0.1:6379> zadd key1 1 one # 将下标为1的地方添加数据
(integer) 1
127.0.0.1:6379> zadd key1 2 two # 将下标为2的地方添加数据
(integer) 1
127.0.0.1:6379> zadd key1 2 three # 将下标为2的地方添加数据 如果下标为2的地方有数据 下标为2的数据向后移动,将新数据放在下标为2的最前方, 当前下标为2的数据有three two
(integer) 1
127.0.0.1:6379> zrange key 0 -1
(empty array)
127.0.0.1:6379> zrange key1 0 -1
1) "one"
2) "three"
3) "two"
127.0.0.1:6379>
查询所有数据并且带序号
127.0.0.1:6379> zrangebyscore key1 -inf +inf withscores # 查询所有数据并且带序号
1) "one"
2) "1"
3) "three"
4) "2"
5) "two"
6) "2"
查看小于某个序号的值
127.0.0.1:6379> zrangebyscore key1 -inf 2 withscores # 查询小于2的所值,并且打印序号
1) "one"
2) "1"
3) "three"
4) "2"
5) "two"
6) "2"
127.0.0.1:6379> zrangebyscore key1 -inf +inf # 查询小于2的的数据
1) "one"
2) "three"
3) "two"
移除指定元素元素
127.0.0.1:6379> zrem key1 two # 移除指定元素two
(integer) 1
查看多少个元素
127.0.0.1:6379> zcard key1 # 获取有序集合的格式
(integer) 3
查看从大到小序号的值
127.0.0.1:6379> zrevrange key1 0 -1 withscores # 查询从大到小的序号的值
1) "two"
2) "5"
3) "five"
4) "5"
5) "three"
6) "2"
7) "one"
8) "1"
查看区间的值有几个
127.0.0.1:6379> zrange key1 0 -1 withscores # 查看所有值并且显示序号
1) "one"
2) "1"
3) "three"
4) "2"
5) "two"
6) "2"
7) "five"
8) "5"
127.0.0.1:6379> zcount key1 0 3 # 查询0-3区间的值有几个
(integer) 3
6.三种特殊数据类型
6.1 geospatial (地理位置)
geospatial可以存放城市维度
有效精度 -180度到 180度
有效维度-85.05112878度到85.05112878度
**维度查询网站:**http://www.jsons.cn/lngcode/
添加地理位置
127.0.0.1:6379> geoadd HongKong:city 115.164167 22.2664163 hongkong # 插入HongKong的维度
(integer) 1
127.0.0.1:6379> geoadd HongKong:city 114.17495 22.327115 jiulong # 插入HongKong JiuLong的维度
(integer) 1
获取指定城市坐标
127.0.0.1:6379> geopos HongKong:city jiulong # 获取HongKong JiuLong的经度纬度
1) 1) "114.17495101690292358"
2) "22.3271154118881654"
两个位置的距离
127.0.0.1:6379> geodist HongKong:city hongkong jiulong km # 查看两者之间的距离
"102.0236"
m标识单位为米
km表示单位为千米
mi标识单位为英里
ft标识单位为英尺
附近的人
以给定的经纬度为中心,找出某一半径内的元素
127.0.0.1:6379> georadius HongKong:city 115.164167 22.2664163 100 km
1) "jlc"
2) "hongkong"
127.0.0.1:6379> georadius HongKong:city 115.164167 22.2664163 1000 km
1) "jiulong"
2) "jlc"
3) "hongkong"
以位置为中心附近的位置
127.0.0.1:6379> georadiusbymember HongKong:city hongkong 100 km # 查看hongkong周围100km的城市
1) "jlc"
2) "hongkong"
6.2Hyperloglog(基数统计)
做什么?
如果允许容错,可以使用Hyperloglog进行计数
什么是基数
A{1,3,5,7,8,9} B{1,3,5,7}
就是查找不重复的元素
基本使用
127.0.0.1:6379> pfadd key3 a b b3 d f g h i j k # 添加数据
(integer) 1
127.0.0.1:6379> pfcount key3 # 查询数据返回长度
(integer) 10
127.0.0.1:6379> pfadd key2 i j k z x c v b n # 添加数据
(integer) 1
127.0.0.1:6379> pfmerge key key2 key3 # 合并key3 key2的数据返回给key
OK
127.0.0.1:6379> pfcount key # 查询key长度,Hyperloglog重复的数据会添加失败
(integer) 15
127.0.0.1:6379> pfadd key3 qw # 添加数据
(integer) 1
127.0.0.1:6379> pfcount key3 # 查询数据返回长度
(integer) 11
6.3 Bitmaps(位图)
他的值只有0和1
就来做个签到系统为案例
设置值 --0为未签到1为签到
127.0.0.1:6379> setbit bhk 0 1
(integer) 0
127.0.0.1:6379> setbit bhk 2 1
(integer) 0
127.0.0.1:6379> setbit bhk 3 0
(integer) 0
127.0.0.1:6379> setbit bhk 1 0
(integer) 0
127.0.0.1:6379> setbit bhk 4 0
(integer) 0
127.0.0.1:6379> setbit bhk 5 1
(integer) 0
127.0.0.1:6379> setbit bhk 6 1
(integer) 0
查看值 --如果返回0为未签到如果返回1为签到
127.0.0.1:6379> getbit bhk 4
(integer) 0
127.0.0.1:6379> getbit bhk 6
(integer) 1
查看为1的数据
127.0.0.1:6379> bitcount bhk
(integer) 4
7.基本的事务操作
7.1事务
Redis事务的本质:一组命令的集合,一块执行!一个事务中的所有命令都会被序列化,在事务执行过程中,会按照顺序执行。
**特性:**一次性、顺序性、排他性!
Redis事务没有隔离级别的概念
所有的命令在事务中,没有直接被执行,只有发起执行命令的时候才会执行。
Redis单条命令保存原子性,事务不保证原子性。
7.2 Redis的事务
1.开启事务(multi)
127.0.0.1:6379> multi # 开启事务
OK
2.命令入队 (写命令)
# 命令入队
127.0.0.1:6379> set name dazui
QUEUED
127.0.0.1:6379> set age 18
QUEUED
3.执行事务 (exec)
127.0.0.1:6379> exec # 执行
1) OK
正常执行事务!
127.0.0.1:6379> multi # 开启事务
OK
127.0.0.1:6379> set name dazui # 命令入队
QUEUED
127.0.0.1:6379> exec # 执行事务
1) OK
放弃事务
127.0.0.1:6379> multi # 开启事务
OK
127.0.0.1:6379> set k1 a # 命令入列
QUEUED
127.0.0.1:6379> set k2 a # 命令入列
QUEUED
127.0.0.1:6379> DISCARD # 放弃事务
OK
127.0.0.1:6379> get k1 # 获取String数据
(nil)
编译型异常
代码有问题或者命令有错误
如果触发了事务中所有命令都不会执行
127.0.0.1:6379> multi # 创建事务
OK
127.0.0.1:6379> set a1 a
QUEUED
127.0.0.1:6379> set a2 a
QUEUED
127.0.0.1:6379> set a3 a
QUEUED
127.0.0.1:6379> getset a1 # 错误命令入列
(error) ERR wrong number of arguments for 'getset' command
127.0.0.1:6379> set a4 4
QUEUED
127.0.0.1:6379> exec # 执行事务,报错
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get a1 # 证明了所有的命令都没有被执行
(nil)
运行时异常
比如1/0报错,如果事务队列中存在余发星,那么执行命令的时候其他命令都是可以正常执行的,错误命令抛出异常
127.0.0.1:6379> multi # 创建事务
OK
127.0.0.1:6379> set k1 "v1" # 设置值 加入命令队列
QUEUED
127.0.0.1:6379> incr k1 # 对k1进行自加1,但是k1存放的的数据不是纯数字,就会报错 加入队列
QUEUED
127.0.0.1:6379> set k2 12 # 设置值 加入队列
QUEUED
127.0.0.1:6379> exec # 执行事务
1) OK
2) (error) ERR value is not an integer or out of range
3) OK
127.0.0.1:6379> get k2 # 获取k2数据 证明运行时异常其他命令可以正常执行。
"12"
8.加锁
8.1 悲观锁
悲观锁认为什么时候都会出问题,无论做什么都会加锁。这样做特别影响性能。
8.2 乐观锁
他认为什么时候都不会出现问题。更新数据的时候去判断下,在此期间是否有人修改过这个数据。
8.3 监视
正常执行
127.0.0.1:6379> set money 100 # 设置100块钱
OK
127.0.0.1:6379> set out 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 # 钱自减20
QUEUED
127.0.0.1:6379> incrby out 20 # 花出去的钱自增20
QUEUED
127.0.0.1:6379> exec # 执行
1) (integer) 80
2) (integer) 20
并发情况
线程A
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 # 监视
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> decrby money 20
QUEUED
127.0.0.1:6379> incrby money 20
QUEUED
127.0.0.1:6379> exec # 执行 一定要在线程B执行完再去执行 为什么失败?因为在在执行事务他会比较money跟监视时候的值是否相同,如果相同就可以执行,不相同就执行失败,这时候线程B修改了money的值,所以更新失败
(nil)
线程B
127.0.0.1:6379> set money 10000
OK
放弃监视 解锁
127.0.0.1:6379> UNWATCH # 取消加锁
OK
9.番外
1.为什么Reids是6379
Alessia Merz 是一位意大利舞女、女演员。 Redis 作者 Antirez 早年看电视节目,觉得 Merz 在节目中的一些话愚蠢可笑,Antirez 喜欢造“梗”用于平时和朋友们交流,于是造了一个词 “MERZ”,形容愚蠢,与 “stupid” 含义相同。
后来 Antirez 重新定义了 “MERZ” ,形容”具有很高的技术价值,包含技艺、耐心和劳动,但仍然保持简单本质“。
到了给 Redis 选择一个数字作为默认端口号时,Antirez 没有多想,把 “MERZ” 在手机键盘上对应的数字 6379 拿来用了。