DAY 105 redis集群搭建

1 redis介绍和基本安装
-celery+redis:消息存储,队列
   -去重,计数,缓存,geo,
   -性能很高
   -源码安装
   
2 api
-公共api
   -字符串
   -列表
   -hash
   -集合
   -有序集合
   
3 客户端
-python 的客户端:redis模块
   
4 高级使用
-慢查询
   -pipline,事务
   -发布订阅
   -bitmap
   -HyperLogLog
   -geo
5 持久化
-rdb:快照方式
   -aof:日志方式
   
6 主从复制
-一主一从
   -一主多从
   
7 哨兵(高可用)

 

1 redis集群搭建

# 第一种:最原始,一步步做(基本不用)
-启动多个节点(6个节点)
   -相互meet
   -指派槽
   -做主从
# 第二种:ruby脚本

# 第三种:新版本上,官方直接支持用命令实现
-redis-cli --cluster(meet,分配槽,做主从)
   
   
# 第三种:
###1 生成6个配置文件起6个节点,6个配置文件
port 7000
daemonize yes
dir "/opt/soft/redis/data/"
logfile "7000.log"
dbfilename "dump-7000.rdb"
cluster-enabled yes
cluster-config-file nodes-7000.conf
cluster-require-full-coverage yes

sed 's/7000/7001/g' redis-7000.conf > redis-7001.conf
sed 's/7000/7002/g' redis-7000.conf > redis-7002.conf
sed 's/7000/7003/g' redis-7000.conf > redis-7003.conf
sed 's/7000/7004/g' redis-7000.conf > redis-7004.conf
sed 's/7000/7005/g' redis-7000.conf > redis-7005.conf


###2 启动6个节点
./src/redis-server ./redis-7000.conf
./src/redis-server ./redis-7001.conf
./src/redis-server ./redis-7002.conf
./src/redis-server ./redis-7003.conf
./src/redis-server ./redis-7004.conf
./src/redis-server ./redis-7005.conf

ps -ef |grep redis
# 现在往任意一个节点中写数据,都写不进去

###3 meet,分配槽,建立主从

##./src/redis-cli --cluster help 查看cluster命令的使用
redis-cli --cluster create --cluster-replicas 1 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005
                       
yes

# 写入数据测试
连接任意一个主节点:发现存需要去固定的节点存取
所以我们使用(自动重定向到固定节点存取)
./src/redis-cli -a -p 7000

1.1 扩容

#配置文件
sed 's/7000/7006/g' redis-7000.conf > redis-7006.conf
sed 's/7000/7007/g' redis-7000.conf > redis-7007.conf

#启动
redis-server ./redis-7006.conf
redis-server .redis-7007.conf

# 让集群感知到新进来的
redis-cli -p 7000 cluster meet 127.0.0.1 7006
redis-cli -p 7000 cluster meet 127.0.0.1 7007

#做主从
redis-cli -p 7007 cluster replicate ca0a50decb2d75985599533471a1b49945db11dd

# 迁移槽
redis-cli --cluster reshard 127.0.0.1:7000
   -迁移多少
   -迁到谁(id)
   -all(均匀的从每个节点拿一些)
   -yes
   
# 查看集群信息
cluster nodes

#测试

缩容

# 下线迁槽(把7006的1366个槽迁移到7000上)
redis-cli --cluster reshard --cluster-from 7006的id --cluster-to 7000的id --cluster-slots 1366 127.0.0.1:7000
yes

redis-cli --cluster reshard --cluster-from 7006的id --cluster-to 7001的id --cluster-slots 1366 127.0.0.1:7001
yes
redis-cli --cluster reshard --cluster-from 7006的id --cluster-to 7002的id --cluster-slots 1365 127.0.0.1:7002
yes

# 忘记节点,关闭节点
redis-cli --cluster del-node 127.0.0.1:7000 要下线的7007id  
# 先下从,再下主,因为先下主会触发故障转移
redis-cli --cluster del-node 127.0.0.1:7000 要下线的7006id


# 关掉其中一个主,另一个从立马变成主顶上, 重启停止的主,发现变成了从

2 缓存的优化和使用

1 做缓存
2 缓存更新策略
-LRU -Least Recently Used,没有被使用时间最长的
-LFU -Least Frequenty User,一定时间段内使用次数最少的
-FIFO -First In First Out
   -LIRS (Low Inter-reference Recency Set)是一个页替换算法,相比于LRU(Least Recently Used)和很多其他的替换算法,LIRS具有较高的性能。这是通过使用两次访问同一页之间的距离(本距离指中间被访问了多少非重复块)作为一种尺度去动态地将访问页排序,从而去做一个替换的选择
   
   
3 缓存穿透  缓存击穿 缓存雪崩
### 缓存穿透
#描述:
缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求,如发起为id为“-1”的数据或id为特别大不存在的数据。这时的用户很可能是攻击者,攻击会导致数据库压力过大。
#解决方案:
1 接口层增加校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截;
2 从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击
3 通过布隆过滤器实现,避免缓存穿透



### 缓存击穿
#描述:
缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力
#解决方案:
设置热点数据永远不过期。


### 缓存雪崩
#描述:
缓存雪崩是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机。和缓存击穿不同的是,        缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。
# 解决方案:
1 缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。
2 如果缓存数据库是分布式部署,将热点数据均匀分布在不同搞得缓存数据库中。
3 设置热点数据永远不过期。


##双写一致性
   1. 先更新数据库,再更新缓存
   2. 先删除缓存,再更新数据库
   3. 先更新数据库,再删除缓存(我们用的)

 

3 mysql主从搭建

1 写操作到主,读操作去从
2 一台机器,使用docker搭建(配置文件)
-

 

4 django实现读写分离

#1 基于docker
sudo yum install -y yum-utils device-mapper-persistent-data lvm2
sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
yum install docker-ce
systemctl start docker  # 启动docker

docker pull mysql:5.7   #拉取mysql5.7镜像
   
   
# 2 修改配置文件
###主
[mysqld]
user=mysql
character-set-server=utf8
default_authentication_plugin=mysql_native_password
secure_file_priv=/var/lib/mysql
expire_logs_days=7
sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
max_connections=1000

server-id=100
log-bin=mysql-bin


###从
[mysqld]
user=mysql
character-set-server=utf8
default_authentication_plugin=mysql_native_password
secure_file_priv=/var/lib/mysql
expire_logs_days=7
sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
max_connections=1000

server-id=101
log-bin=mysql-slave-bin
relay_log=edu-mysql-relay-bin


##3 启动容器,使用配置文件启动
docker run  -di -v /home/mysql/data/:/var/lib/mysql -v /home/mysql/conf.d:/etc/mysql/conf.d -v /home/mysql/my.cnf:/etc/mysql/my.cnf -p 33307:3306 --name mysql-master -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7
                   
                   
                   
docker run  -di -v /home/mysql2/data/:/var/lib/mysql -v /home/mysql2/conf.d:/etc/mysql/conf.d -v /home/mysql2/my.cnf:/etc/mysql/my.cnf -p 33306:3306 --name mysql-slave -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7

                   
##4 远程连接从库,新建一个用户(test)
#连接主库
mysql -h 47.100.50.84 -P 33307 -u root -p123456
#在主库创建用户并授权
##创建test用户
create user 'test'@'%' identified by '123';
##授权用户
grant all privileges on *.* to 'test'@'%' ;
###刷新权限
flush privileges;
#查看主服务器状态(显示如下图)
show master status;

 

### 5 从库的配置

#连接从库
mysql -h 47.100.50.84 -P 33306 -u root -p123456
#配置详解
/*
change master to
master_host='MySQL主服务器IP地址',
master_user='之前在MySQL主服务器上面创建的用户名'
master_password='之前创建的密码',
master_log_file='MySQL主服务器状态中的二进制文件名',
master_log_pos='MySQL主服务器状态中的position值';
*/
#命令如下
change master to master_host='47.100.50.84',master_port=33307,master_user='test',master_password='123',master_log_file='mysql-bin.000003',master_log_pos=0;
#启用从库
start slave;
#查看从库状态(如下图)
show slave statusG;


### 6 测试主从
#在主库上创建数据库test1,创建表,插入数据

 

5 读写分离

多数据库:
python manage.py makemigraions

python manage.py migrate app名称 --databse=配置文件数据名称的别名

#手动操作:
   # 读(db1),写(default)分离
   res = models.Book.objects.all().using('db1')

   # 写到default中
   # models.Book.objects.using('default').create(name='西游记')

#自动操作:
## 写一个py文件,新建一个类,如下
class Router1:
   def db_for_read(self, model, **hints):

       return 'db1'

   def db_for_write(self, model, **hints):

       return 'default'
##在配置文件中配置
DATABASE_ROUTERS = ['db_router.Router1',]
## 以后,自动的读写分离,不需要额外处理


#粒度更细(指定读book表时,去db1库读)
class Router1:
   def db_for_read(self, model, **hints):
       if model._meta.model_name == 'book':  # 通过表名限制更细粒度
           return 'db1'
       else:
           return 'default'

   def db_for_write(self, model, **hints):
       return 'default'

补充

1 你们项目的并发量
-uwsgi+django  200以内
   -用户数:50w用户数
   -日活:1w
   -并发量:几百

 

原文地址:https://www.cnblogs.com/DEJAVU888/p/14914407.html