1. Redis简介

1 基础介绍

  1. 从磁盘或读取数据时,主要时间浪费在寻址上,磁盘的寻址速度为ms级,而内存寻址速度为ns级,差了1000000倍,因此从内存中读取数据效率比从硬盘中读取高的多
  2. 磁盘有磁道和扇区,一扇区512字节,操作系统无论一次需要读取多少字节,硬盘都至少每次返回4k个字节
  3. 最初数据存放在文件中,可以通过grep、awk命令,甚至java程序去读文件,文件越大,读取越慢,硬盘I/O为瓶颈
  4. 后来数据库出现,使用如下方案解决查找数据慢的问题
    1. data page:数据库中,数据最小的存储单位,大小为4K,oracle中称为block,8k。数据库将数据分成很多个4K大小的数据块,与硬盘每次读取大小相同,这样不会浪费I/O。可以想象如果data page为1k,那么为了读取1k,还是需要从硬盘拿4K,浪费很多资源。定义为4K的倍数,就都不会造成浪费
    2. 索引:也是4k,相当于将指定的列以B*树的方式存放起来,并存放该列的值指向的data page。当查询语句的where条件命中了索引,就会先将B+树的树干读入内存,然后依次找到所有需要的树的叶子结点的data page,最终将真正存放数据的data page读到内存,也就找到了具体数据
    3. schema:关系型数据库建表时,必须给出schema,即必须给出表有多少列,每个列的类型是什么。给出类型就确定了该列数据的字节宽度。一个表每行数据的总宽度就定死了。当将数据存放于data page中时,就算某列值为null,也会为其占位,这样未来增删改时,就不用移动数据,直接用新的数据覆盖原来的位置即可
  5. 当数据库中数据量非常大时
    1. 对于有索引的少量查询,速度依然很快
    2. 但并发大的时候,会受硬盘带宽影响,比如来了一万个查询,查的都是不同的数据块,那么这一万个4K,需要依次被读入内存,但由于内存有限,一部分数据就需要等待前面的4K被内存读取完,才能轮到自己,导致速度降低
  6. 因此将一少部分数据拿出来,放入到内存,这就是缓存。memcached ,redis都是分布式缓存系统,也就是将数据存放在内存中
  7. 介绍各种类型数据库特点、热度的网站:https://db-engines.com/en/
  8. redis官方网站:http://redis.cn/https://redis.io/
  9. redis与memcached区别
    1. memcached中的value没有类型的概念,redis有
    2. 当想获取lists中的某个值,对于memcached,需要获取表示这个lists的整个jason,然后在客户端解析。由于需要将整个lists传输给客户端,IO量非常大,服务器上的网卡IO会成为瓶颈。同时客户端需要解码程序
    3. 但对于redis,lists类型有自己的方法,可以直接获取lists中的某个值,不需要将整个lists对应的数据都取走,因此不会再有IO瓶颈
    4. 尽量保证计算发生在存放数据的一端,从而避免大量IO,这叫做计算向数据移动。此时,为得到jason中的一个属性值,客户端不必将数据先拿到客户端再处理,而是直接在数据存放处发生计算,最后仅将结果返回客户端
  10. redis使用单线程单实例来处理数据,因此每个连接中的命令是顺序到达的,不同客户端的连接,命令顺序不能保证。多个client连接到操作系统内核后,会产生很多socket,为保证并发,内核会使用epoll这种非阻塞多路复用的系统调用方式调用redis进程

2 redis安装

  1. 环境:centos6.x+redis5.x
  2. 安装流程
#1. 为linux安装wget下载插件,方便后续从网络下载redis安装包
yum install wget  linux
#2. 在当前用户下建立soft目录,方便存放redis
#3. 进入soft目录,下载redis压缩包
wget http://download.redis.io/releases/redis-5.0.5.tar.gz
#4. 解压压缩包,该命令会直接解压成redis-5.0.5
tar xf redis-5.0.5.tar.gz
#5. 在redis安装包中,执行make,make是编译工具,类似javac,但make不是只面向一个语言
make
#6. 如果安装centos时没安装gcc,会报错,需要先安装gcc
yum install  gcc
#7. 由于之前make了一半,因此需要先将原来make一半的内容清理掉
make distclean
#8. 此时生成了src文件夹,里面是可执行文件,redis此时已经可以通过redis-server启动了,但只是作为一个程序,并不是一个服务,即开机时不会自动启动,因此需要使用/home/wusihan/soft/redis-5.0.5/utils下的install_server.sh来将redis安装为服务
	#1. 将可执行程序,牵出到指定的路径下,不再和源码混在一起
make install PREFIX=/opt/mashibing/redis5
	#2. 将可执行程序添加到环境变量
vi /etc/profile
export  REDIS_HOME=/opt/mashibing/redis5
export PATH=$PATH:$REDIS_HOME/bin
	#3. 由于配置文件在磁盘,不在内存,需要将新文件加载到内存,使配置生效
source /etc/profile
	#4. 将redis安装为服务
		#1. 会让你依次输入服务端口号、配置文件位置、日志文件位置、数据文件位置(虽然是内存数据库,但为防止掉电后数据丢失,需要在硬盘中也存放一份数据)、redis可执行程序路径(如果环境变量中有,默认使用环境变量中的,如果环境变量中没有,可以人为指定)
		#2. 全输入成功后,redis会首先建立一个config配置文件,放在指定路径,然后将一个名为redis_端口号的脚本放在/etc/init.d目录中,linux的开机启动实际上就是从这个目录中加载所有脚本。最后将redis服务启动
		#3. 可以使用service redis_端口号 status查看服务状态确实已经启动
		#4. 可以多次执行install_server.sh在不同端口号上建立多个redis实例
/root/redis-5.0.5/utils/install_server.sh

3 redis的客户端连接与帮助文档

  1. redis默认提供了16个库,序号从0到15,连接时,可以指定连接哪个redis实例中的第几号库,默认连接6379端口对应实例上的0号库
redis-cli 
#连接指定实例上的指定库
redis-cli -p 6380 -n 8
#切换到0号库
select 0
  1. 帮助文档
#1. 查看组中的所有命令,或命令的详情
help 组/命令
#2. redis中可以使用tab将内容自动补全和切换,例如输入help @后按tab键,就会依次切换所有的@开头的内容
#3. 查看string组下所有命令
help @string
#4. 查看SETBIT命令的详情,summary中有对该命令的简单功能描述,group为string,表示该命令只能用于string组
help SETBIT

4 redis中value的数据类型

4.1 string
4.1.1 字符串操作
set key value
get key
#1. 表示必须新建key,即key存在时,设置失败,一般应用于一堆人设置key,谁抢到算谁的,其他人失败
set key value nx
#2. 表示必须更新,即key存在时,设置成功
set key value xx
#3. 同时设置多组key value
mset k1 value1 k2 value2
#4. 同时取出多组key的value值
mget k3 k4
#5. 为k1的value追加word
append k1 " word"
#6. 与substr功能相同,值得注意的是,对于string类型,有正向索引和反向索引,hello中的o的索引既可以用正向索引4表示,又可以使用反向索引-1表示
getrange k1 6 10
#7. 索引6往后,替换成mashibing
setrange k1 6 mashibing
#8. 查看string的长度
strlen k1
#9. 返回老value,并设置新value,通讯上就发了一个命令,减少io通信
getset k1 mashibing
#10.同时新增多组key value,但如果其中一个新增失败,所有都新增失败,是原子的
msetnx k1 value1 k2 value2
4.1.2 数值操作
#1. 查看k1的value值的类型,key中存放一个type属性,用于描述value类型,此处显示string,key中还有length属性,用于保存字段长度,这样就不用每次重新统计value长度
type k1
#2. 发现set的组为string,因此set只用于为string类型赋值
help set
#3. key中还有一个encondifng属性
	#1. 这是为了防止每次都需要判断该value是否能进行加减操作,如果之前做过数值计算,就标记为int,表示一定能+1,下次计算直接累加即可
	#2. 对于string类型的value,有三种值,embstr、int、raw,如果k1当时设置的是99,那么enconding值为int,如果为正常字符串,值为embstr
object enconding k1
#4. 自增1,只适用于encoding为int的string
	#1. 一般用于抢购,秒杀,可以规避并发下对数据库的事物操作
	#2. 银行里的钱这种必须持久化和有事务的数据不适合使用redis	
incr k1
#5. 加22
incrby k1 22
#6. 自减1
decr k1
#7. 减22
decrby k1 22
#8. 增加0.5
incrbyfloat k1 0.5
#9. 清空数据库,生产环境一般rename,防止库误被清空
flushall
#10. 查看所有key
keys *
4.1.3 bitmap操作
#1. 设置bit,这里的3,是二进制位的索引,而不是字节的索引
	#1. 二进制索引,是从0开始,第一个字节中到7,第二个字节中从8开始
	#2. 本条表示将二进制位的第三位设为1,也就是00010000,当get k1时,会得到这个二进制转为数值后,对应的ascii码的符号
	#3. 由于一个字节已经可以表示该值,因此此时value长度为1
setbit k1 3 1
#2. 在linux中查看查看ascii列表
man ascii
#3. 长度变为2,因为一个字节最多存到7,此时get k1时,会先显示前8位对应的ascii,再显示后8位对应的ascii
setbit k1 9 1 
#4. ascii为基础字符集,其他字符集都叫做扩展字符集,扩展的意思就是指,其他字符集不再对ascii中存在的字符重新编码。ascii字符集中所有字符转为字节后的bit位中,第一位必须为0,因此可以想像,无论是自己写程序去解析字节流,还是redis,都是先判断第一位
	#1. 如果是0,直接去ascii中找这8bit对应的字符
	#2. 如果为1,先判断是否以redis-cli --raw的方式连接到redis客户端
		#1. 如果不是,直接以字节对应的十六进制表示法显示
		#2. 如果是,尝试将字节转换为当前客户端的字符集,先看该字节的bit表示中,前几个bit是1,因为前面有几个1,就表示当前字节流中,是由多少个字节来表示一个字符,例如utf-8,前3个bit都是1,表示用三个字节来一起表示一个字符。此时应该连续取出三个字节,然后将前面的1去掉,剩下的部分,组成一个新的字节,然后redis再将这个新的字节表示的值,去客户端现在的编码集中去查找对应的字符,从而得到最后应该显示的字符
#5. 查看二进制位1,第一次在k1的value的起始字节索引到终止字节索引间(前后都包含)出现的位置的二进制索引是几。当然这个字节索引,可以用反向索引
bitpos k1 1 起始字节索引 终止字节索引
#6. 查看从起始字节索引到终止字节索引的字符串,的二进制表示法中,出现几次1
bitcount k1 起始字节索引 终止字节索引
#7. 按位操作
setbit k1 1 1
	#a. 此时k1就是A
setbit k1 7 1
setbit k2 1 1
	#b. 此时k2就是B
setbit k2 6 1 
	#c. 将k1和k2按位与的操作结果,放入andkey,值就是01000000 00000000
bitop and andkey k1 k2
	#d. orkey值为C
bitop or orkey k1 k2
#8. bit在生产中的操作
	#1. 公司有用户系统,需要在随机日期间,统计某用户登录天数
setbit sean 1 1
setbit sean 7 1
setbit sean 364 1
STRLEN sean
BITCOUNT sean -2 -1
	#2. 统计随机日期间,登陆过的用户总人数,需去重
	#每个用户的id对应一个bit位
setbit 20190101 1 1
setbit 20190102 1 1
setbit 20190102 7 1
	#统计20190101与20190102这两天所有活动过的用户并去重
bitop or destkey 20190101 20190102 
	#0到-1,表示所有字节
bitcount destkey 0 -1
4.1.4 redis的二进制安全性
  1. redis二进制安全,是指redis只关心二进制化的字符串,不关心具体格式。即只会严格的按照二进制的数据存取,不会妄图以某种特殊格式解析数据。客户端必须先将数据转为字节数组,才能将数据给到redis
  2. 由于二进制安全,所以使用redis时,一定在用户层沟通好,用什么字符集,防止A客户端放入的数据,B取出时有问题
  3. strlen用于查看value占多少个字节,对于123,1会转为ascii对应的字节进行存放,因此123为3位,而对于汉字,reids会将汉字按当前字符集转为字节,再存放起来,如果当前客户端字符集为utf-8编码,而汉字在utf-8中为3字节,所以汉字的长度为3。如果客户端字符集为gbk,长度为2
  4. 连接客户端时,可以指定redis尝试以当前客户端字符集来显示存放的字节
#1. 如果不这样连接客户端,那么get key时候,如果字节无法转为ascii,就会直接显示该字节的二进制码对应的十六进制值,以\xxx格式显示
redis-cli --raw
4.2 list
  1. value为list的key中有head和tail属性,指向双向链表的头和尾
  2. help @list:查看关于list的所有操作
  3. redis中,所有命令基本上,基本都是以其类型的首字母开头,比如对于list中,有命令llength、linsert
  4. list中的命令有一些l开头的和r开头的比较特殊,表示对链表的左侧或右侧进行操作,例如lpush表示从左侧插入,rpush表示从右侧插入
4.2.1 模拟栈和队列
#1. 从左插入链表,即双向链表中,以f e d c b a形式存放
lpush k1 a b c d e f
#2. 从右侧插入
rpush k2 a b c d e f
#3. 弹出最左侧元素'f'
lpop k1
#4. 模拟栈操作:同向命令,lpush后lpop
lpush k1 1
lpush k1 2
lpop k1
#5. 模拟队列操作:反向命令,lpush后rpop
4.2.2 模拟数组
#1. 返回list中,索引从0到-1的所有元素。l不再是左边的意思,而是list的首字母,list中的索引也是从0开始,并同时拥有正向索引和反向索引
lrange k1 0 -1
#2. 根据索引取出元素
lindex k1 -1
#3. 将索引3处元素改为xxx
lset k1 3 xxx
#4. 移除2个a,如果为正数,表示从左往右移除,负数表示从右往左移除
lrem k3 2 a
#5. 第一个元素6后插入a
linsert k3 after 6 a
#6. 第一个元素3前面插
linsert k3 before 3 a
#7. 查看list中元素个数
llen k1
#8. 保留索引2到-2的元素,其他删除
ltrim k1 2 -2
4.2.3 模拟阻塞队列
#1. 从k1中取元素,取不到就阻塞,0表示一直阻塞,如果>0表示阻塞多少s
blpop k1 0
#2. 有其他客户端放入集合中元素后,才解除阻塞。如果多个客户端阻塞,会按阻塞先后顺序解除阻塞,FIFO
4.3 hash
  1. hash这种value类型,类似java中的HashMap,也就是说整个键值对为Map<String,Map>

  2. 命令都是H开头,help @hash查看帮助文档

  3. 如果没有hash,可通过如下方式实现HashMap

    set sean::name zzl
    set sean::age 18
    #1. 当客户端想获取某个key下的value中的所有value时,需要先通过keys sean*查出所有内容,再将结果分别mget,需要客户端多次与服务端通信,浪费IO
    keys sean*
    mget sean::name sean::age
    
  4. 相关操作

#1. Map<sean,Map<name,zzl>>
hset sean name zzl
#2. 同时为value设置多个k-v对
hmset sean age 18 address bj
#3. 获取sean.name的值
hget sean name
#4. 获取sean.name与sean.age
hmget sean name age
#5. 获取hash中所有key
hkeys sean
#6. 取出hash中所有value
hvals sean
#7. 取出所有键值对,结果会按键、值、键、值...进行排列
hgetall sean
#8. 将sean.age增加0.5,age的值在操作前后,只要有一个是小数,就应该使用HINCRBYFLOAT,而不是HINCRBY
hincrbyfloat sean age 0.5
4.4 set
#1. 批量加入元素
sadd k1 value1 value2 value3
#2. 查看所有元素
smembers k1
#3. 移除元素
srem k1  value1 value2
#4. 实现交集、并集等集合操作
sadd k2 1 2 3 4 5
sadd k3 4 5 6 7 8
#a. 取交集,结果为4 5
sinter k2 k3
#b. 带store,表示将结果存放在dest中。带store这种命令也是为了防止来回移动数据,如果没有该命令,客户端就先必须获取交集的结果,然后在将交集结果传回服务端进行存储
sinterstore dest k2 k3
#c. 并集,也可以使用sunionstore
sunion k2 k3
#d. 差集,从前面的集合中刨除后面的集合
#从k2中刨除k3
sdiff k2 k3
#从k3中刨除k2
sdiff k2 k3
#5. 随机取出n个元素
	#1. 如果n>0:表示取出一个去重的结果集,如果n大于集合中不重复元素数量,返回整个集合的去重集合
	#2. 如果n<0:取出一个带有重复的结果集,一定满足你要的数量n
	#3. 如果n=0:什么都不返回
srandmemeber k1 4
#6. 随机取出一个元素,取出的元素会从集合中删除
spop k1
4.5 sorted_set
  1. help @sorted_set
  2. sorted_set底层用跳表实现
  3. sorted_set会按分值(score)从小到大排序,如果不想按分值进行排序,可以将score设置为同一个值,此时会按value值的字典顺序排序
  4. 因为set中的命令已经是s开头,因此sorted_set的相关命令改用最后一个英文字母z开头
#1. 为k1中批量添加元素,添加元素同时必须给定分值
zadd k1 8 apple 2 banana 3 orange  
#2. 按索引显示集合中元素,如果加上withscores表示同时显示分值,会按value1,score1,value2,score2这样显示
zrange k1 0 -1 withscores
#3. 按分值显示集合中元素
zrangebyscore k1 3 8 
#4. 按分值从低到高,显示前两个元素(和上面一样)
zrange k1 0 1 
#5. 按分值从高到低,取出前两个,注意和zrange k1 -2 -1命令不同,zrange返回banana、orange,而zrevrange返回orange、banana
zrevrange k1 0 1 
#6. 根据元素取分值
zscore k1 apple
#7. 根据元素取排名,其实就是取apple的索引
zrank k1 apple
#8. 为元素增加分值,一般用于歌曲排行榜,不用再读取数据库了,直接从redis中读取即可
zincrby k1 2.5 banana
#9. 实现交集、并集等集合操作,但要注意对分值的处理
zadd k1 80 tom 60 sean 70 baby
zadd k2 60 tom 100 sean 40 yiming 
#a. 2表示2个key,zuninonstore命令默认情况下,会对两个集合中相同元素,分数做加和
zuninonstore unkey 2 k1 k2 
#b. 会将分数乘权重再加和,第一个集合中元素权重为1,第二个集合中权重为0.5
zuninonstore unkey1 2 k1 k2 weights 1 0.5
#c. aggregate:配置分数的聚合方式,max表示保留最大的分数
zuninonstore unkey1 2 k1 k2 aggregate max
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值