电商平台 lnmp 架构之 mysql 高速缓存--redis

Posted 123坤

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了电商平台 lnmp 架构之 mysql 高速缓存--redis相关的知识,希望对你有一定的参考价值。

1. redis的介绍

  • Redis 是一个 nosql(not only sql不仅仅只有sql) 数据库,翻译成中文叫做非关系型型数据库。Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动分区(Cluster)提供高可用性(high availability)
  • redis 的应用场景
    1)热点数据的缓存
    由于redis访问速度块、支持的数据类型比较丰富,所以redis很适合用来存储热点数据,另外结合expire,我们可以设置过期时间然后再进行缓存更新操作,这个功能最为常见,几乎所有的大型项目都有所运用。
    2)限时业务的运用
    redis中可以使用expire命令设置一个键的生存时间,到时间后redis会删除它。利用这一特性可以运用在限时的优惠活动信息、手机验证码等业务场景。
    3)计数器相关问题
    redis由于incrby命令可以实现原子性的递增,所以可以运用于高并发的秒杀活动、分布式序列号的生成、具体业务还体现在比如限制一个手机号发多少条短信、一个接口一分钟限制多少请求、一个接口一天限制调用多少次等等。
    4) 排行榜相关问题
    关系型数据库在排行榜方面查询速度普遍偏慢,所以可以借助redis的SortedSet进行热点数据的排序。
    5) 分布式锁
    在并发进程中,我们通过锁(lock),来避免由于竞争而造成的数据不一致问题。通常,我们结合Lock的过期时间防止程序死锁。
    6)延时操作
    在用户提交订单后我们占用了库存,10分钟后去检验用户是够真正购买,如果没有购买将该单据设置无效,同时还原库存。所以我们对于上面的需求就可以用以下解决方案,我们在订单生成时,设置一个key,同时设置10分钟后过期, 我们在后台实现一个监听器,监听key的实效,监听到key失效时将后续逻辑加上。 当然我们也可以利用rabbitmq、activemq等消息中间件的延迟队列服务实现该需求。
    7)分页、模糊搜索
    redis的set集合中提供了一个zrangebylex方法, 这个方法可以返回字典区间的数据,利用这个特性可以进行模糊查询功能,这个也是目前我在redis中发现的唯一一个支持对存储内容进行模糊查询的特性。假如数据60万左右,响应时间在700ms左右,比mysql的like查询稍微快一点,但是由于它可以避免大量的数据库io操作,所以总体还是比直接mysql查询更利于系统的性能保障。
    8)点赞、好友等相互关系的存储
    Redis set对外提供的功能与list类似是一个列表的功能,特殊之处在于set是可以自动排重的,当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择,并且set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list所不能提供的。 又或者在微博应用中,每个用户关注的人存在一个集合中,就很容易实现求两个人的共同好友功能。
    9)队列
    由于redis有list push和list pop这样的命令,所以能够很方便的执行队列操作。

2. redis服务的安装

此处直接从官网 https://redis.io/download下载最新版的来操作。

主节点

[root@server1 ~]# tar zxf redis-6.2.1.tar.gz 
[root@server1 ~]# cd redis-6.2.1	#由于已经有了 Makefile ,直接可以make
[root@server1 redis-6.2.1]# ll Makefile 
-rw-rw-r-- 1 root root 151 Mar  2 14:14 Makefile
[root@server1 redis-6.2.1]# make	
#make成功后会在src文件夹下产生一些二进制可执行文件,包括redis-server、redis-cli.
[root@server1 redis-6.2.1]# make install

make 过程中有什么报错,提示缺失的东西时,便要依次解决,直至成功。

安装完成之后,用原始的办法启动;

[root@server1 ~]# mkdir /etc/redis
[root@server1 ~]# mkdir /var/lib/redis
[root@server1 redis-6.2.1]# cp redis.conf /etc/redis/6379.conf
[root@server1 redis-6.2.1]# ll /etc/redis/6379.conf
-rw-r--r-- 1 root root 92222 Jun 27 20:57 /etc/redis/6379.conf

  75 bind 0.0.0.0 
  
 247 daemonize yes

 444 dir /var/lib/redis
[root@server1 utils]# cp redis_init_script /etc/init.d/redis_6379	#启动脚本
[root@server1 ~]# /etc/init.d/redis_6379 start
[root@server1 ~]# ps ax

 9721 ?        Ssl    0:00 /usr/local/bin/redis-server 0.0.0.0:6379
[root@server1 redis]# redis-cli
127.0.0.1:6379> info
# Server
redis_version:6.2.1
redis_git_sha1:00000000

# Replication
role:master
connected_slaves:0

从节点

由于主节点之前安装过 mysql,gcc 已经安装过,此处从节点需要先安装gcc,然后在make;如果先make ,没有gcc,即使安装好,也不能make,因为源码不够纯净。

[root@server2 ~]# yum install -y gcc
[root@server2 redis-6.2.1]# make
[root@server2 redis-6.2.1]# make install
[root@server2 redis-6.2.1]# mkdir /etc/redis
[root@server2 redis-6.2.1]# mkdir /var/lib/redis
#完成之后,将server1上改好的启动脚本复制过来即可

[root@server1 init.d]# scp redis_6379 server2:/etc/init.d/
[root@server1 redis]# scp 6379.conf  server2:/etc/redis/
[root@server2 redis-6.2.1]# /etc/init.d/redis_6379 start
[root@server2 init.d]# cd /etc/redis/
[root@server2 redis]# vim 6379.conf 

 468 replicaof 172.25.25.1 6379
[root@server2 redis]# /etc/init.d/redis_6379 stop
Stopping ...
Redis stopped
[root@server2 redis]# /etc/init.d/redis_6379 start
Starting Redis server...
[root@server2 redis]# redis-cli 
127.0.0.1:6379> info

# Replication
role:slave
master_host:172.25.25.1
master_port:6379
master_link_status:up

测试:

[root@server1 ~]# redis-cli 
127.0.0.1:6379> set name westos
OK
127.0.0.1:6379> get name
"westos"
127.0.0.1:6379> 

[root@server2 redis]# redis-cli 
127.0.0.1:6379> get name
"westos"
127.0.0.1:6379> 

redis 做slave 时,是只读的,当做为slave 时,所有数据均来自master ;保持有持久化。

以上的方式是原始的启动方式,接下来用systemd的方式来启动;
再用systemd 的方式时,make 时需要加上参数make USE_SYSTEMD=yes

[root@server1 utils]# pwd
/root/redis-6.2.1/utils
[root@server1 utils]# cp systemd-redis_server.service /usr/lib/systemd/system/redis_server.service
[root@server1 utils]# cd /usr/lib/systemd/system
#systemd 的方式需要更改这个
 18 [Unit]
 19 Description=Redis data structure server
 20 Documentation=https://redis.io/documentation
 21 #Before=your_application.service another_example_application.service
 22 AssertPathExists=/var/lib/redis
 23 Wants=network-online.target
 24 After=network-online.target
 25 
 26 [Service]
 27 #ExecStart=/usr/local/bin/redis-server --supervised systemd --daemonize no
 28 ## Alternatively, have redis-server load a configuration file:
 29 ExecStart=/usr/local/bin/redis-server /etc/redis/6379.conf
 30 ExecStop=/usr/local/bin/redis-cli -p 6379 shutdown
 31 ExecReload=/bin/kill -s HUP $MAINPID
 32 LimitNOFILE=10032
 33 NoNewPrivileges=yes
 34 #OOMScoreAdjust=-900
 35 PrivateTmp=yes
 36 Type=notify
 37 #TimeoutStartSec=infinity
 38 #TimeoutStopSec=infinity
 39 #UMask=0077
 40 #User=redis
 41 #Group=redis
 42 WorkingDirectory=/var/lib/redis

[root@server1 redis]# vim 6379.conf 
#除了以上改文件的内容还要再改一个这个
 265 supervised auto
[root@server1 system]# systemctl daemon-reload
[root@server1 system]# systemctl start redis_server.service 

3. Redis常用命令

redis支持的数据类型:
字符串(strings)
散列(hashes)
列表(lists)
集合(sets)
有序集合(sorted sets)

指令含义
config get *查看配置
select 1选择数据库,默认有16个库
flushdb清空当前数据库
flushall清空所有数据库
move key 1移动key
del key删除
rename oldkey newkey改名
expire key 10设置过期时间
persist key设置持久化
keys user*查询
exists key判断是否存在

4. Redis异步复制

redis主从复制:
slaveof 172.25.0.11 6379
min-slaves-to-write <slave 数量>
min-slaves-max-lag <秒数>
Redis 使用异步复制,因此无法确保 slave 是否实际接收到给定的写命令

[root@server1 redis]# redis-cli 
127.0.0.1:6379> CONFIG GET min-slaves-max-lag
1) "min-slaves-max-lag"
2) "10"		##默认是10s
127.0.0.1:6379> CONFIG GET min-slaves-to-write
1) "min-slaves-to-write"
2) "0"	##默认最小是0

以下三个节点全部安装redis,并且配置为systemd的启动方式;除了第一台之外,其他的再用源码包编译时需要加参数make USE_SYSTEMD=yes;也可以直接将1编译的二进制程序拷贝过去。

[root@server1 redis]# mv 6379.conf redis.conf
[root@server1 redis]# vim /usr/lib/systemd/system/redis_server.service 

 29 ExecStart=/usr/local/bin/redis-server /etc/redis/redis.conf
[root@server1 redis]# vim /etc/redis/redis.conf 

2014 min-slaves-to-write 1
[root@server1 redis]# systemctl daemon-reload
[root@server1 redis]# systemctl reload redis_server.service

[root@server1 system]# cd /usr/local/bin/
[root@server1 bin]# rsync * -a server3:/usr/local/bin/
[root@server1 ~]# scp redis.conf server3:/etc/redis/
[root@server1 ~]# scp redis_6379 server3:/etc/init.d/

做好一主两从之后,接下来就需要做高可用。

5. Redis高可用

Redis Sentinel 为Redis提供了高可用的实现。通俗来说就是你可以部署一套无需人为干预即可防灾的Redis环境。RS同时为客户端提供了其他诸如监控,通知的功能。

  1. 监控: Redis Sentinel 时刻监控主从是否在正常工作。
  2. 通知: 当某个Redis实例出现问题,Redis Sentinel 可以通知系统管理员或者通过API通知其他程序。
  3. 自动切换: 如果一个主实例失效,Redis Sentinel 会启动一个失效转移(从升级为主)的进程,其他的从节点将重新跟随新的主节点。连接到RedisServer的会被通知切换到新的地址。
  4. 配置提供者: Redis Sentinel 充当了客户端服务自动发现的提供者:连接到Sentinal的客户端,Sentianl会响应最新的主节点地址给客户端,并且当发生转移的时候会发送通知。

配置

[root@server1 ~]# cd redis-6.2.1
[root@server1 redis-6.2.1]# cp sentinel.conf /etc/redis/
[root@server1 redis-6.2.1]# cd /etc/redis/
[root@server1 redis]# ls
redis.conf  sentinel.conf
[root@server1 redis]# vim sentinel.conf 

 84 sentinel monitor mymaster 172.25.25.1 6379 2	#指向master 
 
125 sentinel down-after-milliseconds mymaster 10000	#超时10s
[root@server1 redis]# scp sentinel.conf server2:/etc/redis/	#改完以后将数据同步,启动之后数据会变化
sentinel.conf                                               100%   13KB   9.4MB/s   00:00    
[root@server1 redis]# scp sentinel.conf server3:/etc/redis/
sentinel.conf                                               100%   13KB  10.3MB/s   00:00  

使用:直接用命令来启动

[root@server1 ~]# redis-sentinel /etc/redis/sentinel.conf 
3792:X 28 Jun 2021 20:56:42.294 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
3792:X 28 Jun 2021 20:56:42.294 # Redis version=6.2.1, bits=64, commit=00000000, modified=0, pid=3792, just started
3792:X 28 Jun 2021 20:56:42.294 # Configuration loaded
3792:X 28 Jun 2021 20:56:42.295 * Increased maximum number of open files to 10032 (it was originally set to 1024).
3792:X 28 Jun 2021 20:56:42.295 * monotonic clock: POSIX clock_gettime
                _._                                                  
           _.-``__ ''-._                                             
      _.-``    `.  `_.  ''-._           Redis 6.2.1 (00000000/0) 64 bit
  .-`` .-```.  ```\\/    _.,_ ''-._                                   
 (    '      ,       .-`  | `,    )     Running in sentinel mode
 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 26379
 |    `-._   `._    /     _.-'    |     PID: 3792
  `-._    `-._  `-./  _.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |           http://redis.io        
  `-._    `-._`-.__.-'_.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |                                  
  `-._    `-._`-.__.-'_.-'    _.-'                                   
      `-._    `-.__.-'    _.-'                                       
          `-._        _.-'                                           
              `-.__.-'                                               

3792:X 28 Jun 2021 20:56:42.295 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
3792:X 28 Jun 2021 20:56:42.302 # Sentinel ID is fea135a7e203f3e895aece3ddc0ee4844a186d83
3792:X 28 Jun 2021 20:56:42.302 # +monitor master mymaster 172.25.25.1 6379 quorum 2
3792:X 28 Jun 2021 20:56:42.303 * +slave slave 172.25.25.2:6379 172.25.25.2 6379 @ mymaster 172.25.25.1 6379
3792:X 28 Jun 2021 20:56:42.308 * +slave slave 172.25.25.3:6379 172.25.25.3 6379 @ mymaster 172.25.25.1 6379

#当再启一个sentinel时,所有节点是时时同步的
4438:X 28 Jun 2021 20:57:50.220 * +sentinel sentinel fea135a7e203f3e895aece3ddc0ee4844a186d83 172.25.25.1 26379 @ mymaster 172.25.25.1 6379

此时三个节点都开启sentinel ,再开一个server1的终端,来模拟主从切换。

[root@server1 ~]# redis-cli 
127.0.0.1:6379> SHUTDOWN
not connected> 
在server1下线之后10s后会选出新的 master,在server1端sentinel的页面可以看到切换的过程。此时的master 是server3。

3792:X 28 Jun 2021 21:04:37.497 # +switch-master mymaster 172.25.25.1 6379 172.25.25.3 6379
3792:X 28 Jun 2021 21:04:37.497 * +slave slave 172.25.25.2:6379 172.25.25.2 6379 @ mymaster 172.25.25.3 6379
3792:X 28 Jun 2021 21:04:37.497 * +slave slave 172.25.25.1:6379 172.25.25.1 6379 @ mymaster 172.25.25.3 6379
3792:X 28 Jun 2021 21:04:47.574 # +sdown slave 172.25.25.1:6379 172.25.25.1 6379 @ mymaster 172.25.25.3 6379
#+sdown表示主观下线状态

此时可以远程连接serve2和server3来查看是否已经切换过来,然后可以再开启server1,此时serer1自动为slave状态。并且配置信息也会自动更改过来。

[root@server1 ~]# redis-cli -h 172.25.25.3
172.25.25.3:6379> info

# Replication
role:master
connected_slaves:1
min_slaves_good_slaves:1
slave0:ip=172.25.25.2,port=6379,state=online,offset=145738,lag=0
master_failover_state:no-failover

此时让server1在此上线,然后再查看
# Replication
role:master
connected_slaves:2
min_slaves_good_slaves:2
slave0:ip=172.25.25.2,port=6379,state=online,offset=174042,lag=0
slave1:ip=172.25.25.1,port=6379,state=online,offset=174042,lag=0

除了自动故障切换和通知之外,还会做相应的配置更改。

[root@server1 ~]# redis-cli 
127.0.0.1:6379> CONFIG GET replicaof
1) "replicaof"
2) "172.25.25.3 6379"
127.0.0.1:6379> 
#自动更改配置信息
[root@server1 ~]# tail -n3 /etc/redis/redis.conf 
save 60 10000
user default on nopass ~* &* +@all
replicaof 172.25.25.3 6379

由于 redis 的数据完全来自 master,当 master 没有启动时,slave 是启动不了的,因为它会在一直连接master。

之前做的是一主两从的架构,接下来先将主从结构都转换为master,只需删除配置文件/etc/redis/redis.conf中的 replicaof的信息即可将从变为master;然后就可以做多组的主从结构,让 sentinel 可以多监控几个master。

6. twemproxy分片代理

官方地址:https://github.com/twitter/twemproxy

  • Twemproxy 是一种代理分片机制,由Twitter开源。Twemproxy作为代理,可接受来自多个程序的访问,按照路由规则,转发给后台的各个 Redis 服务器,再原路返回。该方案很好的解决了单个Redis实例承载能力的问题。当然,Twemproxy本身也是单点,需要用Keepalived 做高可用方案。通过 Twemproxy 可以使用多台服务器来水平扩张 redis 服务,可以有效的避免单点故障问题。虽然使用Twemproxy需要更多的硬件资源和在redis性能有一定的损失(twitter测试约20%),但是能够提高整个系统的HA也是相当划算的。twemproxy不光可以代理 redis,还可以代理 memcached。
  • 集群
    解决方案:
    由于 Redis 出众的性能,其在众多的移动互联网企业中得到广泛的应用。Redis 在 3.0 版本前只支持单实例模式,虽然现在的服务器内存可以到 100GB、200GB 的规模,但是单实例模式限制了 Redis 没法满足业务的需求。Redis 到 2015 年才发布正式版集群。各大企业在 3.0 版本还没发布前为了解决 Redis 的存储瓶颈,纷纷推出了各自的 Redis 集群方案。这些方案的核心思想是把数据分片(sharding)存储在多个 Redis 实例中,每一片就是一个 Redis 实例。
  • 下面介绍 Redis 的集群方案。
  1. 客户端分片
    客户端分片是把分片的逻辑放在 Redis 客户端实现,通过 Redis 客户端预先定义好的路由规则,把对 Key 的访问转发到不同的 Redis 实例中,最后把返回结果汇集。
    客户端分片的好处是所有的逻辑都是可控的,不依赖于第三方分布式中间件。开发人员清楚怎么实现分片、路由的规则,不用担心踩坑。

  2. 客户端分片方案有下面这些缺点

    这是一种静态的分片方案,需要增加或者减少 Redis 实例的数量,需要手工调整分片的程序。
    可运维性差,集群的数据出了任何问题都需要运维人员和开发人员一起合作,减缓了解决问题的速度,增加了跨部门沟通的成本。
    在不同的客户端程序中,维护相同的分片逻辑成本巨大。例如,系统中有两套业务系统共用一套 Redis 集群,一套业务系统用 Java 实现,另一套业务系统用 php 实现。为了保证分片逻辑的一致性,在 Java 客户端中实现的分片逻辑也需要在 PHP 客户端实现一次。相同的逻辑在不同的系统中分别实现,这种设计本来就非常糟糕,而且需要耗费巨大的开发成本保证两套业务系统分片逻辑的一致性。

  3. Twemproxy
    Twemproxy 是由 Twitter 开源的 Redis 代理,其基本原理是:Redis 客户端把请求发送到 Twemproxy,Twemproxy 根据路由规则发送到正确的 Redis 实例,最后 Twemproxy 把结果汇集返回给客户端。
    Twemproxy 通过引入一个代理层,将多个 Redis 实例进行统一管理,使 Redis 客户端只需要在 Twemproxy 上进行操作,而不需要关心后面有多少个 Redis 实例,从而实现了 Redis 集群。

Twemproxy 的优点如下
客户端像连接 Redis 实例一样连接 Twemproxy,不需要改任何的代码逻辑。
支持无效 Redis 实例的自动删除。
Twemproxy 与 Redis 实例保持连接,减少了客户端与 Redis 实例的连接数。

Twemproxy 有如下不足
由于 Redis 客户端的每个请求都经过 Twemproxy 代理才能到达 Redis 服务器,这个过程中会产生性能损失。
没有友好的监控管理后台界面,不利于运维监控。
最大的问题是 Twemproxy 无法平滑地增加 Redis 实例。对于运维人员来说,当因为业务需要增加 Redis 实例时工作量非常大。
Twemproxy 作为最被广泛使用、最久经考验、稳定性最高的 Redis 代理,在业界被广泛使用。

Twemproxy 不能平滑增加 Redis 实例的问题带来了很大的不便,于是豌豆荚自主研发了 Codis,一个支持平滑增加载 Redis 实例的 Redis 代理软件。

部署:
新开一台主机,来做 redis的代理;下载所需要的包;将下载的包解压,

[root@server4 ~]# ls
MHA-7                                           twemproxy-master
mysql-router-community-8.0.21-1.el7.x86_64.rpm  twemproxy-master.zip
[root@server4 ~]# cd twemproxy-master
[root@server4 twemproxy-master]# ls
ChangeLog     contrib  Makefile.am  NOTICE     src
conf          LICENSE  man          README.md  tests
configure.ac  m4       notes        scripts    travis.sh
发现没有 configure 没办法编译。查看 README.md 安装方法。
[root@server4 twemproxy-master]# yum install -y automake libtool
[root@server4 twemproxy-master]# autoreconf -fvi
[root@server4 twemproxy-master]# ./configure 
[root@server4 twemproxy-master]# make
[root@server4 twemproxy-master]# make install

此时便已经安装完成,修改配置文件并启动;

[root@server4 conf]# pwd
/root/twemproxy-master/conf
[root@server4 conf]# ls
nutcracker.leaf.yml  nutcracker.root.yml  nutcracker以上是关于电商平台 lnmp 架构之 mysql 高速缓存--redis的主要内容,如果未能解决你的问题,请参考以下文章

电商平台 lnmp 架构之 mysql 优化

电商平台 lnmp 架构之 mysql 优化

电商平台 lnmp 架构之 nginx+tomcat

电商平台 lnmp 架构之 nginx+tomcat

电商平台 lnmp 架构之 nginx 优化

lamp 架构之lnmp架构