1、Redis为什么这么快?
- 误区一:以为高性能服务器一个是多线程来实现的
- 误区二:多线程一定比单线程效率高
- redis核心就是 如果我的数据全都放在内存里,我单线程去操作 就是效率最高的,为什么呢,因为多线程的本质就是cpu模拟出来多个线程情况,这种模拟出来的情况就有一个代价,就是上下文的切换,对于一个内存的系统来说,它没有上下文的切换就是效率最高的。
2、五种基础类型学习
1、基本数据尝试
默认16个数据库,类似数组下标从零开始,初始默认使用零号库
查看 redis.conf ,里面有默认的配置
databases 16
# Set the number of databases. The default database is DB 0, you can select
# a different one on a per-connection basis using SELECT <dbid> where
# dbid is a number between 0 and 'databases'-1
databases 16
2、Select 命令切换数据库
127.0.0.1:6379> select 7
OK
3、Dbsize 查看当前数据库的key的数量
127.0.0.1:6379> dbsize
(integer) 0
127.0.0.1:6379> set name zse
OK
127.0.0.1:6379> set age 10
OK
127.0.0.1:6379> dbsize
(integer) 2
4、Flushdb
127.0.0.1:6379> dbsize
(integer) 2
127.0.0.1:6379> FLUSHDB
OK
127.0.0.1:6379> dbsize
(integer) 0
5、五大数据类型
- 1、String-----------字符串类型
- 2、Hash------------哈希 类似于Map
- 3、LIst--------------列表
- 4、Set--------------集合
- 5、Zset-----------有序集合
6、Redis键 key
keys * //查看所有的key
exists name //exists key 的名字,判断某个key是否存在
move key db // move key db ----> 当前库就没有了,被移除了 移动到其他数据库
expire name 10 // exprie key seconds(秒钟)
ttl key //查看还有多少秒过期 -1表示用不过期 -2 表示已经过期
7、字符串String
set key value //设置值
get key //获取值
del key //删除key
exists name //判断某个key是否存在
append key 'test' //对不存在的key进行Append,等同于set,对已经存在的字符串进行Append字符串增加,尾部拼接
strlen key //获取key字符串长度
incr key //一定要是数字才能进行加减,+1
decr key //一定要是数字才能进行加减,-1
incrby views 10(increment) //一定要是数字才能进行加减,+10(自定义)
decrby views 10(increment) //一定要是数字才能进行加减,-10(自定义)
getrange key start end //获取指定区间范围内的值 0 -1 全部的值 (截取字符串)
setex key3 60 expire /设置过期时间
setnx key value //如果不存在就设置,成功返回1 如果存在就设置,不生效,失败返回0
mset k10 v10 k11 v11 k12 v12 //Mset 命令用于同时设置一个或多个 key-value 对。
mget k10 k11 k12 k13 //Mget 命令返回所有(一个或多个)给定 key 的值。
set user:1 value(json数据) //传统对象缓存
mset user:1:name zhangsan user:1:age 2
mget user:1:name user:1:age //可以用来缓存对象
getset key value //先获取在设置
String数据结构是简单的key-value类型,value其实不仅可以是String,也可以是数字。
常规key-value缓存应用:
常规计数:微博数,粉丝数等
8、列表LIst
LPUSH list "one" # Lpush:将一个或多个值插入到列表头部。(左)
RPUSH list "right" # rpush:将一个或多个值插入到列表尾部。(右)
Lrange list 0 -1 # lrange:返回列表中指定区间内的元素,区间以偏移量 START 和 END 指定。
Lpop list # lpop 命令用于移除并返回列表的第一个元素。当列表 key 不存在时,返回 nil
Rpop list # rpop 移除列表的最后一个元素,返回值为移除的元素。
Lindex list 1 # Lindex,按照索引下标获得元素(-1代表最后一个,0代表是第一个)
Llen list # 返回列表的长度
lrem list 1 "two" # lrem key 根据参数 COUNT 的值,移除列表中与参数 VALUE 相等的元素。
ltrim mylist 1 2 # Ltrim key 对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区
间之内的元素都将被删除。
rpoplpush mylist myotherlist # rpoplpush 移除列表的最后一个元素,并将该元素添加到另一个列表并返回。
lset list 0 "new" # lset key index value 将列表 key 下标为 index 的元素的值设置为 value
LINSERT mylist BEFORE "World" "There" # insert key before/after pivot value 用于在列表的元素前或者后插入元素
9、集合Set
sadd myset hello # sadd 将一个或多个成员元素加入到集合中,不能重复
smembers myset # smembers 返回集合中的所有的成员。
sismember myset yfy # sismember 命令判断成员元素是否是集合的成员
SCARD myset # scard,获取集合里面的元素个数
SREM myset zse # srem key value 用于移除集合中的一个或多个成员元素
SRANDMEMBER myset # srandmember key 命令用于返回集合中的一个随机元素。
spop myset # spop key 用于移除集合中的指定 key 的一个或多个随机元素
SMOVE myset myser1 zse # 将指定成员 member 元素从 source 集合移动到 destination 集合。
SDIFF key1 key2 差集: sdiff
SINTER key1 key2 交集: sinter
SUNION key1 key2 并集: sunion
在微博应用中,可以将一个用户所有的关注人存在一个集合中,将其所有粉丝存在一个集合。Redis还为集合提供了求交集、并集、差集等操作,可以非常方便的实现如共同关注、共同喜好、二度好友等功能,对上面的所有集合操作,你还可以使用不同的命令选择将结果返回给客户端还是存集到一个新的集合中
10、哈希Hash
kv 模式不变,但是v是一个键值对
hset myhash field1 zse
hget myhash field1 # hset、hget 命令用于为哈希表中的字段赋值
hmset myhash field1 zse field2 yfy
hmget myhash field1 dield2 # hmset、hmget 同时将多个field-value对设置到哈希表中。会覆盖哈希表中已存在的字段
hgetall myhash # hgetall 用于返回哈希表中,所有的字段和值
HDEL myhash field1 # hdel 用于删除哈希表 key 中的一个或多个指定字段
HLEN myhash # hlen 获取哈希表中字段的数量
HEXISTS myhash field2 # hexists 查看哈希表的指定字段是否存在。
HKEYS myhash # hkeys 获取哈希表中的所有域(field)。
HVALS myhash # hvals 返回哈希表所有域(field)的值。
HINCRBY myhash field1 2 # hincrby 为哈希表中的字段值加上指定增量值。
HSETNX myhash field2 100 # 为哈希表中不存在的的字段赋值
Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。
存储部分变更的数据,如用户信息等。
11、有序集合Zset
在set基础上,加一个score值。之前set是k1 v1 v2 v3,现在zset是 k1 score1 v1 score2 v2
zadd myset 1 one 2 two 3 three # zadd 将一个或多个成员元素及其分数值加入到有序集当中
zrange key 0 -1 # zrange 返回有序集中,指定区间内的成员
zrevrange key 0 -1 # zrange 返回有序集中,指定区间内的成员
zrangebyscore key 分数值min 分数值max # zrangebyscore 返回有序集合中指定分数区间的成员列表。(升序)
ZREVRANGEBYSCORE key 分数值min 分数值max # ZREVRANGEBYSCORE 返回有序集合中指定分数区间的成员列表(降序)。
zrem key kuangshen # zrem 移除有序集中的一个或多个成员
zcard key # zcard 命令用于计算集合中元素的数量。
zcount key # zcount 计算有序集合中指定分数区间的成员数量。
# zrank 返回有序集中指定成员的排名。其中有序集成员按分数值递增(从小到大)顺序排列
# zrevrank 返回有序集中成员的排名。其中有序集成员按分数值递减(从大到小)排序。
zrank salary kuangshen
ZREVRANK salary kuangshen # 狂神第三
3、三种特殊数据类型
GEO 地理位置
Redis 的 GEO特性在 Redis 3.2 版本中推出, 这个功能可以将用户给定的地理位置信息储存起来, 并对这些信息进行操作。来实现诸如附近位置、摇一摇这类依赖于地理位置信息的功能。geo的数据类型为zset
GEO 的数据结构总共有六个常用命令:geoadd、geopos、geodist、georadius、eoradiusbymember、gethash
官方文档:https://www.redis.net.cn/order/3685.html
geoadd key longitude(经度) latitude(维度) name # 将给定的空间元素(纬度、经度、名字)添加到指定的键里面。
geopos key member [member...] ## 从key里返回所有给定位置元素的位置(经度和纬度)
geodist key member1 member2 [unit] # 返回两个给定位置之间的距离,如果两个位置之间的其中一个不存在,那么命令返回空值。
georadius key longitude latitude radius m|km|ft|mi [withcoord][withdist][withhash][asc|desc][count count] # 以给定的经纬度为中心, 找出某一半径内的元素
georadiusbymember key member radius m|km|ft|mi [withcoord][withdist][withhash][asc|desc][count count] # 找出位于指定范围内的元素,中心点是由给定的位置元素决定
geohash key member [member...] # Redis使用geohash将二维经纬度转换为一维字符串,字符串越长表示位置更精确,两个字符串越相似表示距离越近。
zrem china:city beijin # GEO没有提供删除成员的命令,但是因为GEO的底层实现是zset,所以可以借用zrem命令实现对地理位置信息的删除.
HyperLogLog
Redis 在 2.8.9 版本添加了 HyperLogLog 结构。
Redis HyperLogLog 是用来做基数统计的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定 的、并且是很小的。
在 Redis 里面,每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基数。这和计算基数时,元素越多耗费内存就越多的集合形成鲜明对比。
HyperLogLog则是一种算法,它提供了不精确的去重计数方案。
举个栗子:假如我要统计网页的UV(浏览用户数量,一天内同一个用户多次访问只能算一),传统的解决方案是使用Set来保存用户id,然后统计Set中的元素数量来获取页面UV。但这种方案只能承载少量用户,一旦用户数量大起来就需要消耗大量的空间来存储用户id。我的目的是统计用户数量而不是保存用户,这简直是个吃力不讨好的方案!而使用Redis的yperLogLog最多需要12k就可以统计大量的用户数,尽管它大概有0.81%的错误率,但对于统计UV这种不需要很精确的数据是可以忽略不计的
什么是基数
比如数据集 {1, 3, 5, 7, 5, 7, 8}, 那么这个数据集的基数集为 {1, 3, 5 ,7, 8}, 基数(不重复元素)为5。
基数估计就是在误差可接受的范围内,快速计算基数。
命令 | 描述 |
---|---|
PFADD key element [element …] | 添加指定元素到 HyperLogLog 中。 |
PFCOUNT key [key …] | 返回给定 HyperLogLog 的基数估算值。 |
PFMERGE destkey sourcekey[sourcekey …] | 将多个 HyperLogLog 合并为一个 HyperLogLog,并集计算 |
BitMap
在开发中,可能会遇到这种情况:需要统计用户的某些信息,如活跃或不活跃,登录或者不登录;又如需要记录用户一年的打卡情况,打卡了是1, 没有打卡是0,如果使用普通的 key/value存储,则要记录365条记录,如果用户量很大,需要的空间也会很大,所以 Redis 提供了 Bitmap 位图这中数据结构,Bitmap 就是通过操作二进制位来进行记录,即为 0 和 1;如果要记录 365 天的打卡情况,使用 Bitmap表示的形式大概如下:0101000111000111………………………,这样有什么好处呢?当然就是节约内存了,365 天相当于 365 bit,又 1 字节 = 8 bit , 所以相当于使用 46 个字节即可。BitMap 就是通过一个 bit 位来表示某个元素对应的值或者状态, 其中的 key 就是对应元素本身,实际上底层也是通过对字符串的操作来实现。Redis 从 2.2 版本之后新增了setbit, getbit, bitcount 等几个bitmap 相关命令。
SETBIT key offset value 设置 key 的第 offset 位为value (1或0)
GETBIT key offset 获取offset设置的值,未设置过默认返回0
bitcount key [start, end] 统计操作
4、Redis 事务
事务的概念
redis事务的本质是一组命令的集合。redis事务就是一次性、循序性、排他性的执行一个队列中的命令。
事务没有隔离级别的概念
批量操作在发送EXEC命令前放入队列缓存,并不会执行。
不保证原子性
- 开始事务
- 命令入队
- 执行事务
Redis事务的相关命令
watch key1 key2....#监视一或多个key,如果在事务执行之前,被监视的key被其他命令改动,则事务被打断 ( 类似乐观锁 )
multi # 标记一个事务块的开始
exec # 执行所有事务块的命令
discard # 取消事务。放弃事务块中的所有命令
unwatch # 取消watch对所有key的监控
悲观锁
顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会被上锁,这样别人想拿这个数据的时候就会block,直到他拿到锁。传统的关系型数据库里面就
用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在操作之前先上锁。
乐观锁
顾名思义,每次去拿数据的时候都认为别人不会修改,所以不会上锁。但是在更新的时候会判断再此期间有没有别人更新这个数据,可以使用版本号等机制,乐观锁使用于多读的应用场景,这样可以提高吞吐量,乐观锁策略:提交版本必须大于记录当前的版本号才能执行更新。
一但执行 EXEC 开启事务的执行后,无论事务使用执行成功, WARCH 对变量的监控都将被取消。故当事务执行失败后,需重新执行WATCH命令对变量进行监控,并开启新的事务进行操作。
小结
watch指令类似于乐观锁,在事务提交时,如果watch监控的多个KEY中任何KEY的值已经被其他客户端更改,则使用EXEC执行事务时,事务队列将不会被执行,同时返回Nullmulti-bulk应答以通知调用者事务执行失败