1. 程式人生 > 其它 >DAY 105 redis叢集搭建

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 status\G;


### 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
-併發量:幾百