Featured image of post redis学习

redis学习

本文阅读量

适者生存!一定要逼着自己学习,这是在这个社会生存的唯一法则!

nosql概述

为什么要用nosql

大数据时代:一般的数据库无法进行分析处理了!

1、单机mysql的年代

90年代,一个基本的网站访问量一般不会太大,单个数据库完全足够!那个时候,更多的去使用静态网页 html~ 服务器根本没有太大的压力!

思考一下,这种情况下:整个网站的瓶颈是什么?

1、数据量太大、一个机器放不下了!

2、数据的索引(B+Tree),一个机器内存也放不下

3、访问量(读写混合),一个服务器承受不了了

只要你开始出现以上的三种情况之一,那么你就必须要晋级!

2、Memcached(缓存) +mysql + 垂直拆分(读写分离)

网站80%的情况下都是在读,每次都要去查询数据库的话就十分的麻烦!所以我们希望减轻服务器的压力,我们可以使用缓存来保证效率

发展过程:优化数据结构和索引 -> 文件缓存(IO) ->Memcached(当时最热门的技术!)

3、分库分表 + 水平拆分 + mysql集群

技术和业务在发展的同时,对人的要求也越来越高!

本质:数据库(读,写)

mysql引擎:

MyISAM:表锁,十分影响效率!高并发下就会出现严重的锁问题!

Innodb:行锁

慢慢的就开始使用分库分表来解决写得压力!Mysql在哪个年代推出了表分区

MySQL的集群,很好满足那个年代的所有需求!

4、如今最近的年代

2010–2020 十年之间,世界已经发生了翻天覆地的变化。

MySQL等关系型数据库就不够用了!数据量很多,变化很快!

MySQL使用它来存储一些比较打的文件,博客,图片!数据库表很大,效率很低!如果有一种数据库来专门处理这种数据 MySQL的压力就变得十分小(研究如何解决这些问题)大数据I0压力下,表几乎没法更大!

目前一个互联网公司

为什么要用NoSQL!

用户 个人信息,社交网络,地理位置。用户自己产生的数据,用户日志等等爆发式增长!

这时候我们就需要使用NoSQL数据库,NoSQL可以很好的处理以上的问题!

什么是nosql

NoSql = not only sql (不仅仅是sql) 泛指非关系型数据库

关系型数据库: 表格,行,列

随着web2.0 互联网的诞生!传统的关系型数据库很难对付web2.0时代!尤其是超大规模的高并发的社区!暴露出来很多难以克服的问题,NoSQL在当今大数据环境下发展的十分迅速,Redis是发展最快的,而且是我们当下必须要掌握的一个技术!

很多的数据类型,用户的个人信息,社交网络,地理位置。这些数据类型的存储不需要一个固定的格式!不需要多余的操作就可以横向扩展的!

nosql特点

1、方便拓展(数据之间没有关系,很多拓展!)

2、大数据量,高性能(Redis 1s可以写8W次,读取11W,nosql的缓存,记录级的,是一种细粒度的缓存,性能会比较高!)

3、数据类型是多样型的!(不需要事先设计数据库!随取随用!如果是数据量十分大的表,很多人就无法设计了!)

传统的RDBMS 和 Nosql的区别

传统的RDBMS

结构化组织

sql

数据和关系都存在单独的表中

严格的一致性

基础的事务

NOSQL

不仅仅是数据

没有固定的查询语言

键值对存储,列存储,文档存储,图形数据库(社交关系)

最终一致性

CAP定理和BASE (异地多活) 初级架构师!

高可用,高性能,高可拓

了解3V+3高

大数据时代的3V: 主要是描述问题的

1、海量Volume

2、多样Variety

3、实时Velocity

大数据时代的3高:主要是对程序的要求

1、高并发

2、高可拓

3、高性能

真正在公司中的实践:NoSQL+RDBMS一起使用才是最强的。

技术没有高低之分,就看你如何使用(思维的提高!)

nosql的四大分类

kv键值对

  • 新浪:redis
  • 美团:redis+tail
  • 阿里、百度:redis+memecache

文档型数据库(bson格式和json一样)

  • MongoDB(一般必须要掌握)

    MongoDB是一个基于分布式文件存储的数据库,C++编写,主要用来处理大量的文档!

    MongoDb是一个介于关系型数据库和非关系型数据库中间的产品!MongoDB是非关系型数据库中功能最丰富,最像关系型数据库的!

列存储的数据库

  • HBase
  • 分布式文件系统

图关系数据库

  • 它不是存储图片的,放的是关系,比如:朋友社交网络,广告推荐!
  • Neo4j

Redis入门

redis是什么?

redis(Remote Dictionary Server),即远程字典服务。是一个开源的使用C语言编写、支持网络、可基于内存亦可持久化的日志型、key-value数据库,并提供多种语言的api。

因为值(value)可以是 字符串(String), 哈希(Hash), 列表(list), 集合(sets) 和 有序集合(sorted sets)等类型,也被人们称为结构化数据库!

redis能干什么?

1、内存存储、持久化,内存中是断电即失的,所以持久话很重要(rdb、dof)

2、效率高,可以用于高速缓存

3、发布订阅系统

4、地图信息分析

5、计时器、计数器(浏览量!)

特性

1、多样的数据类型,不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储。

2、持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用

3、集群

4、事务

安装

linux安装

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# 1、下载安装包!
# 2、解压redis的安装包! 程序都放到/opt目录下
[root@snailsir home]# mv redis-5.0.8.tar.gz /opt
[root@snailsir home]# cd /opt
[root@snailsir opt]# tar -zxvf redis-5.0.8.tar.gz

# 3、基本的环境安装
yum install gcc-c++
# 4、安装
cd redis-5.0.8
make
make install

# 5、复制配置文件
cp /opt/redis-5.0.8/redis.conf /usr/local/bin/redis

# 6、启动redis
redis-server /usr/local/bin/redis/redis.conf

问题

安装6.0 出错

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
server.c:5151:94: error: 'struct redisServer' has no member named 'unixsocket'
             serverLog(LL_NOTICE,"The server is now ready to accept connections at %s", server.unixsocket);
                                                                                              ^
server.c:5152:19: error: 'struct redisServer' has no member named 'supervised_mode'
         if (server.supervised_mode == SUPERVISED_SYSTEMD) {
                   ^
server.c:5153:24: error: 'struct redisServer'' has no member named 'masterhost'
             if (!server.masterhost) {
                        ^
server.c:5166:15: error: 'struct redisServer' has no member named 'maxmemory'
     if (server.maxmemory > 0 && server.maxmemory < 1024*1024) {
               ^
server.c:5166:39: error: 'struct redisServer' has no member named 'maxmemory'
     if (server.maxmemory > 0 && server.maxmemory < 1024*1024) {
                                       ^
server.c:5167:176: error: 'struct redisServer' has no member named 'maxmemory'
         serverLog(LL_WARNING,"WARNING: You specified a maxmemory value that is less than 1MB (current value is %llu bytes). Are you sure this is what you really want?", server.maxmemory);
                                                                                                                                                                                ^
server.c:5170:31: error: 'struct redisServer' has no member named 'server_cpulist'
     redisSetCpuAffinity(server.server_cpulist);

错误原因:

gcc版本问题,新版本的。redis6.0以上

1
2
# 查看gcc版本
gcc -v

解决办法

1
2
3
4
5
6
7
8
9
#升级到 5.3及以上版本
yum -y install centos-release-scl
yum -y install devtoolset-9-gcc devtoolset-9-gcc-c++ devtoolset-9-binutils
 
scl enable devtoolset-9 bash
 
#注意:scl命令启用只是临时的,推出xshell或者重启就会恢复到原来的gcc版本。
#如果要长期生效的话,执行如下:
echo "source /opt/rh/devtoolset-9/enable" >>/etc/profile

性能测试

redis-benchmark是一个压力测试工具!

官方自定的性能测试工具!

redis-benchmark命令参数:

redis 性能测试工具可选参数如下所示:

序号 选项 描述 默认值
1 -h 指定服务器主机名 127.0.0.1
2 -p 指定服务器端口 6379
3 -s 指定服务器 socket
4 -c 指定并发连接数 50
5 -n 指定请求数 10000
6 -d 以字节的形式指定 SET/GET 值的数据大小 2
7 -k 1=keep alive 0=reconnect 1
8 -r SET/GET/INCR 使用随机 key, SADD 使用随机值
9 -P 通过管道传输 请求 1
10 -q 强制退出 redis。仅显示 query/sec 值
11 –csv 以 CSV 格式输出
12 -l 生成循环,永久执行测试
13 -t 仅运行以逗号分隔的测试命令列表。
14 -I Idle 模式。仅打开 N 个 idle 连接并等待。

我们来简单测试下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#测试 100个并发连接,  每个并发100000请求
redis-benchmark -h localhost -p 6379 -c 100 -n 100000

#********* set *********#
100000 requests completed in 1.68 seconds # 对我们的10w个请求进行写入测试
100 parallel clients  # 100个并发客户端
3 bytes payload       # 每次写入3个字节
keep alive: 1         # 只有一台服务器来处理这些请求,单机性能

29.03% <= 1 milliseconds
99.90% <= 2 milliseconds
100.00% <= 2 milliseconds  # 所有请求在3毫秒处理完成
59382.42 requests per second # 每秒处理59382.42次请求

redis的基础知识

redis默认有16个数据库,可以通过配置文件配置

1
databases 16

默认使用的是第0个

可以使用select进行切换数据库

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# 设置过期时间 毫秒# 重命名key
rename old_key new_key # 如何new_key存在的话,old_key的值会覆盖new_key值
renamenx old_key new_key # 不会覆盖,存在返回0,不存在返回1127.0.0.1:6379> select 3 #切换数据库
OK
127.0.0.1:6379[3]> DBSIZE #查看DB大小
(integer) 0


127.0.0.1:6379> keys * #查看所有key
1) "mylist"

# 匹配[]内某个字符
keys sit[ey] # 可以匹配 site或sity

# 匹配单个字符
keys si?e 


# 随机返回一个key
randomkey

# 删除一个key
del keyname

# 重命名key
rename old_key new_key # 如何new_key存在的话,old_key的值会覆盖new_key值
renamenx old_key new_key # 不会覆盖,存在返回0,不存在返回1

# set key
set name snailsir 

#查看当前key的类型
type name

#判断当前key是否存在
exists name

#将name的key从当前空间移动到1空间内
move name 1

#设置key过期时间,单位是s
expire name 10 

#查看当前key的剩余时间,单位s
ttl name

# 设置过期时间 毫秒
pexpire key 9000

# 查看生命周期剩余时间,毫秒
pttl key


#清除当前数据库
127.0.0.1:6379> flushdb  
OK
127.0.0.1:6379> keys *
(empty list or set)

#清空所有数据库
127.0.0.1:6379> flushall  
OK

redis是单线程的

明白redis是很快的,官方表示,redis是基于内存操作,CPU不是redis性能瓶颈,Redis的瓶颈是根据机器的内存和网络带宽,既然可以使用单线层来实现,就使用单线程了!

为什么redis单线程还这么快?

1、误区1:高性能的服务器一定是多线程的?

2、误区2:多线程(CPU上下文会切换!)一定比单线程效率高! cpu>内存>硬盘的速度

核心:redis是将所有的数据全部放在内存中的,所以说使用单线程去操作效率就是最高的,多线程(CPU上下文切换:耗时的操作!),对于内存系统来说,如果没有上下文切换效率就是最高的!多次读写都是在一个CPU上的,在内存情况下,这个就是最佳方案!

redis五大数据类型

特殊数据类型

geospetial 地理位置

朋友的定位,附近的人,打车距离计算?

Redis 的 Geo 这个功能可以推算地理位置的信息,两地之间的距离,方圆几里的人

可以查询一些测试数据:http://www.jsons.cn/lngcodeinfo/0706D99C19A781A3

官方文档: https://www.redis.net.cn/order/3685.html

geoadd 添加地理位置

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#规则:两极无法直接添加,我们一般会下载城市数据,直接通过程序一次性导入!
#参数 key 值(维度 经度 名称)
127.0.0.1:6379> geoadd china:city 116.40 39.90 beijing
(integer) 1
127.0.0.1:6379> geoadd china:city  121.47 31.23 shanghai 106.50 29.53 chongqing 115.05 22.52 shenzhen 120.15 30.28 hangzou 125.14 42.92 xian
(integer) 5

#有效的经度从-180度到180度。
#有效的纬度从-85.05112878度到85.05112878度。
#当坐标位置超出上述指定范围时,该命令将会返回一个错误
127.0.0.1:6379> geoadd china:city 39.90 116.40 beijing
(error) ERR invalid longitude,latitude pair 39.900000,116.400000

geopos 获取指定城市的经度和维度

1
2
3
4
5
127.0.0.1:6379> geopos china:city beijing chongqing
1) 1) "116.39999896287918091"
   2) "39.90000009167092543"
2) 1) "106.49999767541885376"
   2) "29.52999957900659211"

geodist 两人经纬度的直线距离

单位:

  • m
  • km
  • mi 英里
  • ft 英尺
1
2
3
4
127.0.0.1:6379> geodist china:city beijing shanghai #默认是米
"1067378.7564"
127.0.0.1:6379> geodist china:city beijing shanghai km
"1067.3788"

georadius 以给定的经纬度为中心,找出某一半径内的元素

我附近的人?(获得所有附近的人的地址,定位!)通过半径来查询!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
127.0.0.1:6379> georadius china:city 110 30 1000 km #以100 30这个经纬度为中心,寻找方圆1000km内的城市
1) "chongqing"
2) "shenzhen"
3) "hangzou"
127.0.0.1:6379> georadius china:city 110 30 500 km
1) "chongqing"
127.0.0.1:6379> georadius china:city 110 30 500 km withdist #显示到中心距离的位置
1) 1) "chongqing"
   2) "341.9374"
127.0.0.1:6379> georadius china:city 110 30 500 km withcoord #显示他人的定位信息
1) 1) "chongqing"
   2) 1) "106.49999767541885376"
      2) "29.52999957900659211"
      
127.0.0.1:6379> georadius china:city 110 30 500 km withcoord count 1 #筛选出指定的结果
1) 1) "chongqing"
   2) 1) "106.49999767541885376"
      2) "29.52999957900659211"

georadiusbymember 找出位于指定元素周围的其他元素

1
2
3
4
5
6
127.0.0.1:6379> georadiusbymember china:city beijing 1000 km
1) "beijing"
2) "xian"
127.0.0.1:6379> georadiusbymember china:city shanghai 400 km
1) "hangzou"
2) "shanghai"

geohash 返回一个或多个位置元素的getohash表示

该命令将返回11个字符的geohash字符串!

1
2
3
4
# 将二维的经纬度转换为一维的字符串,如果两个字符串越接近,那么则越近
127.0.0.1:6379> geohash china:city beijing chongqing
1) "wx4fbxxfke0"
2) "wm5xzrybty0"

geo 底层的实现原理其实就是zset! 我们可以使用zset命令来操作geo!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
127.0.0.1:6379> zrange china:city 0 -1 #查看地图中全部元素
1) "chongqing"
2) "shenzhen"
3) "hangzou"
4) "shanghai"
5) "beijing"
6) "xian"

127.0.0.1:6379> zrem china:city beijing #移除指定元素
(integer) 1
127.0.0.1:6379> zrange china:city 0 -1
1) "chongqing"
2) "shenzhen"
3) "hangzou"
4) "shanghai"
5) "xian"

Hyperloglog

基数:不重复的元素,可以接受误差!

Hyperloglog 用来做基数统计的算法!

网页的uv(一个人访问一个网站多次,但是还是算做一个人)

传统方式:set保存用户的id,然后就可以统计set中的元素数量作为标准判断!

这种方式如果保存大量的用户id,就会比较麻烦!我们的目的是为了计数,而不是保存用户id;

优点:

占用内存是固定的,2^64不同的元素的基数,只需要12kb内存!如果要从内存角度来比较的话,hyperloglog是我们首选。

0.81%错误率!统计uv任务,可以忽略不计的!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
127.0.0.1:6379> pfadd mykey a b c d e f g h i j #创建第一组元素
(integer) 1
127.0.0.1:6379> pfcount mykey #统计mykey元素的基数数量
(integer) 10
127.0.0.1:6379> pfadd mykey2 i j z x c v b n m #创建第二组元素 mykey2
(integer) 1
127.0.0.1:6379> pfcount mykey2
(integer) 9
127.0.0.1:6379> pfmerge mykey3 mykey mykey2 #合并两组 mykey mykey2 =》 mykey3 并集
OK
127.0.0.1:6379> pfcount mykey3 #查看并集数量
(integer) 15

如果允许容错,那么一定可以使用Hyperloglog

如果不允许容错,就使用set或者自己的数据类型即可!

bitmap

位存储(两个状态)操作二进制位来记录,就只有0和1两个状态!

统计疫情感染人数: 0 0 0 1 0

统计用户信息,活跃,不活跃

登录、未登录

打卡 365天 365bit

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
# 记录周一到周末的打卡
127.0.0.1:6379> setbit sign 0 1 #周一打卡
(integer) 0
127.0.0.1:6379> setbit sign 1 0 #周二未打卡
(integer) 0
127.0.0.1:6379> setbit sign 2 0
(integer) 0
127.0.0.1:6379> setbit sign 3 1
(integer) 0
127.0.0.1:6379> setbit sign 4 1
(integer) 0
127.0.0.1:6379> setbit sign 5 0
(integer) 0
127.0.0.1:6379> setbit sign 6 0
(integer) 0

127.0.0.1:6379> getbit sign 6 # 查看星期天是否打卡
(integer) 0
 
 127.0.0.1:6379> bitcount sign #统计这周打卡天数,就可以看是否全勤
(integer) 3
使用 Hugo 构建
主题 StackJimmy 设计