一、Redis基础

一、Redis基础

    1、数据类型

        (1)、String

            ①、概述

                String类型存储的是字符串(简单的字符串、复杂的字符串(xml、json)、数字(整数、浮点数)、二进制(图片、音频、视频))。String类型是二进制安全的,String类型是Redis最基本的数据类型,最大不能超过512M。Redis是由C语言编写的,以高效和轻量著称。C语言中的字符串是基于字符数组实现的。Redis并没有直接使用了C语言的字符串结构,而是对其做了一些封装,得到自己的简单动态字符串(simple dynamic string, SDS)的抽象类型。

struct sdshdr {
    // 用于记录buf数组中使用的字节的数目和SDS存储的字符串的长度相等
    int len;
    // 用于记录buf数组中没有使用的字节的数目
    int free;
    // 字节数组,用于储存字符串
    char buf[];
};

            ②、应用场景

                分布式锁、计数器(如知乎每个问题的被浏览器次数)、粉丝数、对象缓存存储、分布式全局唯一id(string)或变量。

            ③、String常用命令

keys *  # 查看所有的key
set name zhangsan # set key value,set成功返回OK
exists name # 判断当前的key是否存在,存在返回1,不存在返回0
move name # 移除当前key,成功返回1,不成功返回0
expire name 10 # 设置key的过期时间,单位是秒,设置成功返回1,不成功返回0
ttl name # 查看当前key的剩余时间
type name # 查看当前key的数据类型
setex key3 30 "hello" # 设置key3的值为hello,30秒后过期
setnx mykey "redis" # 如果mykey不存在,则创建mykey,返回1,如果存在,则创建失败返回0
mset k1 v1 k2 v2 k3 v3 # 同时设置多个值
mget k1 k2 k3 # 同时获取多个值
msetnx k5 v5 k6 v6 # msetnx是一个原子性操作,要么一起成功,要么一起失败
set user:1 {name:zhangsan, age:3} # 设置一个user:1对象,值为json字符来保存一个对象。
#这里key是一个巧妙的设计:user:{id}:{filed}
getset key1 value1 # 先get再set,如果不存在值,则返回nil。如果存在值,则获取原来的值,并设置新的值。
set views 0 # 设置key:views初始值为0
incr views # 自增1,浏览量+1变为1
decr views # 自减1,浏览量-1
incrby views 10 # 可以设置步长,指定增量

        (2)、Hash

            ①、概述

                Hash是一个键值(key=>value)对集合,Redis Hash是一个String类型的field和value的映射表,特别适合用于存储对象。

            ②、应用场景

                a、Hash类型的(key, field, value),以用户id为key,商品id为field,商品数量为value,恰好构成了购物车的3个要素。

                b、Hash可以用来存储对象,string + json也是存储对象的一种方式,当对象的某个属性需要频繁修改时,不适合用string+json,因为它不够灵活,每次修改都需要重新将整个对象序列化并赋值,如果使用hash类型,则可以针对某个属性单独修改,没有序列化,也不需要修改整个对象。比如,商品的价格、销量、关注数、评价数等可能经常发生变化的属性,就适合存储在hash类型里。 

            ③、Hash常用命令

hset myhash name zhangsan # 在hash中set一个具体的key-value
hget myhash name # 获取myhash中的name的值
hdel myhash name # 删除myhash中的name的值
hmset myhash name zhangsan age 11 # set多个key-value值
hmget myhash name age # 获取多个字段属性值
hlen myhash # 获取hash表中元素数量
hexists myhash name # 判断hash中指定字段是否存在
hgetall myhash # 获取myhash中全部key-value
hkeys myhash # 只获取所有的key
hvals myhash # 只获取所有的value

        (3)、List

            ①、概述

                列表是字符串列表,按照插入顺序排序。List实际上是一个链表,left、right都可以插入值,如果key不存在,创建新的链表,插入值,如果存在,插入值。如果移除了所有值,为空链表,也代表不存在。在两边插入或者改动值效率最高,中间相对来说会低一些。

            ②、应用场景

                a、作为消息队列(lpush   rpop),从左边插入,右边移除。

                b、作为栈(lpush  lpop),从左边插入,左边移除。

                c、新浪/Twitter用户消息列表(list)。

            ③、List常用命令

lpush lista one # 将一个或多个值,插入到名为lista的列表头部(头部为左侧,尾部为右侧)
rpush lista one # 讲一个或多个值,插入到列表的尾部(右)
lrange lista 0 -1 # 获取lista中的值
lrange lista 0 1 # 获取lista中区间的具体值
lpop lista # 移除lista中的第一个元素(左侧头部)
rpop lista # 移除lista中的最后一个元素(右侧尾部)
lindex lista 1 # 获得lista中下标为1的元素
llen lista # 返回列表lista的长度
lrem lista 1 one # 把lista中的元素为one的元素,移除一个
ltrim lista 1 2 # 截取lista中下标1-2的元素,重新赋值给lista
rpoplpush lista listb # 移除lista列表的最后一个元素到新的listb中

        (4)、Set

            ①、概述

                Set是String类型的无序集合,且不允许重复的成员。通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。

            ②、应用场景

                a、抽奖活动列表、点赞列表、签到列表。

                b、共同关注、共同爱好、二度好友等实现,将A用户所有关注的人放在一个set集合中,将A用户的粉丝也放在一个set集合中,通过sinter计算两个set的交集。

            ③、Set常用命令

sadd myset "hello" # set集合中添加元素
smembers myset # 查看myset中的所有元素值
sismember myset hello # 判断某一个值在set集合中是否存在,存在返回1,不存在返回0
scard myset # 获取set集合中的元素个数
srem myset hello # 从myset集合中移除元素hello
srandmember myset 2 # 从myset中随机抽取2个元素
sdiff set1 set2 # 输出set1和set2的差集
sinter set1 set2 # 输出set1和set2的交集
sunion set1 set2 # 输出set1和set2的并集

        (5)、zset

            ①、概述

                String类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。zset的成员是唯一的,但分数(score)却可以重复。

            ②、应用场景

                a、(排行版)排行榜、好友列表、去重、历史记录等。

                b、班级成绩排序、工资排序等可以应用zset排序

                c、存储消息,普通消息score为1,重要消息score为2,带权重进行判断

                d、在排行榜上获取topN

            ③、zset常用命令 

zadd salary 2000 zhangsan # 添加一个值,salary zset名称,2000 score,zhangsan 元素值
zadd salary 1000 lisi 5000 wangwu # 添加多个值
zrem salary zhangsan # 移除有序集合中的指定元素zhangsan
zcard salary # 获取有序集合中的个数
zrangebyscore salary -inf +inf # 显示全部用户,从小到大显示(zset会根据score进行排序,score即2000、1000、5000)
zrangebyscore salary -inf +inf withscores # 显示所有用户并且附带score
zrangebyscore salary -inf 2500 whthscores # 显示工资小于2500的员工

        (6)、geospatial

geoadd china:city 116.40 39.30 beijing # 添加北京的地理位置 经度 维度
getpos china:city beijing # 获取之前存的北京的地理位置

        (7)、Hyperloglog

            基数统计的算法,集合 a b c 和集合 c d f 合并后的基数集合为  a b c d f 。基数统计的优点是占用的内存是固定,2^64不同的元素的个数,只需要12KB内存。但是该算法存在0.81%错误率,如果统计网页浏览量该错误率可以忽略不计。

pfadd mykey1 a b c d e f g h i j # 创建第一组元素mykey1
pfcount mykey1 # 统计mykey1元素的基数数量
pfmerge mykey3 mykey1 mykey2 # 合并两组 mykey1 mykey2 => mykey3并集

        (8)、Bitmaps

            Bitmaps位图,是一种数据结构,都是操作二进制位来进行记录,就只有0和1两个状态。主要用于两个状态的数据,都可以使用bitmaps,例如统计用户登录、未登录;活跃、不活跃;统计上班是否打卡等。

setbit sign 0 1 # 以打卡为例,每天打卡存储到bitmaps中,名称为sign,0表示周一,1表示状态已打卡
setbit sign 1 0 # 周二未打卡
setbit sign 2 0 # 周三已打卡
getbit sign 2 # 查看周三是否打卡,返回1为已打卡,返回0为未打卡
bitcount sign # 统计这周的打卡记录,查看是否全勤

    2、为什么Redis单线程还那么快?

        (1)、redis是基于内存的,内存的读写速度非常快,官方给出的QPS10W+;

        (2)、redis是单线程的,省去了很多上下文切换线程的时间,单线程机制使得Redis内部实现的复杂度大大降低,Hash的惰性Rehash、Lpush等等“线程不安全”的命令都可以无锁进行;

        (3)、redis使用多路复用技术,可以处理并发的连接;

            多路指的是多个Socket连接,复用指的是复用一个线程。多路复用主要有三种技术:Select,Poll,Epoll。

            Epoll是最新的也是目前最好的多路复用技术。采用多路I/O复用技术可以让单个线程高效的处理多个连接请求(尽量减少网络IO的时间消耗),且Redis在内存中操作数据的速度非常快(内存内的操作不会成为这里的性能瓶颈),主要以上两点造就了 Redis 具有很高的吞吐量。

        (4)、Redis单线程和多线程

            ①、其实,Redis4.0开始就有多线程的概念了,比如Redis通过多线程方式在后台删除对象、以及通过Redis模块实现的阻塞命令等。

            ②、Redis的瓶颈并不在CPU,而在内存和网络。内存不够的话,可以加内存或者做数据结构优化和其他优化等,但网络IO的读写在Redis整个执行期间占用了大部分的CPU时间,如果把网络处理这部分做成多线程处理方式,那对整个Redis的性能会有很大的提升。

            ③、多线程可以充分利用服务器CPU资源,目前主线程只能利用一个核。多线程任务可以分摊Redis同步IO读写负荷。

            ④、官方建议:4核的机器建议设置为2或3个线程,8核的建议设置为6个线程,线程数一定要小于机器核数,尽量不超过8个。

            ⑤、开启多线程后,仍然不会存在线程并发安全问题,Redis的多线程部分只是用来处理网络数据的读写和协议解析,执行命令仍然是单线程顺序执行。

    3、常用命令

        Redis命令不区分大小写。

del key # 在key存在时删除key
exists key # 检查key是否存在
expire key 60 # 为key设置过期时间为60【秒】
pexpire key 60 # 为key设置过期时间为60【毫秒】
ttl key # 以【秒】为单位返回key的剩余时间
pttl key # 以【毫秒】为单位返回key的剩余时间

    4、事务

        Redis事务本质就是一组命令的集合,一个事务中的所有命令都会被序列化,在事务执行过程中会按照顺序执行。Redis事务没有隔离级别的概念,单条命令能够保证原子性,但是整个事务不能保证原子性。Redis不支持回滚。

multi # 开启事务
set k1 v1 # set各种命令入队,成功返回Queue
set k2 v2 # 各种命令入队,成功返回Queue
...
exec # 执行事务
discard # 取消事务,事务队列中的命令不被执行

            ①、编译异常,命令中途出现一处命令代码有错误,事务中的所有命令都不会执行。

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

    5、乐观锁

        Redis的乐观锁通过watch来实现,通过监视某个对象。

set money 100 # 设置money值为100
set out 0 # 设置out值为0
watch money # 开启事务前先监视money对象
multi # 开启事务
decrby money 20 # money减20
incrby out 20 # out加20
exec # 执行事务,money值中途没被修改过,执行成功,返回OK;money值中途被修改过,执行失败,返回nil。
unwatch # 如果发现事务执行失败,需要先解锁,然后获取新的money值,重新watch监视,类似于select version

            ①、如果执行的时候会比较监视的对象的最新值是否发生变化,如果没变执行成功。如果事务执行之前,有另外一个线程修改了money值,事务就会执行失败。需要unwatch之后重新watch,然后重新exec。应用场景:秒杀

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值