MY HAOSE BLOG!!!

查看mysql数据select查询是否用到索引

问题发现

我认为一条很简单的SQL然后跑了很久,明明我已经都建立相应的索引,逻辑也不需要优化。

复制代码
SELECT a.custid, b.score, b.xcreditscore, b.lrscoreFROM (    SELECT DISTINCT custid    FROM sync.`credit_apply`    WHERE SUBSTR(createtime, 1, 10) >= '2019-12-15'
        AND rejectrule = 'xxxx') a    LEFT JOIN (        SELECT *
        FROM sync.`credit_creditchannel`
    ) b    ON a.custid = b.custid;
复制代码

查看索引状态:

credit_apply表

复制代码
mysql> show index from sync.`credit_apply`;+--------------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+| Table        | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |+--------------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+| credit_apply |          0 | PRIMARY  |            1 | applyId     | A         |     1468496 | NULL     | NULL   |      | BTREE      |         |               || credit_apply |          1 | index2   |            1 | custId      | A         |      666338 | NULL     | NULL   |      | BTREE      |         |               || credit_apply |          1 | index2   |            2 | createTime  | A         |     1518231 | NULL     | NULL   |      | BTREE      |         |               |+--------------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
复制代码

或者

复制代码
CREATE TABLE `credit_apply` (
  `applyId` bigint(20) NOT NULL AUTO_INCREMENT,
  `custId` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,
  `ruleVersion` int(11) NOT NULL DEFAULT '1',
  `rejectRule` varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT 'DP0000',
  `status` tinyint(4) NOT NULL DEFAULT '0',
  `extra` text COLLATE utf8mb4_unicode_ci,
  `createTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `updateTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `mobile` varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT '',  PRIMARY KEY (`applyId`) USING BTREE,  KEY `index2` (`custId`,`createTime`)) ENGINE=InnoDB AUTO_INCREMENT=1567035 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
复制代码



sync.`credit_creditchannel`表

复制代码
mysql> show index from sync.`credit_creditchannel` ;+----------------------+------------+-----------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+| Table                | Non_unique | Key_name                    | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |+----------------------+------------+-----------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+| credit_creditchannel |          0 | PRIMARY                     |            1 | recId       | A         |      450671 | NULL     | NULL   |      | BTREE      |         |               || credit_creditchannel |          1 | nationalId_custid           |            1 | nationalId  | A         |      450770 | NULL     | NULL   |      | BTREE      |         |               || credit_creditchannel |          1 | nationalId_custid           |            2 | custId      | A         |      450770 | NULL     | NULL   | YES  | BTREE      |         |               || credit_creditchannel |          1 | credit_creditchannel_custId |            1 | custId      | A         |      450770 |       10 | NULL   | YES  | BTREE      |         |               |+----------------------+------------+-----------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
复制代码

 或者

复制代码
CREATE TABLE `credit_creditchannel` (
  `recId` bigint(20) NOT NULL AUTO_INCREMENT,
  `nationalId` varchar(128) NOT NULL DEFAULT '',
  `identityType` varchar(3) NOT NULL DEFAULT '',
  `brief` mediumtext,
  `score` decimal(10,4) NOT NULL DEFAULT '0.0000',
  `npaCode` varchar(128) NOT NULL DEFAULT '',
  `basic` mediumtext,
  `createTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `updateTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `request` mediumtext,
  `custId` varchar(128) DEFAULT '',
  `xcreditScore` decimal(10,4) DEFAULT '0.0000',
  `queryTime` varchar(24) DEFAULT '',
  `lrScore` decimal(10,4) DEFAULT '0.0000',  PRIMARY KEY (`recId`) USING BTREE,  KEY `nationalId_custid` (`nationalId`,`custId`),  KEY `credit_creditchannel_custId` (`custId`(10))) ENGINE=InnoDB AUTO_INCREMENT=586557 DEFAULT CHARSET=utf8
复制代码

我们都可以看到相应的索引。以现在简单的sql逻辑理论上走custid这个索引就好了

 

解释函数explain

复制代码
mysql> explain SELECT a.custid, b.score, b.xcreditscore, b.lrscore FROM(SELECT DISTINCT custid FROM sync.`credit_apply` WHERE SUBSTR(createtime, 1, 10) >= '2019-12-15' AND rejectrule = 'xxx') aLEFT JOIN (select * from sync.`credit_creditchannel`) bON a.custid = b.custid;+----+-------------+----------------------+------------+-------+---------------+--------+---------+------+---------+----------+----------------------------------------------------+| id | select_type | table                | partitions | type  | possible_keys | key    | key_len | ref  | rows    | filtered | Extra                                              |+----+-------------+----------------------+------------+-------+---------------+--------+---------+------+---------+----------+----------------------------------------------------+|  1 | PRIMARY     | <derived2>           | NULL       | ALL   | NULL          | NULL   | NULL    | NULL |  158107 |   100.00 | NULL                                               ||  1 | PRIMARY     | credit_creditchannel | NULL       | ALL   | NULL          | NULL   | NULL    | NULL |  450770 |   100.00 | Using where; Using join buffer (Block Nested Loop) ||  2 | DERIVED     | credit_apply         | NULL       | index | index2        | index2 | 518     | NULL | 1581075 |    10.00 | Using where                                        |+----+-------------+----------------------+------------+-------+---------------+--------+---------+------+---------+----------+----------------------------------------------------+3 rows in set (0.06 sec)
复制代码

如何去看我们的SQL是否走索引?

我们只需要注意一个最重要的type 的信息很明显的提现是否用到索引:

type结果

type结果值从好到坏依次是:

system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL

一般来说,得保证查询至少达到range级别,最好能达到ref,否则就可能会出现性能问题。

possible_keys:sql所用到的索引

key:显示MySQL实际决定使用的键(索引)。如果没有选择索引,键是NULL

rows: 显示MySQL认为它执行查询时必须检查的行数。

分析:

我们的credit_creditchannel是ALL,而possible_keys是NULL索引在查询该表的时候并没有用到索引怪不得这么慢!!!!!!!!!

 

分析和搜索解决办法

换着法的改sql也没用;换着群问大神也没用;各种搜索引擎搜才总算有点思路。 

索引用不上的原因可能是字符集和排序规则不相同。

于是看了了两张表的字符集和两张表这个字段的字符集以及排序规则:

修改数据库和表的字符集
alter database sync default character set utf8mb4;//修改数据库的字符集alter table sync.credit_creditchannel default character set utf8mb4;//修改表的字符集

修改表排序规则

alter table sync.`credit_creditchannel` convert to character set utf8mb4 COLLATE utf8mb4_unicode_ci;

 

由于数据库中的数据表和表字段的字符集和排序规则不统一,批量修改脚本如下:

1. 修改指定数据库中所有varchar类型的表字段的字符集为ut8mb4,并将排序规则修改为utf8_unicode_ci

复制代码
SELECT CONCAT('ALTER TABLE `', table_name, '` MODIFY `', column_name, '` ', DATA_TYPE, '(', CHARACTER_MAXIMUM_LENGTH, ') CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci', CASE 
        WHEN IS_NULLABLE = 'NO' THEN ' NOT NULL'
        ELSE ''
    END, ';')FROM information_schema.COLUMNSWHERE (TABLE_SCHEMA = 'databaseName'
    AND DATA_TYPE = 'varchar'
    AND (CHARACTER_SET_NAME != 'utf8mb4'
        OR COLLATION_NAME != 'utf8mb4_unicode_ci'));
复制代码

2. 修改指定数据库中所有数据表的字符集为UTF8,并将排序规则修改为utf8_general_ci 

SELECT CONCAT('ALTER TABLE ', table_name, 'CONVERT TO CHARACTER SET  utf8mb4 COLLATE utf8_unicode_ci;')FROM information_schema.TABLESWHERE TABLE_SCHEMA = 'databaseName'

 

explain 查看是否用到了索引

复制代码
mysql> explain SELECT a.custid, b.score, b.xcreditscore, b.lrscore FROM(SELECT DISTINCT custid FROM sync.`credit_apply` WHERE SUBSTR(createtime, 1, 10) >= '2019-12-15' AND rejectrule = 'xxx') aLEFT JOIN (select * from sync.`credit_creditchannel`) bON a.custid = b.custid;+----+-------------+----------------------+------------+-------+-----------------------------+-----------------------------+---------+----------+---------+----------+-------------+| id | select_type | table                | partitions | type  | possible_keys               | key                         | key_len | ref      | rows    | filtered | Extra       |+----+-------------+----------------------+------------+-------+-----------------------------+-----------------------------+---------+----------+---------+----------+-------------+|  1 | PRIMARY     | <derived2>           | NULL       | ALL   | NULL                        | NULL                        | NULL    | NULL     |  146864 |   100.00 | NULL        ||  1 | PRIMARY     | credit_creditchannel | NULL       | ref   | credit_creditchannel_custId | credit_creditchannel_custId | 43      | a.custid |       1 |   100.00 | Using where ||  2 | DERIVED     | credit_apply         | NULL       | index | index2                      | index2                      | 518     | NULL     | 1468644 |    10.00 | Using where |+----+-------------+----------------------+------------+-------+-----------------------------+-----------------------------+---------+----------+---------+----------+-------------+
复制代码

 

就是这样!!!!

补充大全:

可以看到结果中包含10列信息,分别为

id、select_type、table、type、possible_keys、key、key_len、ref、rows、Extra

对应的简单描述如下:

  • id: select查询的序列号,包含一组数字,表示查询中执行select子句或操作表的顺序===id如果相同,可以认为是一组,从上往下顺序执行;在所有组中,id值越大,优先级越高,越先执行

  • select_type: 表示查询的类型。用于区别普通查询、联合查询、子查询等的复杂查询。

  • table: 输出结果集的表 显示这一步所访问数据库中表名称(显示这一行的数据是关于哪张表的),有时不是真实的表名字,可能是简称,例如上面的e,d,也可能是第几步执行的结果的简称

  • partitions:匹配的分区

  • type:对表访问方式,表示MySQL在表中找到所需行的方式,又称“访问类型”。

  • possible_keys:表示查询时,可能使用的索引

  • key:表示实际使用的索引

  • key_len:索引字段的长度

  • ref:列与索引的比较

  • rows:扫描出的行数(估算的行数)

  • filtered:按表条件过滤的行百分比

  • Extra:执行情况的描述和说明

挑选一些重要信息详细说明:

  • select_type

    • SIMPLE 简单的select查询,查询中不包含子查询或者UNION

    • PRIMARY 查询中若包含任何复杂的子部分,最外层查询则被标记为PRIMARY

    • SUBQUERY 在SELECT或WHERE列表中包含了子查询

    • DERIVED 在FROM列表中包含的子查询被标记为DERIVED(衍生),MySQL会递归执行这些子查询,把结果放在临时表中

    • UNION 若第二个SELECT出现在UNION之后,则被标记为UNION:若UNION包含在FROM子句的子查询中,外层SELECT将被标记为:DERIVED

    • UNION RESULT 从UNION表获取结果的SELECT

 

  • type

    • mysql找到数据行的方式,效率排名

    • NULL > system > const > eq_ref > ref > range > index > all

***一般来说,得保证查询至少达到range级别,最好能达到ref。


    1. system 表只有一行记录(等于系统表),这是const类型的特列,平时不会出现,这个也可以忽略不计

    2. const   通过索引一次就找到了,const用于比较primary key 和 unique key,因为只匹配一行数据,所以很快。如果将主键置于where列表中,mysql就能将该查询转换为一个常量

    3. eq_ref 唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配。常见于主键索引和唯一索引  区别于const eq_ref用于联表查询的情况

    4. ref 非唯一索引扫描,返回匹配某个单独值的所有行,本质上也是一种索引访问,它返回所有匹配某个单独值的行,然而,他可能会找到多个符合条件的行,所以他应该属于查找和扫描的混合体

    5. range   只检索给定范围的行,使用一个索引来选择行,一般是在where中出现between、<、>、in等查询,范围扫描好于全表扫描,因为他只需要开始于索引的某一点,而结束于另一点,不用扫描全部索引

    6. index   Full Index Scan,Index与All区别为index类型只遍历索引树。通常比All快,因为索引文件通常比数据文件小。也就是说,虽然all和index都是读全表,但是index是从索引中读取的,而all是从硬盘读取的

    7. ALL Full Table Scan,将遍历全表以找到匹配的行

 

  • possible_keys

指出mysql能使用哪个索引在表中找到记录,查询涉及到的字段若存在索引,则该索引被列出,但不一定被查询使用(该查询可以利用的索引,如果没有任何索引显示null)
实际使用的索引,如果为NULL,则没有使用索引。(可能原因包括没有建立索引或索引失效)
查询中若使用了覆盖索引(select 后要查询的字段刚好和创建的索引字段完全相同),则该索引仅出现在key列表中 possible_keys为null

 

  • key

key列显示mysql实际决定使用的索引,必然包含在possible_keys中。如果没有选择索引,键是NULL。想要强制使用或者忽视possible_keys列中的索引,在查询时指定FORCE INDEX、USE INDEX或者IGNORE index

  • key_len

表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度,在不损失精确性的情况下,长度越短越好。key_len显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索出的。

  • ref

显示索引的那一列被使用了,如果可能的话,最好是一个常数。哪些列或常量被用于查找索引列上的值。

  • rows

根据表统计信息及索引选用情况,大致估算出找到所需的记录所需要读取的行数,也就是说,用的越少越好

 

  • extra

包含不适合在其他列中显式但十分重要的额外信息


    • Using Index:表示相应的select操作中使用了覆盖索引(Covering Index),避免访问了表的数据行,效率不错。如果同时出现using where,表明索引被用来执行索引键值的查找;如果没有同时出现using where,表明索引用来读取数据而非执行查找动作。

    • Using where:不用读取表中所有信息,仅通过索引就可以获取所需数据,这发生在对表的全部的请求列都是同一个索引的部分的时候,表示mysql服务器将在存储引擎检索行后再进行过滤

    • Using temporary:表示MySQL需要使用临时表来存储结果集,常见于排序和分组查询,常见 group by ; order by

    • Using filesort:当Query中包含 order by 操作,而且无法利用索引完成的排序操作称为“文件排序”

    • Using join buffer:表明使用了连接缓存,比如说在查询的时候,多表join的次数非常多,那么将配置文件中的缓冲区的join buffer调大一些。

    • Impossible where:where子句的值总是false,不能用来获取任何元组

    • Select tables optimized away:这个值意味着仅通过使用索引,优化器可能仅从聚合函数结果中返回一行

    • No tables used:Query语句中使用from dual 或不含任何from子句

以上两种信息表示mysql无法使用索引


    1. using filesort :表示mysql会对结果使用一个外部索引排序,而不是从表里按索引次序读到相关内容,可能在内存或磁盘上排序。mysql中无法利用索引完成的操作称为文件排序

    2. using temporary: 使用了用临时表保存中间结果,MySQL在对查询结果排序时使用临时表。常见于排序order by和分组查询group by。



0分/0个投票

TOP


浏览: 268    评论: 0
Kubernetes 通过statefulset部署redis cluster集群

Redis集群架构图

Kubernetes 通过statefulset部署redis cluster集群
每个Mater 都可以拥有多个slave.当Master掉线后,redis cluster集群会从多个Slave中选举出来一个新的Matser作为代替,而旧的Master重新上线后变成 Master 的Slave.

部署redis集群方式的选择

  • Statefulset

  • Service&depolyment

对于redis,mysql这种有状态的服务,我们使用statefulset方式为首选.我们这边主要就是介绍statefulset这种方式

ps:
statefulset 的设计原理模型:
    拓扑状态.应用的多个实例之间不是完全对等的关系,这个应用实例的启动必须按照某些顺序启动,比如应用的
    主节点 A 要先于从节点 B 启动。而如果你把 A 和 B 两个Pod删除掉,他们再次被创建出来是也必须严格按
    照这个顺序才行,并且,新创建出来的Pod,必须和原来的Pod的网络标识一样,这样原先的访问者才能使用同样
    的方法,访问到这个新的Pod

    存储状态:应用的多个实例分别绑定了不同的存储数据.对于这些应用实例来说,Pod A第一次读取到的数据,和
    隔了十分钟之后再次读取到的数据,应该是同一份,哪怕在此期间Pod A被重新创建过.一个数据库应用的多个
    存储实例

statefulset 部署redis的架构图

Kubernetes 通过statefulset部署redis cluster集群
无论是Master 还是 slave都作为statefulset的一个副本,通过pv/pvc进行持久化,对外暴露一个service 接受客户端请求

部署

安装NFS(共享存储)

因为k8s上pod是飘忽不定的,所以我们肯定需要用一个共享存储来提供存储,这样不管pod漂移到哪个节点都能访问这个共享的数据卷.我这个地方先使用NFS来做共享存储,后期可以 选择别的替换

yum -y install nfs-utils rpcbind
vim /etc/exports
/usr/local/kubernetes/redis/pv1 0.0.0.0/0(rw,all_squash)
/usr/local/kubernetes/redis/pv2 0.0.0.0/0(rw,all_squash)
/usr/local/kubernetes/redis/pv3 0.0.0.0/0(rw,all_squash)
/usr/local/kubernetes/redis/pv4 0.0.0.0/0(rw,all_squash)
/usr/local/kubernetes/redis/pv5 0.0.0.0/0(rw,all_squash)
/usr/local/kubernetes/redis/pv6 0.0.0.0/0(rw,all_squash)
mkdir -p /usr/local/kubernetes/redis/pv{1..6}
chmod 777 /usr/local/kubernetes/redis/pv{1..6}

后期我们可以写成域名 通配符

启动服务
systemctl enable nfs 
systemctl enable rpcbind
systemctl start nfs
systemctl start rpcbind

创建pv

创建6个pv 一会供pvc挂载使用

vim pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs-pv1
spec:
  capacity:
    storage: 200M      #磁盘大小200M
  accessModes:
    - ReadWriteMany    #多客户可读写
  nfs:
    server: NFS服务器地址
    path: "/usr/local/kubernetes/redis/pv1"

---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs-vp2
spec:
  capacity:
    storage: 200M
  accessModes:
    - ReadWriteMany
  nfs:
    server: NFS服务器地址
    path: "/usr/local/kubernetes/redis/pv2"

---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs-pv3
spec:
  capacity:
    storage: 200M
  accessModes:
    - ReadWriteMany
  nfs:
    server: NFS服务器地址
    path: "/usr/local/kubernetes/redis/pv3"

---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs-pv4
spec:
  capacity:
    storage: 200M
  accessModes:
    - ReadWriteMany
  nfs:
    server: NFS服务器地址
    path: "/usr/local/kubernetes/redis/pv4"

---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs-pv5
spec:
  capacity:
    storage: 200M
  accessModes:
    - ReadWriteMany
  nfs:
    server: NFS服务器地址
    path: "/usr/local/kubernetes/redis/pv5"

---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs-pv6
spec:
  capacity:
    storage: 200M
  accessModes:
    - ReadWriteMany
  nfs:
    server: NFS服务器地址
    path: "/usr/local/kubernetes/redis/pv6"

字段说明:
apiversion:     api版本
kind:           这个yaml是生成pv的
metadata:       元数据
spec.capacity:  进行资源限制的
spec.accessmodes: 访问模式(读写模式)
spec.nfs:       这个pv卷名是通过nfs提供的

创建pv

kubectl create -f pv.yaml
kubectl get pv   #查看创建的pv

创建configmap,用来存放redis的配置文件

因为redis的配置文件里面可能会改变,所以我们使用configmap这种方式给配置文件弄出来,我们后期改的时候就不需要没改次配置文件就从新生成一个docker images包了

appendonly yes                      #开启Redis的AOF持久化cluster-enabled yes                 #集群模式打开cluster-config-file /var/lib/redis/nodes.conf  #下面说明cluster-node-timeout 5000           #节点超时时间dir /var/lib/redis                  #AOF持久化文件存在的位置port 6379                           #开启的端口

cluster-conf-file: 选项设定了保存节点配置文件的路径,如果这个配置文件不存在,每个节点在启动的时候都为他自身指定了一个新的ID存档到这个文件中,实例会一直使用同一个ID,在集群中保持一个独一无二的(Unique)名字.每个节点都是用ID而不是IP或者端口号来记录其他节点,因为在k8s中,IP地址是不固定的,而这个独一无二的标识符(Identifier)则会在节点的整个生命周期中一直保持不变,我们这个文件里面存放的是节点ID

创建名为redis-conf的Configmap:

kubectl create configmap redis-conf --from-file=redis.conf

查看:

[root@rke ~]# kubectl get cmNAME         DATA   AGE
redis-conf   1      22h
[root@rke ~]# kubectl describe cm redis-confName:         redis-conf
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
redis.conf:
----
appendonly yes
cluster-enabled yes
cluster-config-file /var/lib/redis/nodes.conf
cluster-node-timeout 5000
dir /var/lib/redis
port 6379

Events:  <none>

创建headless service

Headless service是StatefulSet实现稳定网络标识的基础,我们需要提前创建。准备文件headless-service.yml如下:

apiVersion: v1
kind: Service
metadata:
  name: redis-service
  labels:
    app: redis
spec:
  ports:
  - name: redis-port
    port: 6379
  clusterIP: None
  selector:
    app: redis
    appCluster: redis-cluster

创建:

kubectl create -f headless-service.yml

查看:

[root@k8s-node1 redis]# kubectl get svc redis-serviceNAME            TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
redis-service   ClusterIP   None         <none>        6379/TCP   53s

可以看到,服务名称为redis-service,其CLUSTER-IP为None,表示这是一个“无头”服务。

创建redis集群节点

这是本文的核心内容,创建redis.yaml文件

[root@rke ~]# cat /home/docker/redis/redis.yml
apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
  name: redis-app
spec:
  serviceName: "redis-service"
  replicas: 6
  template:
    metadata:
      labels:
        app: redis
        appCluster: redis-cluster
    spec:
      terminationGracePeriodSeconds: 20
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 100
            podAffinityTerm:
              labelSelector:
                matchExpressions:
                - key: app
                  operator: In
                  values:
                  - redis
              topologyKey: kubernetes.io/hostname
      containers:
      - name: redis
        image: "redis"
        command:
          - "redis-server"                  #redis启动命令
        args:
          - "/etc/redis/redis.conf"         #redis-server后面跟的参数,换行代表空格
          - "--protected-mode"              #允许外网访问
          - "no"
        # command: redis-server /etc/redis/redis.conf --protected-mode no
        resources:                          #资源
          requests:                         #请求的资源
            cpu: "100m"                     #m代表千分之,相当于0.1 个cpu资源
            memory: "100Mi"                 #内存100m大小
        ports:
            - name: redis
              containerPort: 6379
              protocol: "TCP"
            - name: cluster
              containerPort: 16379
              protocol: "TCP"
        volumeMounts:
          - name: "redis-conf"              #挂载configmap生成的文件
            mountPath: "/etc/redis"         #挂载到哪个路径下
          - name: "redis-data"              #挂载持久卷的路径
            mountPath: "/var/lib/redis"
      volumes:
      - name: "redis-conf"                  #引用configMap卷
        configMap:
          name: "redis-conf"
          items:
            - key: "redis.conf"             #创建configMap指定的名称
              path: "redis.conf"            #里面的那个文件--from-file参数后面的文件
  volumeClaimTemplates:                     #进行pvc持久卷声明,
  - metadata:
      name: redis-data
    spec:
      accessModes:
      - ReadWriteMany
      resources:
        requests:
          storage: 200M

PodAntiAffinity:表示反亲和性,其决定了某个pod不可以和哪些Pod部署在同一拓扑域,可以用于将一个服务的POD分散在不同的主机或者拓扑域中,提高服务本身的稳定性。
matchExpressions:规定了Redis Pod要尽量不要调度到包含app为redis的Node上,也即是说已经存在Redis的Node上尽量不要再分配Redis Pod了.
另外,根据StatefulSet的规则,我们生成的Redis的6个Pod的hostname会被依次命名为$(statefulset名称)-$(序号),如下图所示:

[root@rke ~]# kubectl get pods -o wideNAME                                     READY   STATUS    RESTARTS   AGE   IP           NODE            NOMINATED NODE
redis-app-0                              1/1     Running   0          40m   10.42.2.17   192.168.1.21    <none>
redis-app-1                              1/1     Running   0          40m   10.42.0.15   192.168.1.114   <none>
redis-app-2                              1/1     Running   0          40m   10.42.1.13   192.168.1.20    <none>
redis-app-3                              1/1     Running   0          40m   10.42.2.18   192.168.1.21    <none>
redis-app-4                              1/1     Running   0          40m   10.42.0.16   192.168.1.114   <none>
redis-app-5                              1/1     Running   0          40m   10.42.1.14   192.168.1.20    <none>

如上,可以看到这些Pods在部署时是以{0..N-1}的顺序依次创建的。注意,直到redis-app-0状态启动后达到Running状态之后,redis-app-1 才开始启动。
同时,每个Pod都会得到集群内的一个DNS域名,格式为$(podname).$(service name).$(namespace).svc.cluster.local,也即是:

redis-app-0.redis-service.default.svc.cluster.local
redis-app-1.redis-service.default.svc.cluster.local
...以此类推...

在K8S集群内部,这些Pod就可以利用该域名互相通信。我们可以使用busybox镜像的nslookup检验这些域名:

[root@k8s-node1 ~]# kubectl run -i --tty --image busybox dns-test --restart=Never --rm /bin/sh If you don't see a command prompt, try pressing enter.
/ # nslookup redis-app-1.redis-service.default.svc.cluster.local
Server:     10.43.0.10
Address:    10.43.0.10:53

Name:   redis-app-1.redis-service.default.svc.cluster.local
Address: 10.42.0.15

*** Can't find redis-app-1.redis-service.default.svc.cluster.local: No answer

/ # nslookup redis-app-0.redis-service.default.svc.cluster.localServer:     10.43.0.10
Address:    10.43.0.10:53

Name:   redis-app-0.redis-service.default.svc.cluster.local
Address: 10.42.2.17

可以看到, redis-app-0的IP为10.42.2.17。当然,若Redis Pod迁移或是重启(我们可以手动删除掉一个Redis Pod来测试),则IP是会改变的,但Pod的域名、SRV records、A record都不会改变。
另外可以发现,我们之前创建的pv都被成功绑定了:

[root@k8s-node1 ~]# kubectl get pvNAME      CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS    CLAIM                            STORAGECLASS   REASON    AGE
nfs-pv1   200M       RWX            Retain           Bound     default/redis-data-redis-app-2                            1h
nfs-pv2   200M       RWX            Retain           Bound     default/redis-data-redis-app-3                            1h
nfs-pv3   200M       RWX            Retain           Bound     default/redis-data-redis-app-4                            1h
nfs-pv4   200M       RWX            Retain           Bound     default/redis-data-redis-app-5                            1h
nfs-pv5   200M       RWX            Retain           Bound     default/redis-data-redis-app-0                            1h
nfs-pv6   200M       RWX            Retain           Bound     default/redis-data-redis-app-1                            1h

初始化redis集群

创建好6个Redis Pod后,我们还需要利用常用的Redis-tribe工具进行集群的初始化。

创建centos容器

由于Redis集群必须在所有节点启动后才能进行初始化,而如果将初始化逻辑写入Statefulset中,则是一件非常复杂而且低效的行为。这里,本人不得不称赞一下原项目作者的思路,值得学习。也就是说,我们可以在K8S上创建一个额外的容器,专门用于进行K8S集群内部某些服务的管理控制。
这里,我们专门启动一个Ubuntu的容器,可以在该容器中安装Redis-tribe,进而初始化Redis集群,执行:

kubectl run -i --tty centos --image=centos --restart=Never /bin/bash

成功后,我们可以进入centos容器中,原项目要求执行如下命令安装基本的软件环境:

cat >> /etc/yum.repo.d/epel.repo<<'EOF'[epel]
name=Extra Packages for Enterprise Linux 7 - $basearchbaseurl=https://mirrors.tuna.tsinghua.edu.cn/epel/7/$basearch#mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=epel-7&arch=$basearchfailovermethod=priority
enabled=1
gpgcheck=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-7
EOF

初始化redis集群
首先,我们需要安装redis-trib(redis集群命令行工具):

yum -y install redis-trib.noarch bind-utils-9.9.4-72.el7.x86_64

然后创建一主一从的集群节点信息:

redis-trib create --replicas 1 \
`dig +short redis-app-0.redis-service.default.svc.cluster.local`:6379 \
`dig +short redis-app-1.redis-service.default.svc.cluster.local`:6379 \
`dig +short redis-app-2.redis-service.default.svc.cluster.local`:6379 \
`dig +short redis-app-3.redis-service.default.svc.cluster.local`:6379 \
`dig +short redis-app-4.redis-service.default.svc.cluster.local`:6379 \
`dig +short redis-app-5.redis-service.default.svc.cluster.local`:6379#create: 创建一个新的集群#--replicas 1 : 创建的集群中每个主节点分配一个从节点,达到3主3从#后面跟的就是redis实例所在的位置

如上,命令dig +short redis-app-0.redis-service.default.svc.cluster.local用于将Pod的域名转化为IP,这是因为redis-trib不支持域名来创建集群。
执行完成后redis-trib会打印一份预配置文件给你查看,如果没问题输入yes,redis-trib就会把这份配置文件应用到集群中

>>> Creating cluster
>>> Performing hash slots allocation on 6 nodes...
Using 3 masters:
10.42.2.17:6379
10.42.0.15:6379
10.42.1.13:6379
Adding replica 10.42.2.18:6379 to 10.42.2.17:6379
Adding replica 10.42.0.16:6379 to 10.42.0.15:6379
Adding replica 10.42.1.14:6379 to 10.42.1.13:6379
M: 4676f8913cdcd1e256db432531c80591ae6c5fc3 10.42.2.17:6379
   slots:0-5460 (5461 slots) master
M: 505f3e126882c0c5115885e54f9b361bc7e74b97 10.42.0.15:6379
   slots:5461-10922 (5462 slots) master
M: 589b4f4f908a04f56d2ab9cd6fd0fd25ea14bb8f 10.42.1.13:6379
   slots:10923-16383 (5461 slots) master
S: 366abbba45d3200329a5c6305fbcec9e29b50c80 10.42.2.18:6379
   replicates 4676f8913cdcd1e256db432531c80591ae6c5fc3
S: cee3a27cc27635da54d94f16f6375cd4acfe6c30 10.42.0.16:6379
   replicates 505f3e126882c0c5115885e54f9b361bc7e74b97
S: e9f1f704ff7c8f060d6b39e23be9cd8e55cb2e46 10.42.1.14:6379
   replicates 589b4f4f908a04f56d2ab9cd6fd0fd25ea14bb8f
Can I set the above configuration? (type 'yes' to accept):

输入yes后开始创建集群

>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join...
>>> Performing Cluster Check (using node 10.42.2.17:6379)
M: 4676f8913cdcd1e256db432531c80591ae6c5fc3 10.42.2.17:6379
   slots:0-5460 (5461 slots) master
   1 additional replica(s)
M: 589b4f4f908a04f56d2ab9cd6fd0fd25ea14bb8f 10.42.1.13:6379@16379
   slots:10923-16383 (5461 slots) master
   1 additional replica(s)
S: e9f1f704ff7c8f060d6b39e23be9cd8e55cb2e46 10.42.1.14:6379@16379
   slots: (0 slots) slave
   replicates 589b4f4f908a04f56d2ab9cd6fd0fd25ea14bb8f
S: 366abbba45d3200329a5c6305fbcec9e29b50c80 10.42.2.18:6379@16379
   slots: (0 slots) slave
   replicates 4676f8913cdcd1e256db432531c80591ae6c5fc3
M: 505f3e126882c0c5115885e54f9b361bc7e74b97 10.42.0.15:6379@16379
   slots:5461-10922 (5462 slots) master
   1 additional replica(s)
S: cee3a27cc27635da54d94f16f6375cd4acfe6c30 10.42.0.16:6379@16379
   slots: (0 slots) slave
   replicates 505f3e126882c0c5115885e54f9b361bc7e74b97
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

最后一句表示集群中的16384个槽都有至少一个主节点在处理, 集群运作正常.

至此,我们的Redis集群就真正创建完毕了,连到任意一个Redis Pod中检验一下:

root@k8s-node1 ~]# kubectl exec -it redis-app-2 /bin/bashroot@redis-app-2:/data# /usr/local/bin/redis-cli -c127.0.0.1:6379> 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:186
cluster_stats_messages_pong_sent:199
cluster_stats_messages_sent:385
cluster_stats_messages_ping_received:194
cluster_stats_messages_pong_received:186
cluster_stats_messages_meet_received:5
cluster_stats_messages_received:385

127.0.0.1:6379> cluster nodes
589b4f4f908a04f56d2ab9cd6fd0fd25ea14bb8f 10.42.1.13:6379@16379 master - 0 1550555011000 3 connected 10923-16383
e9f1f704ff7c8f060d6b39e23be9cd8e55cb2e46 10.42.1.14:6379@16379 slave 589b4f4f908a04f56d2ab9cd6fd0fd25ea14bb8f 0 1550555011512 6 connected
366abbba45d3200329a5c6305fbcec9e29b50c80 10.42.2.18:6379@16379 slave 4676f8913cdcd1e256db432531c80591ae6c5fc3 0 1550555010507 4 connected
505f3e126882c0c5115885e54f9b361bc7e74b97 10.42.0.15:6379@16379 master - 0 1550555011000 2 connected 5461-10922
cee3a27cc27635da54d94f16f6375cd4acfe6c30 10.42.0.16:6379@16379 slave 505f3e126882c0c5115885e54f9b361bc7e74b97 0 1550555011713 5 connected
4676f8913cdcd1e256db432531c80591ae6c5fc3 10.42.2.17:6379@16379 myself,master - 0 1550555010000 1 connected 0-5460

另外,还可以在NFS上查看Redis挂载的数据:

[root@rke ~]# tree /usr/local/kubernetes/redis//usr/local/kubernetes/redis/
├── pv1
│   ├── appendonly.aof
│   ├── dump.rdb
│   └── nodes.conf
├── pv2
│   ├── appendonly.aof
│   ├── dump.rdb
│   └── nodes.conf
├── pv3
│   ├── appendonly.aof
│   ├── dump.rdb
│   └── nodes.conf
├── pv4
│   ├── appendonly.aof
│   ├── dump.rdb
│   └── nodes.conf
├── pv5
│   ├── appendonly.aof
│   ├── dump.rdb
│   └── nodes.conf
└── pv6
    ├── appendonly.aof
    ├── dump.rdb
    └── nodes.conf

6 directories, 18 files

创建用于访问service

前面我们创建了用于实现statefulset的headless service,但该service没有cluster IP,因此不能用于外界访问.所以我们还需要创建一个service,专用于为Redis集群提供访问和负载均衡:

piVersion: v1
kind: Service
metadata:
  name: redis-access-service
  labels:
    app: redis
spec:
  ports:
  - name: redis-port
    protocol: "TCP"
    port: 6379
    targetPort: 6379
  selector:
    app: redis
    appCluster: redis-cluster

如上,该Service名称为 redis-access-service,在K8S集群中暴露6379端口,并且会对labels nameapp: redisappCluster: redis-cluster的pod进行负载均衡。
创建后查看:

[root@rke ~]# kubectl get svc redis-access-service -o wideNAME                   TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)    AGE   SELECTOR
redis-access-service   ClusterIP   10.43.40.62   <none>        6379/TCP   47m   app=redis,appCluster=redis-cluster

如上,在k8s集群中,所有应用都可以通过10.43.40.62:6379来访问redis集群,当然,为了方便测试,我们也可以为Service添加一个NodePort映射到物理机上,待测试。

测试主从切换

在K8S上搭建完好Redis集群后,我们最关心的就是其原有的高可用机制是否正常。这里,我们可以任意挑选一个Master的Pod来测试集群的主从切换机制,如redis-app-2

[root@rke ~]# kubectl get pods redis-app-2 -o wideNAME          READY   STATUS    RESTARTS   AGE   IP           NODE           NOMINATED NODE
redis-app-2   1/1     Running   0          1h    10.42.1.13   192.168.1.20   <none>

进入redis-app-2查看:

[root@rke ~]# kubectl exec -it redis-app-2 /bin/bashroot@redis-app-2:/data# redis-cli127.0.0.1:6379> role
1) "master"2) (integer) 9478
3) 1) 1) "10.42.1.14"
      2) "6379"
      3) "9478"

如上可以看到,其为master,slave10.42.1.14redis-app-5

接着,我们手动删除redis-app-2

[root@rke ~]# kubectl delete pods redis-app-2pod "redis-app-2" deleted
[root@rke ~]# kubectl get pods redis-app-2 -o wideNAME          READY   STATUS    RESTARTS   AGE   IP           NODE           NOMINATED NODE
redis-app-2   1/1     Running   0          19s   10.42.1.15   192.168.1.20   <none>

如上,IP改变为10.42.1.15。我们再进入redis-app-2内部查看:

[root@rke ~]# kubectl exec -it redis-app-2 /bin/bashroot@redis-app-2:/data# redis-cli127.0.0.1:6379> ROLE
1) "slave"2) "10.42.1.14"3) (integer) 6379
4) "connected"5) (integer) 9688

如上,redis-app-2变成了slave,从属于它之前的从节点10.42.1.14redis-app-5

redis动态扩容

我们现在这个集群中有6个节点三主三从,我现在添加两个pod节点,达到4主4从

添加nfs共享目录

cat >> /etc/exports <<'EOF'/usr/local/kubernetes/redis/pv7 192.168.0.0/16(rw,all_squash)
/usr/local/kubernetes/redis/pv8 192.168.0.0/16(rw,all_squash)
EOF
systemctl restart nfs rpcbind
[root@rke ~]# mkdir /usr/local/kubernetes/redis/pv{7..8}[root@rke ~]# chmod 777 /usr/local/kubernetes/redis/*

更新pv的yml文件,也可以自己在重新创建一个,这边选择自己新建

[root@rke redis]# cat pv_add.yml
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs-pv7
spec:
  capacity:
    storage: 200M
  accessModes:
    - ReadWriteMany
  nfs:
    server: 192.168.1.253
    path: "/usr/local/kubernetes/redis/pv7"

---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs-pv8
spec:
  capacity:
    storage: 200M
  accessModes:
    - ReadWriteMany
  nfs:
    server: 192.168.1.253
    path: "/usr/local/kubernetes/redis/pv8"

创建查看pv:

[root@rke redis]# kubectl create -f pv_add.ymlpersistentvolume/nfs-pv7 created
persistentvolume/nfs-pv8 created
[root@rke redis]# kubectl get pvNAME          CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM                            STORAGECLASS   REASON   AGE
nfs-pv1       200M       RWX            Retain           Bound       default/redis-data-redis-app-1                           2h
nfs-pv2       200M       RWX            Retain           Bound       default/redis-data-redis-app-2                           2h
nfs-pv3       200M       RWX            Retain           Bound       default/redis-data-redis-app-4                           2h
nfs-pv4       200M       RWX            Retain           Bound       default/redis-data-redis-app-5                           2h
nfs-pv5       200M       RWX            Retain           Bound       default/redis-data-redis-app-0                           2h
nfs-pv6       200M       RWX            Retain           Bound       default/redis-data-redis-app-3                           2h
nfs-pv7       200M       RWX            Retain           Available                                                            7s
nfs-pv8       200M       RWX            Retain           Available                                                            7s

添加redis节点

更改redis的yml文件里面的replicas:字段,把这个字段改为8,然后升级运行

[root@rke redis]# kubectl apply -f redis.ymlWarning: kubectl apply should be used on resource created by either kubectl create --save-config or kubectl apply
statefulset.apps/redis-app configured

[root@rke redis]# kubectl get  podsNAME                                     READY   STATUS    RESTARTS   AGE
redis-app-0                              1/1     Running   0          2h
redis-app-1                              1/1     Running   0          2h
redis-app-2                              1/1     Running   0          19m
redis-app-3                              1/1     Running   0          2h
redis-app-4                              1/1     Running   0          2h
redis-app-5                              1/1     Running   0          2h
redis-app-6                              1/1     Running   0          57s
redis-app-7                              1/1     Running   0          30s

添加集群节点

[root@rke redis]#kubectl exec -it centos /bin/bash[root@centos /]# redis-trib add-node \`dig +short redis-app-6.redis-service.default.svc.cluster.local`:6379 \
`dig +short redis-app-0.redis-service.default.svc.cluster.local`:6379

[root@centos /]# redis-trib add-node \`dig +short redis-app-7.redis-service.default.svc.cluster.local`:6379 \
`dig +short redis-app-0.redis-service.default.svc.cluster.local`:6379

add-node后面跟的是新节点的信息,后面是以前集群中的任意 一个节点

查看添加redis节点是否正常

[root@rke redis]# kubectl exec -it redis-app-0 bashroot@redis-app-0:/data# redis-cli127.0.0.1:6379> cluster nodes
589b4f4f908a04f56d2ab9cd6fd0fd25ea14bb8f 10.42.1.15:6379@16379 slave e9f1f704ff7c8f060d6b39e23be9cd8e55cb2e46 0 1550564776000 7 connected
e9f1f704ff7c8f060d6b39e23be9cd8e55cb2e46 10.42.1.14:6379@16379 master - 0 1550564776000 7 connected 10923-16383
366abbba45d3200329a5c6305fbcec9e29b50c80 10.42.2.18:6379@16379 slave 4676f8913cdcd1e256db432531c80591ae6c5fc3 0 1550564777051 4 connected
505f3e126882c0c5115885e54f9b361bc7e74b97 10.42.0.15:6379@16379 master - 0 1550564776851 2 connected 5461-10922
cee3a27cc27635da54d94f16f6375cd4acfe6c30 10.42.0.16:6379@16379 slave 505f3e126882c0c5115885e54f9b361bc7e74b97 0 1550564775000 5 connected
e4697a7ba460ae2979692116b95fbe1f2c8be018 10.42.0.20:6379@16379 master - 0 1550564776549 0 connected
246c79682e6cc78b4c2c28d0e7166baf47ecb265 10.42.2.23:6379@16379 master - 0 1550564776548 8 connected
4676f8913cdcd1e256db432531c80591ae6c5fc3 10.42.2.17:6379@16379 myself,master - 0 1550564775000 1 connected 0-5460

重新分配哈希槽

redis-trib.rb reshard `dig +short redis-app-0.redis-service.default.svc.cluster.local`:6379## 输入要移动的哈希槽## 移动到哪个新的master节点(ID)## all 是从所有master节点上移动

查看对应的节点信息

127.0.0.1:6379> cluster nodes
589b4f4f908a04f56d2ab9cd6fd0fd25ea14bb8f 10.42.1.15:6379@16379 slave e9f1f704ff7c8f060d6b39e23be9cd8e55cb2e46 0 1550566162000 7 connected
e9f1f704ff7c8f060d6b39e23be9cd8e55cb2e46 10.42.1.14:6379@16379 master - 0 1550566162909 7 connected 11377-16383
366abbba45d3200329a5c6305fbcec9e29b50c80 10.42.2.18:6379@16379 slave 4676f8913cdcd1e256db432531c80591ae6c5fc3 0 1550566161600 4 connected
505f3e126882c0c5115885e54f9b361bc7e74b97 10.42.0.15:6379@16379 master - 0 1550566161902 2 connected 5917-10922
cee3a27cc27635da54d94f16f6375cd4acfe6c30 10.42.0.16:6379@16379 slave 505f3e126882c0c5115885e54f9b361bc7e74b97 0 1550566162506 5 connected
246c79682e6cc78b4c2c28d0e7166baf47ecb265 10.42.2.23:6379@16379 master - 0 1550566161600 8 connected 0-453 5461-5916 10923-11376
4676f8913cdcd1e256db432531c80591ae6c5fc3 10.42.2.17:6379@16379 myself,master - 0 1550566162000 1 connected 454-5460

对redis的相关操作可以查看:http://redisdoc.com/topic/cluster-tutorial.html#id10



0分/0个投票

TOP


浏览: 182    评论: 0
喜欢哪一款阴唇?。。。

7a20ed33ly1gg3n25zfb2j20kw2efgwb.jpg


0分/0个投票

TOP


浏览: 155    评论: 0
«   2024年6月   »
12
3456789
10111213141516
17181920212223
24252627282930
TOP 搜索
TOP 控制面板
您好,欢迎到访网站!
  [查看权限]
TOP 网站分类
TOP 热门评分
TOP 最高评分
TOP 最新评分
TOP 你好,朋友
真是美好的一天!
TOP 站点信息
  • 文章总数:5538
  • 页面总数:3
  • 分类总数:4
  • 标签总数:6
  • 评论总数:36
  • 浏览总数:8188776
返回顶部
haose888
返回顶部