一、tomcat
docker pull billygoo/tomcat8-jdk8
docker images
docker run -d -p 8080:8080 --name mytomcat8 billygoo/tomcat8-jdk8:latest
二、mysql
1.单机配置
//下载镜像
docker pull mysql:5.7
//创建容器并挂载在容器卷
docker run -d -p 3306:3306 --privileged=true \
-v /lizjuse/mysql/log:/var/log/mysql \
-v /lizjuse/mysql/data:/var/lib/mysql \
-v /lizjuse/mysql/conf:/etc/mysql/conf.d \
-e MYSQL_ROOT_PASSWORD=123456 \
--name mysql5.7_1 mysql:5.7
//配置mysql
cd /lizjuse/mysql/conf
vim my.cnf
[client]
default_character_set = utf8
[mysqld]
collation_server = utf8_general_ci
character_set_server = utf8
2.主从复制配置
主服务器3307,从服务器3308
①创建主机
//创建3307
docker run -p 3307:3306 --name mysql-master \
-v/mydata/mysql-master/log:/var/log/mysql \
-v/mydata/mysql-master/data:/var/lib/mysql \
-v/mydata/mysql-master/conf:/etc/mysql \
-e MYSQL_ROOT_PASSWORD=root \
-d mysql:5.7
//创建3308
docker run -p 3308:3306 --name mysql-slave \
-v/mydata/mysql-slave/log:/var/log/mysql \
-v/mydata/mysql-slave/data:/var/lib/mysql \
-v/mydata/mysql-slave/conf:/etc/mysql \
-e MYSQL_ROOT_PASSWORD=root \
-d mysql:5.7
②mysql配置文件my.cnf
##master
[mysqld]
##设置server_id,同一局域网中需要唯一
server_id=101
#指定不需要同步的数据库名称
binlog-ignore-db=mysql
##开启二进制日志功能
log-bin=mall-mysql-bin
#设置二进制日志使用内存大小(事务)
binlog_cache_size=1M
##设置使用的二进制日志格式(mixed,statement,row)
binlog_format=mixed
##二进制日志过期清理时间。默认值为0,表示不自动清理。
expire_logs_days=7
##跳过主从复制中遇到的所有错误或指定类型的错误,避免slaves端复制中断。
##如:1062错误是指一些主键重复,1032错误是因为主从数据库数据不一致
slave_skip_errors=1062
##slave
[mysqld]
##设置server_id,同一局域网中需要唯一
server_id=102
#指定不需要同步的数据库名称
binlog-ignore-db=mysql
#开启二进制日志功能,以备Slave作为其它数据库实例的Masterl时使用
log-bin=mall-mysql-slave1-bin
##设置二进制日志使用内存大小(事务)
binlog_cache_size=1M
#设置使用的二进制日志格式(mixed,statement,row)
binlog_format=mixed
##二进制日志过期清理时间。默认值为0,表示不自动清理。
expire_logs_days=7
#跳过主从复制中遇到的所有错误或指定类型的错误,避免slave端复制中断。
##如:1062错误是指一些主键重复,1032错误是因为主从数据库数据不一致
slave_skip_errors=1062
#relay_log配置中继日志
relay_log=mall-mysql-relay-bin
##Log_slave_updates表示slave将复制事件写进自己的二进制日志
log_slave_updates=1
#slavei设置为只读(具有super权限的用户除外)
read_only=1
③配置
主数据库master数据库创建授权用户
CREATE USER 'slave'@'%' IDENTIFIED BY '123456';
GRANT REPLICATION SLAVE,REPLICATION CLIENT ON *.* TO 'slave'@'%';
查看主从同步状态show master status;
从数据库slave配置主从复制
change master to master_host='172.21.55.100',master_user='slave',master_password='123456',master_port=3307,master_log_file='mall-mysql-bin.000001',master_log_pos=617,master_connect_retry=30;
查看主从同步状态show slave status \G;
![](https://img.haomeiwen.com/i24795580/c2556e642486a885.png)
从数据库slave开启主从同步start slave;
![](https://img.haomeiwen.com/i24795580/5ba8aa1b85e59435.png)
三、redis
1.单机配置
//下载镜像
docker pull redis:6.0.8
//创建容器并挂载在容器卷
docker run -d -p 6379:6379 --privileged=true \
-v /lizjuse/redis/redis.conf:/etc/redis/redis.conf \
-v /lizjuse/redis/data:/data \
--name redis6.0.8_1 \
-d redis:6.0.8 redis-server /etc/redis/redis.conf
//测试
redis-cli
127.0.0.1:6379> auth 123456
OK
127.0.0.1:6379> set k1 v1
OK
127.0.0.1:6379> get k1
"v1"
127.0.0.1:6379> ping
PONG
127.0.0.1:6379>
2.集群配置
①创建redis集群
docker run -d --name redis-node-1 --net host --privileged=true -v /data/redis/share/redis-node-1:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6381
docker run -d --name redis-node-2 --net host --privileged=true -v /data/redis/share/redis-node-2:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6382
docker run -d --name redis-node-3 --net host --privileged=true -v /data/redis/share/redis-node-3:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6383
docker run -d --name redis-node-4 --net host --privileged=true -v /data/redis/share/redis-node-4:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6384
docker run -d --name redis-node-5 --net host --privileged=true -v /data/redis/share/redis-node-5:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6385
docker run -d --name redis-node-6 --net host --privileged=true -v /data/redis/share/redis-node-6:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6386
②配置主从
redis-cli --cluster create 172.21.55.100:6381 172.21.55.100:6382 172.21.55.100:6383 172.21.55.100:6384 172.21.55.100:6385 172.21.55.100:6386 --cluster-replicas 1
查看集群信息
//进入节点
root@lizj:/data# redis-cli -p 6381
//查看集群详情
127.0.0.1:6381> cluster info
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:6
cluster_my_epoch:1
cluster_stats_messages_ping_sent:200
cluster_stats_messages_pong_sent:191
cluster_stats_messages_sent:391
cluster_stats_messages_ping_received:186
cluster_stats_messages_pong_received:200
cluster_stats_messages_meet_received:5
cluster_stats_messages_received:391
//查看节点状态
127.0.0.1:6381> cluster nodes
8716061af89bd1ab05c7b61344fc1710ce42cc1c 172.21.55.100:6386@16386 slave 6e5c36d824e2838c7ef138d72ea81601ee6d28be 0 1697121966814 3 connected
6e5c36d824e2838c7ef138d72ea81601ee6d28be 172.21.55.100:6383@16383 master - 0 1697121966000 3 connected 10923-16383
21e5a7fecc3b664af5ee9355d4be81083fbfa5c1 172.21.55.100:6382@16382 master - 0 1697121967878 2 connected 5461-10922
d04aa04a5fdca742658ff2a65b8eb09c316a59c1 172.21.55.100:6381@16381 myself,master - 0 1697121965000 1 connected 0-5460
33a571d032e6f389473679de135724b1663e4a6c 172.21.55.100:6384@16384 slave d04aa04a5fdca742658ff2a65b8eb09c316a59c1 0 1697121965000 1 connected
54da511af35f7143881277f1bdc7c3df9709c948 172.21.55.100:6385@16385 slave 21e5a7fecc3b664af5ee9355d4be81083fbfa5c1 0 1697121966000 2 connected
可以看出主从分别为83:86,81:84,82:85
连接集群化redis要使用参数-c优化路由redis-cli -p 6381 -c
![](https://img.haomeiwen.com/i24795580/0e03b5d1b80413f8.png)
![](https://img.haomeiwen.com/i24795580/9e35727efbb96dbf.png)
查看个节点hash存储情况redis-cli --cluster check 172.21.55.100:6381
![](https://img.haomeiwen.com/i24795580/1f9dbe43ee3d107f.png)
②主从扩容(写入简历,问哈希槽的重新分配)
创建节点容器
docker run -d --name redis-node-7 --net host --privileged=true -v /data/redis/share/redis-node-7:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6387
docker run -d --name redis-node-8 --net host --privileged=true -v /data/redis/share/redis-node-8:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6388
进入redis节点7进行配置
[root@lizj ~]# docker exec -it redis-node-7 bash
root@lizj:/data# redis-cli --cluster add-node 172.21.55.100:6387 172.21.55.100:6381
#检查添加情况可以发现6387添加成功但没有槽位
root@lizj:/data# redis-cli --cluster check 172.21.55.100:6381
172.21.55.100:6381 (d04aa04a...) -> 2 keys | 5461 slots | 1 slaves.
172.21.55.100:6382 (21e5a7fe...) -> 1 keys | 5462 slots | 1 slaves.
172.21.55.100:6387 (59bca87e...) -> 0 keys | 0 slots | 0 slaves.
172.21.55.100:6383 (6e5c36d8...) -> 1 keys | 5461 slots | 1 slaves.
[OK] 4 keys in 4 masters.
0.00 keys per slot on average.
>>> Performing Cluster Check (using node 172.21.55.100:6381)
M: d04aa04a5fdca742658ff2a65b8eb09c316a59c1 172.21.55.100:6381
slots:[0-5460] (5461 slots) master
1 additional replica(s)
S: 33a571d032e6f389473679de135724b1663e4a6c 172.21.55.100:6384
slots: (0 slots) slave
replicates d04aa04a5fdca742658ff2a65b8eb09c316a59c1
M: 21e5a7fecc3b664af5ee9355d4be81083fbfa5c1 172.21.55.100:6382
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
S: 54da511af35f7143881277f1bdc7c3df9709c948 172.21.55.100:6385
slots: (0 slots) slave
replicates 21e5a7fecc3b664af5ee9355d4be81083fbfa5c1
S: 8716061af89bd1ab05c7b61344fc1710ce42cc1c 172.21.55.100:6386
slots: (0 slots) slave
replicates 6e5c36d824e2838c7ef138d72ea81601ee6d28be
M: 59bca87efa0732360c7c2079750636b695147266 172.21.55.100:6387
slots: (0 slots) master
M: 6e5c36d824e2838c7ef138d72ea81601ee6d28be 172.21.55.100:6383
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
重新分配槽号
root@lizj:/data# redis-cli --cluster reshard 172.21.55.100:6381
>>> Performing Cluster Check (using node 172.21.55.100:6381)
M: d04aa04a5fdca742658ff2a65b8eb09c316a59c1 172.21.55.100:6381
slots:[0-5460] (5461 slots) master
1 additional replica(s)
S: 33a571d032e6f389473679de135724b1663e4a6c 172.21.55.100:6384
slots: (0 slots) slave
replicates d04aa04a5fdca742658ff2a65b8eb09c316a59c1
M: 21e5a7fecc3b664af5ee9355d4be81083fbfa5c1 172.21.55.100:6382
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
S: 54da511af35f7143881277f1bdc7c3df9709c948 172.21.55.100:6385
slots: (0 slots) slave
replicates 21e5a7fecc3b664af5ee9355d4be81083fbfa5c1
S: 8716061af89bd1ab05c7b61344fc1710ce42cc1c 172.21.55.100:6386
slots: (0 slots) slave
replicates 6e5c36d824e2838c7ef138d72ea81601ee6d28be
M: 59bca87efa0732360c7c2079750636b695147266 172.21.55.100:6387
slots: (0 slots) master
M: 6e5c36d824e2838c7ef138d72ea81601ee6d28be 172.21.55.100:6383
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
How many slots do you want to move (from 1 to 16384)? 4096
What is the receiving node ID? 59bca87efa0732360c7c2079750636b695147266
Please enter all the source node IDs.
Type 'all' to use all the nodes as source nodes for the hash slots.
Type 'done' once you entered all the source nodes IDs.
Source node #1: all
4096为16384/4=4096,id为新增节点的id
![](https://img.haomeiwen.com/i24795580/e8d3142b28419b79.png)
重新检查状态,可观察到已有槽位,每个节点匀了一点凑到4096
添加从节点
root@lizj:/data# redis-cli --cluster add-node 172.21.55.100:6388 172.21.55.100:6387 --cluster-slave --cluster-master-id 59bca87efa0732360c7c2079750636b695147266
![](https://img.haomeiwen.com/i24795580/485b592e2afd9a8c.png)
③主从缩容(写入简历,问哈希槽的重新分配)
步骤
1.先清除从节点6388
2.清出来的槽号重新分配
3.再删除6387
4.恢复成3主3从
删除从节点6388
redis-cli --cluster del-node [主机IP]:[要删除的节点端口号] 要删除的节点id
root@lizj:/data# redis-cli --cluster del-node 172.21.55.100:6388 07e82323cc3eda6517863a7438b2b2f89152fd56
>>> Removing node 07e82323cc3eda6517863a7438b2b2f89152fd56 from cluster 172.21.55.100:6388
>>> Sending CLUSTER FORGET messages to the cluster...
>>> Sending CLUSTER RESET SOFT to the deleted node.
将6387的槽号清空,重新分配,本例将清出来的槽号都给6381
redis-cli --cluster reshard 172.21.55.100:6381
以6381为入口操作整个集群的槽号
![](https://img.haomeiwen.com/i24795580/60aef2c10c7a04a4.png)
删除节点6387
redis-cli --cluster del-node [主机IP]:[要删除的节点端口号] 要删除的节点id
root@lizj:/data# redis-cli --cluster del-node 172.21.55.100:6387 59bca87efa0732360c7c2079750636b695147266
>>> Removing node 59bca87efa0732360c7c2079750636b695147266 from cluster 172.21.55.100:6387
>>> Sending CLUSTER FORGET messages to the cluster...
>>> Sending CLUSTER RESET SOFT to the deleted node.
![](https://img.haomeiwen.com/i24795580/8a388c90ae77b086.png)
3.面试题
1~2亿条数据需要缓存。请问如何设计这个存储案例?
回答:单机单台100%不可能,肯定是分布式存储,用redis如何落地?
上述问题阿里P6~P7工程案例和场景设计类必考题目,一般业界有3种解决方案
①哈希取余分区(小厂)
![](https://img.haomeiwen.com/i24795580/63b91299b4ee81ab.png)
2亿条记录就是2亿个k,我们单机不行必须要分布式多机,假设有3台机器构成一个集群,用户每次读写操作都是根据公式:hash(key) % N个机器台数,计算出哈希值,用来决定数据映射到哪一个节点上。
优点:简单粗暴,直接有效,只需要预估好数据规划好节点,例如3台、8台、10台,就能保证一段时间的数据支撑。使用Hash算法让固定的一部分请求落到同一台服务器上,这样每台服务器固定处理一部分请求(并维护这些请求的信息),起到负载均衡+分而治之的作用。
缺点:原来规划好的节点,进行扩容或者缩容就比较麻烦了额,不管扩缩,每次数据变动导致节点有变动,映射关系需要重新进行计算,在服务器个数固定不变时没有问题,如果需要弹性扩容或故障停机的情况下,原来的取模公式就会发生变化:Hash(key)3会变成Hash(key) /?。此时地址经过取余运算的结果将发生很大变化,根据公式获取的服务器也会变得不可控。某个redis机器宕机了,由于台数数量变化,会导致hash取余全部数据重新洗牌。
②一致性哈希算法分区(中厂)
三步骤:算法构建一致性哈希环、服务器IP节点映射、key落到服务器的落键规则
算法构建一致性哈希环
一致性哈希算法必然有个hash函数并按照算法产生hash值,这个算法的所有可能哈希值会构成一个全量集,这个集合可以成为一个hash空间[0,232-1],这个是一个线性空间,但是在算法中,我们通过适当的逻辑控制将它首尾相连(0=232),这样让它逻辑上形成了一个环形空间。
它也是按照使用取模的方法,前面笔记介绍的节点取模法是对节点(服务器)的数量进行取模。而一致性Hash算法是对232取模,简单来说,致性Hash算法将整个哈希值空间组织成一个虚拟的圆环,如假设某哈希函数H的值空间为0-232-1(即哈希值是一个32位无符号整形),整个哈希环如下图:整个空间按顺时针方向组织,圆环的正上方的点代表0,0点右侧的第一个点代表1,以此类推,2、3、4、……直到232-1,也就是说0点左侧的第一个点代表232-1,0和232-1在零点中方向重合,我们把这个由232个点组成的圆环称为Hash环。
![](https://img.haomeiwen.com/i24795580/9002df3c22dcdd5d.png)
服务器IP节点映射
将集群中各个P节点映射到环上的某一个位置。
将各个服务器使用Hash进行一个哈希,具体可以选择服务器的P或主机名作为关键字进行哈希,这样每台机器就能确定其在哈希环上的位置。假如4个节点NodeA、B、C、D,经过IP地址的哈希函数计算(hash(ip)),使用IP地址哈希后在环空间的位置如下:
![](https://img.haomeiwen.com/i24795580/d0f7a535fd2e4745.png)
key落到服务器的落键规则
当我们需要存储一个kv键值对时,首先计算key的hash值,hash(key),将这个key使用相同的函数Hash计算出哈希值并确定此数据在环上的位置,从此位置沿环顺时针“行走”,第一台遇到的服务器就是其应该定位到的服务器,并将该键值对存储在该节点上。
如我们有Object A、Object B、Object C、Object D四个数据对象,经过哈希计算后,在环空间上的位置如下:根据一致性Hash算法,数据A会被定为到Node A上,B被定为到Node B上,C被定为到Node C上,D被定为到Node D上。
![](https://img.haomeiwen.com/i24795580/f9ff2b02418413ed.png)
优点:一致性哈希算法的容错性、一致性哈希算法的扩展性
缺点:致性哈希算法的数据倾斜问题,一致性Hash算法在服务节点太少时,容易因为节点分布不均匀而造成数据倾斜(被缓存的对象大部分集中缓存在某一台服务器上)问题,例如系统中只有两台服务器:
![](https://img.haomeiwen.com/i24795580/2f356e5ec04d4293.png)
总结:
为了在节点数目发生改变时尽可能少的迁移数据
将所有的存储节点排列在收尾相接的Hash环上,每个key在计算Hash后会顺时针找到临近的存储节点存放。
而当有节点加入或退出时仅影响该节点在Hash环上顺时针相邻的后续节点。
优点
加入和删除节点只影响哈希环中顺时针方向的相邻的节点,对其他节点无影响。
缺点
数据的分布和节点的位置有关,因为这些节点不是均匀的分布在哈希环上的,所以数据在进行存储时达不到均匀分布的效果。
③哈希槽分区(大厂)
哈希槽实质就是一个数组,数组[0,214 -1]形成hash slot空间。
解决均匀分配的问题,在数据和节点之间又加入了一层,把这层称为哈希槽 (slot),用于管理数据和节点之间的关系,现在就相当于节点上放的是槽,槽里放的是数据。
![](https://img.haomeiwen.com/i24795580/212051ca4d1cd411.png)
槽解决的是粒度问题,相当于把粒度变大了,这样便于数据移动。
哈希解决的是映射问题,使用key的哈希值来计算所在的槽,便于数据分配。
多少个hash槽
一个集排只能有16384个槽(不超过1000个节点),归号0-16383(0-241)。这些会。给集集群的所有于节点,分配策没有要求,可以超定哪些编号的题分配经哪个主节点。集群会记录节点和槽的对应关系。解决了节点和槽的关系后,接下来就需要对key求哈希值,然后对16384取余,余数是儿key就落入欢应的槽里。slot= CRC16(key) % 16384。以槽为单位移动数据,因为槽的数目是固定的,处理起来比较容易,这样数据移动问题就解决了。
哈希槽计算
Redis集群中内置了16384个哈希槽,redis会根据节点数量大致均等的将哈希槽映射到不同的节点。当需要在Redis集群中放置一个key-vaue时,redis先对key使用crc16算法算出一个结果,然后把结果对16384求余数,这样每个key都会对应一个编号在0-16383之间的哈希槽,也就是映射到某个节点上。如下代码,key之A、B在Node2,key之C落在Node3上。
![](https://img.haomeiwen.com/i24795580/39311af91c60a6d5.png)
![](https://img.haomeiwen.com/i24795580/51c9a9e7d0f881c9.png)
网友评论