Redis详解

  • 磁盘, 寻址ms级别,带宽:G/M
  • 内存,寻址ns级别, 是磁盘的十万倍,带宽(双通道DDR400的带宽为6.4GBps)

硬盘的原理: https://www.bilibili.com/video/BV11a4y1x7PC?from=search&seid=15696094628094262938

磁盘的扇区大小通常为512byte,但是操作系统读取磁盘时,无论读取多少磁盘数据,都读取4k数据。

数据存储发展进程

  1. 早期数据存储到简单的文本文件中,使用grep等命令查找,由于没有固定的格式,所以查找困难

  2. 发展为数据库,数据库将数据拆分为多个 data page每个大小为4k,存放到磁盘中,正好为一次i/o。

    1. 关系型数据库在建表时,必须定义好schema,即必须定义好表字段以及字段类型,因为类型确认,所以数据宽度以及行的宽度也确认。新增字段采用行级存储,主动开拓一行数据的宽度,修改数据时,只需将对应占位的数据进行替换即可
    2. 建立索引,用于索引data page,记录某一字段所对应的data page,索引也是data page
    3. 建立B+T,保存在内存中,经过B+T查询最终确认叶子结点,从而确认索引的data page,根据索引查出数据记录

    当数据库表中的数据越来越多时, 增删改要进行树维护,所以会变慢。当查询请求不多时,其查询速度仍然很快。但是当大量查询请求进来时,磁盘的带宽将会严重影响数据库的查询速度。

  3. 由于磁盘的寻址时间,以及带宽对关系型数据库的影响,部分公司出现了极端化的情况:使用内存作为存储关系型数据库的介质:比如SAP公司的HANA内存级数据库,但是其造价昂贵

    数据存储在内存和磁盘的存储体积不同,2T的数据放入内存,可能也就占用1T多的空间

  4. 磁盘造价低廉但是性能较差,内存虽然性能高但是其造假昂贵,所以出现了折中方案: 使用磁盘关系型数据库+内存缓存,比如memcached+oracle,redis + mysql

Redis是什么

Remote Dictionary Server,翻译为远程字典服务。Redis 是一个完全开源免费的基于key-value的NoSQL存储系统。他是一个使用ANSI C语言编写、遵守BSD协议,支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。

它通常被称为数据结构服务器,因为值(value)可以是 字符串(String), 哈希(Map), 列表(list), 集合(sets) 和 有序集合(sorted sets) 五大类型

Redis作者是 Salvatore Sanfilippo,来自意大利的西西里岛。👇

2008年他在开发一个LLOOGG的网站时,需要一个高性能的队列功能,最开始使用MySQL来实现,无奈性能总是提不上去,所以决定自己实现一个专属于LLOOGG的数据库,他就是Redis的前身。后台 Sanfilippoj将 Redis1.0放到Github上大受欢迎。

BSD协议

Redis基于BSD开源协议,BSD开源协议是一个给于使用者很大自由的协议。可以自由的使用,修改源代码,也可以将修改后的代码作为开源或者专有软件再发布。当你发布使用了BSD协议的代码,或者以BSD协议代码为基础做二次开发自己的产品时,需要满足三个条件:

  • 如果再发布的产品中包含源代码,则在源代码中必须带有原来代码中的BSD协议。
  • 如果再发布的只是二进制类库/软件,则需要在类库/软件的文档和版权声明中包含原来代码中的BSD协议。
  • 不可以用开源代码的作者/机构名字和原来产品的名字做市场推广。

BSD代码鼓励代码共享,但需要尊重代码作者的著作权。BSD由于允许使用者修改重新发布代码,也允许使用或在BSD代码上开发商业软件发布和销售,因此是对商业集成很友好的协议。

很多的公司企业在选用开源产品的时候都首选BSD协议,因为可以完全控制这些第三方的代码,在必要的时候可以修改或者 二次开发。

Redis vs Memcache

世界上的数据只有三种表现形式:

  1. k=v
  2. k=[v1, v2,...]
  3. k={x=y, a=[1, 2], b={a,1}}

所以任何数据都可以以json的形式发送或者记录。Memchache是一个没有数据类型的缓存系统。它想要表示数据,通常需要客户端自己将数据转换为json。当我需要修改json的某个字段时,需要获取整个字段值,修改完毕后生成新的json,再发送给memcache服务端:

而拥有数据类型的redis可以通过数据类型提供的方法,直接修改某部分信息(向list k1头部插入数据):

Redis的优势计算向数据移动

  • 客户端代码更简洁,不需要做数据序列化反序列化处理
  • 网络传输数据较少,客户端和服务端只通讯修改的地方

安装redis

CentOS安装Redis5.0

官网下载源码包,解压,根据redeme中的提示按照步骤安装。注意,make是linux系统自带的c编译器,记得安装gcc:

yum install gcc

安装步骤:

# 编译,make命令会去当前路径寻找Makefile文件,执行编译过程
make
make install 
# 指定位置安装
make install PREFIX=/opt/redis/

CentOS7安装Redis6.0

# 安装依赖
yum -y install gcc centos-release-scl cmake wget tcl
yum -y install devtoolset-9-gcc devtoolset-9-gcc-c++ devtoolset-9-binutils
# 解决gcc版本低的问题-临时
scl enable devtoolset-9 bash
# 解决gcc版本低的问题-永久
echo "source /opt/rh/devtoolset-9/enable" >>/etc/profile
# 编译
make
# 安装, 默认安装到/usr/local/bin 目录下
make install
# 指定位置安装
make install PREFIX=/root/redis6

./utils/install_server.sh安装redis服务

在源码包下,有一个快速安装redis服务的脚本:./utils/install_server.sh,执行此脚本,会提示输入相应的参数去安装redis服务,或者指定相应的参数去直接安装redis服务:

sudo REDIS_PORT=6379 \
               REDIS_CONFIG_FILE=/etc/redis/6379.conf \
               REDIS_LOG_FILE=/var/log/redis_6379.log \
               REDIS_DATA_DIR=/var/lib/redis/6379 \
               REDIS_EXECUTABLE=`command -v redis-server` ./utils/install_server.sh

在redis6版本中,可能会出现如下错误:

This systems seems to use systemd.
Please take a look at the provided example service unit files in this directory, and adapt and install them. Sorry!

解决方案:修改脚本vi ./install_server.sh ,将下面代码注释代码

#bail if this system is managed by systemd
#_pid_1_exe="$(readlink -f /proc/1/exe)"
#if [ "${_pid_1_exe##*/}" = systemd ]
#then
#       echo "This systems seems to use systemd."
#       echo "Please take a look at the provided example service unit files in this directory, and adapt and install them. Sorry!"
#       exit 1
#fi

连接redis

redis-cli 是一个redis的命令行客户端工具,默认集成在redis中,可以使用redis-cli --help查看使用帮助,默认链接本地的6379的redis-server服务。

  • 使用交互方式连接redis:redis-cli -h {host} -p {port}
  • 使用命令行方式连接redis并执行命令:redis-cli -h 127.0.0.1 -p 6379 get hello

可以使用redis-cli --help来查看帮助。

应用场景

string 数值

  • redis是并发安全的,并且速度极快,可以使用 incr, decr等命令用于做秒杀、抢购、点赞等
  • 规避并发下,对数据库的事务操作,完全由redis内存操作代替(计算向数据移动)

string 位图

  • 数据类型string 位图,使用365位存储365天内的登录状态,然后使用bitcount统计用户在某一随机时间段内的登录次数,使用bitlen统计总共登录的次数(运算速度快、节省空间)
  • 数据类型string 位图,统计某一天的热活量,以天为单位,将用户映射到bitmap中,使用bitlen统计当天的热活。 如果统计1月1日、1月2日都登陆的用户,就可以使用逻辑与 bitop and rehuo k0101 k0102 所得的key rehuo就是1月1日与1月2日都登陆的用户id

help 命令

在redis客户端中,使用help命令,可以查看redis的各种命令帮助:

127.0.0.1:6379> help
redis-cli 6.0.10
To get help about Redis commands type:
      # 列出组下的命令
      "help @<group>" to get a list of commands in <group>
      # 查看某个命令的帮助
      "help <command>" for help on <command>  
      # 使用tab智能补全
      "help <tab>" to get a list of possible help topics
      # 使用quit退出redis客户端
      "quit" to exit

To set redis-cli preferences:
      ":set hints" enable online hints
      ":set nohints" disable online hints
Set your preferences in ~/.redisclirc

目前在redis6中的group包含:

  • generic 全局通用命令
  • string 数据类型string
  • list 数据类型list
  • set 数据类型set
  • sorted_set 数据类型sorted_set
  • hash 数据类型hash
  • pubsub
  • transactions
  • connection
  • server
  • scripting
  • hyperloglog
  • cluster
  • geo
  • stream

每个命令详情中,包含这个命令的相关信息, 命令形式、命令解释、起始版本、命令所属组, 下面这个命令属于generic组,自1.0.0版本就出现了:

KEYS pattern
summary: Find all keys matching the given pattern
since: 1.0.0
group: generic

数据类型

数据类型

127.0.0.1:6379> set key1 "Hello World"
OK
127.0.0.1:6379> set key2 100
OK

使用type查看数据类型:

127.0.0.1:6379> type key1
string
127.0.0.1:6379> type key2
string

数据类型与使用的命令的组对应。这里set 和 get 命令所属的组为 string。

内部编码

type命令返回对应value的数据结构类型,但是实际上每个数据结构在redis内部都有自己的多种编码实现,Redis会在适合的场景选择合适的内部编码,比如要进行数据计算,string的内部编码就是int:

image-20210221153606191

可以使用 object encoding命令查看内部编码:

127.0.0.1:6379> object encoding key1
"embstr"
127.0.0.1:6379> object encoding key2
"int"

内部编码优势

  1. 可以自由改进内部编码,从而不影响外部数据结构以及命令
  2. 多种内部编码可以在不同的场景下发挥各自的优势

string

help @string

字符串类型,其他数据结构都是基于字符串类型的基础上构建的,字符串类型可以是字符串、数字、二进制,其值最大不可以超过512M

内部编码

  • int:8字节长整型
  • embstr:小于等于39字节的字符串
  • raw:大于39字节的字符串
127.0.0.1:6379> set key1 100
OK
127.0.0.1:6379> set key2 hello
OK
127.0.0.1:6379> set key3 a12345678901234567890123456789012345678901234567890

127.0.0.1:6379> object encoding key1
"int"
127.0.0.1:6379> object encoding key2
"embstr"
127.0.0.1:6379> object encoding key3
"raw"

二进制安全

在redis中,所有数据都是二进制安全的。redis只会保存原始的二进制流数据,不会对他们做处理。即使value的内部编码不同,也不会影响数据数据在redis内部的存储方式,所谓的内部编码只不过在对数据处理时,将其转换为其他编码以方便处理。

所以,redis存储的数据的格式、文本编码等信息,需要客户端自己在存入和读取时保持一致。

127.0.0.1:6379> strlen key1   // int 100 
(integer) 3
127.0.0.1:6379> append key1 abc
(integer) 6
127.0.0.1:6379> get key1
"100abc"
127.0.0.1:6379> object encoding key1
"raw"

常用命令

常用:

  • SET 设置值
  • GET 获取值
  • MGET 获取多个值
  • APPEND 追加字符串
  • GETRANGE 取字符串指定的范围的值
  • SETRANGE 设置字符串指定范围的值
  • STRLEN 字符串长度
字符串操作
SET,GET
set key value [EX seconds|PX milliseconds|KEEPTTL] [NX|XX]

EX: expire time, 过期时间秒
PX: 过期时间,毫秒

NX: not exist, 不存在才设置,通常用于分布式锁
XX: 存在才修改,不存在返回nil
# 永不失效
set key1 value1 

# 一秒钟后失效
set key1 value1 EX 1

# 如果存在不设置
127.0.0.1:6379> set key2 value1 NX
OK
127.0.0.1:6379> set key2 value1 NX
(nil)

# 如果存在才设置(修改)
127.0.0.1:6379> set key3 value3 XX
(nil)
SETNX

与SET的 NX参数功能一致,如果存在则不设置:

127.0.0.1:6379> setnx k1 v1
(integer) 1
127.0.0.1:6379> setnx k1 v2
(integer) 0
SETEX

设置一个key的value,并且设置指定的有效期(单位秒):

# 设置k1=v1 有效期10s
setex k1 10 v1
SETRANGE GETRANGE
127.0.0.1:6379> set key1 "hello world"
OK
127.0.0.1:6379> get key1
"hello world"
# 正向索引
127.0.0.1:6379> GETRANGE key1 0 4
"hello"
# 反向索引,-7代表倒数第七个字符
127.0.0.1:6379> GETRANGE key1 0 -7
"hello"

127.0.0.1:6379> SETRANGE key1 6 zhangsan
(integer) 14
127.0.0.1:6379> get key1
"hello zhangsan"
MSET,MGET,MSETNX

m代表more, 一次设置多个值,一次获取多个值,原子操作

127.0.0.1:6379> mset a 1 b 2 c 3
OK
127.0.0.1:6379> mget a b c
1) "1"
2) "2"
3) "3"

msetnx,m代表more,一次设置多个值,如果存在则不设置,该操作为原子操作,如果有任意一个值没有设置成功,所有都不成功:

127.0.0.1:6379> set a 1
OK
127.0.0.1:6379> msetnx a 1 b 2 c 3
(integer) 0
127.0.0.1:6379> mget a b c
1) "1"
2) (nil)
3) (nil)
GETSET

注意,此命令不能保证原子性,因为是先读后写。此方法仅仅是为了减少一次IO操作(与先GET 后 SET 比较):

127.0.0.1:6379> getset key1 abc
"100abc"
127.0.0.1:6379> get key1
"abc"
STRLEN

获取字符串长度,实际上是流数据的字节数

127.0.0.1:6379> set a 张三
OK
127.0.0.1:6379> strlen a
(integer) 6
127.0.0.1:6379> set b 123
OK
127.0.0.1:6379> strlen b
(integer) 3
APPEND

追加字符串

127.0.0.1:6379> set a hello
OK
127.0.0.1:6379> append a " world"
(integer) 11
127.0.0.1:6379> get a
"hello world"
数值操作
INCRINCRBYDECRDECRBYINCRBYFLOAT
递增1递增指定整数递减1递减指定整数递增指定的浮点值
127.0.0.1:6379> set a 100
OK
127.0.0.1:6379> incr a
(integer) 101
127.0.0.1:6379> decr a
(integer) 100
127.0.0.1:6379> incrby a 10
(integer) 110
127.0.0.1:6379> decrby b 10
(integer) 113
127.0.0.1:6379> decrby a 10
(integer) 100
127.0.0.1:6379> incrbyfloat a 0.5
"100.5"
127.0.0.1:6379> incrbyfloat a -0.5
"100"
127.0.0.1:6379> incrby a -10
(integer) 90
127.0.0.1:6379> decrby a -10
(integer) 100
位操作
SETBITGETBITBITCOUNTBITPOSBITOP
设置位获取位计算位数量寻找位的offset位操作
127.0.0.1:6379> setbit bitkey 7 1     --> 设置位图第7位的值为1  0000 0001
(integer) 0
127.0.0.1:6379> getbit bitkey 7       --> 获取位图第7位的值
(integer) 1
127.0.0.1:6379> getbit bitkey 6
(integer) 0

--------> 因为位图最高位为第7位,八位一个字节,所以只占用一个字节
127.0.0.1:6379> strlen bitkey
(integer) 1

--------> 第8位超过了一个字节,所以使用两个字节存储
127.0.0.1:6379> setbit bitkey 8 1
(integer) 0
127.0.0.1:6379> strlen bitkey
(integer) 2

127.0.0.1:6379> bitcount bitkey 0 15     --> 计算第0位到第15位中,所有1的数量
(integer) 2
127.0.0.1:6379> BITPOS bitkey 0 0 15     --> 在第0位到第15位,寻找0位第一次出现的offset
(integer) 0
127.0.0.1:6379> BITPOS bitkey 1 0 15     --> 在第0位到第15位,寻找1位第一次出现的offset
(integer) 7

---------> 位操作
127.0.0.1:6379> setbit a 0 1             --> 10000 0000
(integer) 0
127.0.0.1:6379> setbit a 1 1             --> 11000 0000
(integer) 0
127.0.0.1:6379> setbit b 0 1             --> 10000 0000
(integer) 0
127.0.0.1:6379> setbit b 2 1             --> 10100 0000
(integer) 0
127.0.0.1:6379> bitop and andkey a b     --> 按位与  1000 0000
(integer) 1
127.0.0.1:6379> bitop or orkey a b       --> 按位或  1110 0000
(integer) 1
127.0.0.1:6379> bitop xor xorkey a b     --> 异或    0110 0000
(integer) 1
127.0.0.1:6379> bitop not notkey a       --> 非      0011 1111
(integer) 1

list

image-20210221180850973
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值