华为云用户手册

  • 优化缓存命中率 合理设置缓存过期时间 CDN加速的本质是缓存加速,把源站资源缓存在遍布全球的节点上,用户可以就近从边缘节点获取资源,从而达到加速的效果。您可以通过CDN控制台合理设置缓存过期时间来提高缓存命中率,建议如下: 对于不经常更新的静态文件(如图片类型、应用下载类型等),建议您将缓存时间设置为1个月以上。 对于频繁更新的静态文件(如JS、CSS等),您可以根据实际业务情况设置。 对于动态文件(如PHP、JSP、ASP等),建议您将缓存时间设置为0,即不缓存。 详细的设置步骤和注意事项请见如何设置缓存过期时间。 如果源站设置了s-maxage=0、max-age=0、no-cache、no-store、private,CDN侧同时开启了“缓存遵循源站”功能(此功能默认关闭),CDN节点将无法缓存源站资源,导致频繁回源。 如果您的源站有多个主机,某个相同的资源在多个主机中的Last-modified、Etag、Content-Length不一致,CDN节点将无法缓存该资源,导致重复回源。 如果源站资源更新,请刷新资源对应的URL,以保证用户可以获得最新的资源。 如果您修改了缓存规则: 新的规则仅对后面缓存的资源生效,已经缓存的资源需要等缓存过期后,再次缓存才会遵循新的缓存规则。 如果您想要立即生效,请在修改缓存规则后执行缓存刷新操作。 配置URL参数 目前大多数的网页请求都携带URL参数信息,参数以“?”开始,如果参数没有包含重要信息(如版本信息等),是否携带该参数访问不会影响用户获得正确的资源,可以选择将“URL参数”功能配置为“忽略参数”或“忽略指定参数”,提高缓存命中率,提升分发效率,详见URL参数。 典型应用: 终端用户首次访问URL“http://www.example.com/1.txt?test1”时,CDN无缓存,回源请求资源;第二次访问“http://www.example.com/1.txt?test2”时,由于配置了“URL参数”的“忽略参数”功能,所以“?”之后的参数不匹配,直接命中缓存“http://www.example.com/1.txt”。 终端用户首次访问URL“http://www.example.com/1.txt?test1”时,CDN无缓存,回源请求资源;第二次访问“http://www.example.com/1.txt?test2”时,由于“URL参数”功能配置为“不忽略参数”,所以“?”之后的参数也需要匹配,要重新回源请求“http://www.example.com/1.txt?test2”。 预热URL CDN可以通过缓存预热将源站资源主动缓存到CDN节点,用户访问时就能直接从CDN节点获取到最新的资源,详见缓存预热。 当您的域名初次接入CDN加速、活动发布时您可以将源站资源预热到CDN节点,用户访问资源时直接从CDN节点获取,从而提升CDN的缓存命中率。 典型场景: 初次接入CDN:域名初次接入CDN时,节点暂未缓存源站资源,此时,您可以将源站资源预热至CDN节点。后续用户访问资源将直接从就近的CDN节点获取资源,提升访问速度。 安装包发布:新版本安装包或是升级包发布前,提前将资源预热至CDN节点。正式上线后,海量用户的下载请求将直接由全球加速节点响应,提升下载速度的同时,大幅度降低源站压力。 运营活动:运营活动发布前,提前将活动页涉及到的静态资源预热至CDN节点。活动开始后,用户访问中所有静态资源均由加速节点响应,海量带宽储备保障用户服务可用性,提升用户体验。 开启Range回源 Range回源是指源站在收到CDN节点回源请求时,根据HTTP请求头中的Range信息返回指定范围的数据给CDN节点。Range回源能有效缩短大文件的分发时间,提升回源效率,提高缓存命中率,详见Range回源。 典型场景: 未开通Range时,用户想观看指定片段的视频,而CDN回源时需要获取整个视频,所以回源流量大于响应给用户的流量,从而造成缓存命中率降低。开启Range回源后,CDN将分片回源获取资源返回给用户,从而提升缓存命中率。 其它 缓存资源需要更新时,尽量避免刷新目录 当源站某个资源更新时,一般需要通过刷新相应的URL来强制节点缓存资源过期。刷新目录会将目录内所有的资源全部置为过期,用户下次访问时将无法命中缓存,全部回源站请求资源,因此尽量避免刷新整个目录,尤其慎重刷新根目录。 避免在URL中携带动态参数 如果您的URL中包含动态参数,如时间戳,CDN无法缓存该资源,导致频繁回源。
  • 判断URL是否命中缓存 在浏览器Chrome上,按F12。 选择“Network”。 查看指定URL的响应头,查看头部信息,进行如下判断: 如果有“x-hcs-proxy-type”头部,值为“1”即命中缓存,值为“0”即未命中缓存,不再查看其它头部; 如果无“x-hcs-proxy-type”头部,而有“X-Cache-Lookup”头部,值为“Hit From MemCache”、“Hit From Disktank”或“Hit From Upstream”即为命中缓存,其它值表示未命中缓存,不再查看其它头部; 如果同时无“x-hcs-proxy-type”、“X-Cache-Lookup”头部,有“age”头部,则值大于“0”即命中缓存,值为“0”即未命中缓存。
  • 响应示例 { "dedicated_hosts": [ { "dedicated_host_id": "d465d0ae-f859-4a83-a508-8db654c05e7e", "name": "DEH001", "auto_placement": "off", "availability_zone": "cn-east-3a", "host_properties": { "vcpus": 74, "cores": 22, "sockets": 2, "memory": 151552, "host_type": "c6", "host_type_name": "dedicated_general_purpose", "available_instance_capacities": [ { "flavor": "c6.large.2" }, { "flavor": "c6.xlarge.2" }, { "flavor": "c6.2xlarge.2" }, { "flavor": "c6.3xlarge.2" }, { "flavor": "c6.6xlarge.2" }, { "flavor": "c6.16xlarge.2" } ] }, "state": "available", "project_id": "9c53a566cb3443ab910cf0daebca90c4", "available_vcpus": 20, "available_memory": 40960, "instance_total": 5, "allocated_at": "2016-10-10T14:35:47Z", "released_at": "" } ], "total": 1 }
  • 请求消息 可以在URI中增加如下参数,来过滤查询结果:host_type、host_type_name、flavor、dedicated_host_id、state、tenant、availability_zone、name、limit、marker、tags、instance_uuid、或者changes-since。 例如:/v1.0/{project_id}/dedicated-hosts?host_type={host_type}&state={state} 表2 请求参数 参数 位置 参数类型 是否必选 描述 dedicated_host_id query String 否 专属主机ID。 name query String 否 专属主机名称。 host_type query String 否 专属主机类型。 host_type_name query String 否 专属主机类型的名称。 flavor query String 否 规格ID。 state query String 否 专属主机状态。 取值范围:“available”、“fault”或“released”。 tenant query String 否 取值范围:租户ID或“all”。 只有具有DeH管理员权限的用户可以指定该参数。 availability_zone query String 否 专属主机所属AZ。 limit query String 否 每个页面上显示的条目数。 marker query String 否 该值是上一页最后一条记录的ID。如果“marker”取值无效,将会返回“400”错误码。 tags query String 否 专属主机标签。 instance_uuid query String 否 专属主机上的云服务器ID。 changes-since query String 否 当专属主机更新了状态时,按日期和时间戳过滤响应。为了便于记录更改,还可能返回最近删除的专属主机。 日期和时间戳的格式为ISO 8601: CCYY-MM-DDThh:mm:ss±hh:mm 如果包含“hh:mm”值,则将时区作为UTC的偏移量返回。例如,“2015-08-27T09:49:58-05:00”。如果您省略时区,则假定为UTC时区。
  • 结果验证 安全组规则配置完成后,需要验证对应的规则是否生效。假设您在云服务器上部署了网站,希望用户能通过HTTP(80端口)访问到您的网站,您添加了一条入方向规则,如表3所示。 表3 安全组规则 方向 协议/应用 端口 源地址 入方向 TCP 80 0.0.0.0/0 Linux云服务器 Linux云服务器上验证该安全组规则是否生效的步骤如下所示。 登录云服务器。 运行如下命令查看TCP 80端口是否被监听。 netstat -an | grep 80 如果返回结果如图2所示,说明TCP 80端口已开通。 图2 Linux TCP 80端口验证结果 在浏览器地址栏里输入“http://云服务器公网IP地址”。 如果访问成功,说明安全组规则已经生效。
  • 相关操作 在“入方向规则”和“出方向规则”页签,您也可以对已有的规则进行修改、复制或删除。 删除安全组规则会导致部分功能无法使用: 删除TCP(20-21)规则会导致无法通过FTP协议向云服务器上传或下载文件。 删除ICMP规则会导致使用Ping程序测试云服务器无法连通。 删除TCP(443)规则会导致无法使用HTTPS协议访问网站。 删除TCP(80)规则会导致无法使用HTTP协议访问网站。 删除TCP(22)规则会导致无法通过SSH协议远程连接到Linux操作系统云服务器。 删除TCP(3389)规则会导致无法通过RDP协议远程连接Windows操作系统云服务器。
  • 不同安全组内云服务器内网互通 场景举例: 在同一个VPC内,用户需要将某个安全组内一台云服务器上的资源复制到另一个安全组内的云服务器上时,用户可以将两台云服务器设置为内网互通后再复制资源。 安全组配置方法: 同一个VPC内,在同一个安全组内的云服务器默认互通。但是,在不同安全组内的云服务器默认无法通信,此时需要添加安全组规则,使得不同安全组内的云服务器内网互通。 在两台云服务器所在安全组中分别添加一条入方向安全组规则,放通来自另一个安全组内的实例的访问,实现内网互通,安全组规则如下所示。 方向 协议/应用 端口 源地址 入方向 TCP 说明: 此处的协议类型设置内网互通时使用的协议类型。 全部 另一个安全组的ID。 例如:014d7278-XXX-530c95350d43 如果同一个安全组内的云服务器也无法互相通信,请您检查是否已删除对应的规则。 如下所示,以安全组sg-demo为例,“源地址”为sg-demo的规则可以实现同一个安全组内云服务通信。
  • 仅允许特定IP地址远程连接云服务器 场景举例: 为了防止云服务器被网络攻击,用户可以修改远程登录端口号,并设置安全组规则只允许特定的IP地址远程登录到云服务器。 安全组配置方法: 以仅允许特定IP地址(例如,192.168.20.2)通过SSH协议访问Linux操作系统的弹性云服务器的22端口为例,安全组规则如下所示。 方向 协议/应用 端口 源地址 入方向 SSH(22) 22 IPv4 CIDR或者另一个安全组的ID。 例如:192.168.20.2/32
  • 数据库基本设计规范 所有表如果没有特殊需求,都要使用InnoDB存储引擎。InnoDB存储引擎支持事务、行级锁、具有更好的恢复性、高并发下性能更强。 数据库和表的字符集统一使用UTF8字符集,避免由于字符集的转换产生乱码。 所有的表和字段都需要添加注释。使用comment从句添加表和列的备注,从设计初期维护好数据字典。 表单行长度不得超过1024字节。 谨慎使用RDS for MySQL分区表,避免跨分区查询,否则查询效率会降低。分区表在逻辑上表现为一个表,但是在物理层面上将数据存储在多个文件。 表中的列不要太多,尽量做到冷热数据分离,减小表的宽度,以便在一页内存中容纳更多的行,进而减少磁盘IO,更有效的利用缓存。 经常一起使用的列尽量放到一个表中,避免过多的关联操作。 禁止在表中建立预留字段,否则修改列的类型会导致锁表,修改一个字段类型的成本要高于增加一个字段。 禁止在数据库中存储图片、文件等大的二进制数据。 不建议使用全文索引,社区MySQL全文索引局限性较多。
  • 数据库字段设计规范 控制单表字段数量,字段上限50左右。 优先为表中的每一列选择符合存储需要的最小的数据类型。优先考虑数字类型,其次为日期或二进制类型,最后是字符类型。列的字段类型越大,建立索引占据的空间就越大,导致一个页中的索引越少,造成IO次数增加,从而影响性能。 整数型选择能符合需求的最短列类型,如果为非负数,声明需是无符号(UNSIGNED)类型。 每个字段尽可能具有NOT NULL属性,int等数字类型默认值推荐给0,varchar等字符类型默认值给空字符串。 避免使用ENUM类型,可以用TINYINT类型替换。 修改ENUM值需要使用ALTER语句,ENUM类型的ORDER BY操作效率低,需要额外操作。 如果定义了禁止ENUM的枚举值是数值,可使用其他数据类型(如char类型)。 实数类型使用DECIMAL,禁止使用FLOAT和DOUBLE类型。 FLOAT和DOUBLE在存储的时候,存在精度损失的问题,很可能在值的比较时,得到错误的结果。 使用datetime、timestamp类型来存储时间,禁止使用字符串替代。 使用数字类型INT UNSIGNED存储IP地址,用INET_ATON、INET_NTOA可以在IP地址和数字类型之间转换。 VARCHAR类型的长度应该尽可能短。VARCHAR类型虽然在硬盘上是动态长度的,但是在内存中占用的空间是固定的最大长度。 使用VARBINARY存储大小写敏感的变长字符串,VARBINARY默认区分⼤小写,没有字符集概念,速度快。
  • 数据库索引设计规范 每个InnoDB表强烈建议有一个主键,且不使用更新频繁的列作为主键,不使用多列主键。不使用UUID、MD5、字符串列作为主键。最好选择值的顺序是连续增长的列作为主键,所以建议选择使用自增ID列作为主键。 限制每张表上的索引数量,建议单张表索引不超过5个。索引并不是越多越好,索引可以提高查询的效率,但会降低写数据的效率。有时不恰当的索引还会降低查询的效率。 禁止给表中的每一列都建立单独的索引。设计良好的联合索引比每一列上的单独索引效率要高出很多。 建议在下面的列上建立索引: 在SELECT,UPDATE,DELETE语句的WHERE从句上的列。 在ORDER BY,GROUP BY,DISTINCT上的列。 多表JOIN的关联列。 索引列顺序: 区分度最高的放在联合索引的最左侧。区分度=列中不同值的数量/列的总行数。 尽量把字段长度小的列放在联合索引的最左侧。因为字段长度越小,一页能存储的数据量越大,IO性能也就越好。 使用最频繁的列放到联合索引的左侧。这样可以比较少的建立一些索引。 避免冗余的索引,如:primary key(id),index(id),unique index(id) 避免重复的索引,如:index(a,b,c),index(a,b),index(a),重复的和冗余的索引会降低查询效率,因为RDS for MySQL查询优化器会不知道该使用哪个索引。 在VARCHAR字段上建立索引时,需指定索引长度,没必要对全字段建立索引,根据实际文本区分度决定索引长度即可。 一般对字符串类型数据,长度为20的索引,区分度会高达90%以上,可以使用 count(distinct left(列名, 索引长度))/count(*) 的区分度来确定。 对于频繁查询优先考虑使用覆盖索引。 覆盖索引指包含了所有查询字段的索引,不仅仅是WHERE从句GROUP BY从句中的列,也包含SELECT查询的列组合,避免InnoDB表进行索引的二次查询。 外键约束: 建立外键关系的对应列的字符集必须保持一致或者存在外键关系的子表父表的字符集保持一致。
  • 数据库命名规范 所有的数据库对象名称(包括库名、表名、列名等)建议以小写字母命名,每个单词之间用下划线分割。 所有的数据库对象名称禁止使用RDS for MySQL保留关键字。 MySQL官方保留字与关键字(MySQL 8.0):https://dev.mysql.com/doc/refman/8.0/en/keywords.html MySQL官方保留字与关键字(MySQL 5.7):https://dev.mysql.com/doc/refman/5.7/en/keywords.html MySQL官方保留字与关键字(MySQL 5.6):https://dev.mysql.com/doc/refman/5.6/en/keywords.html 数据库对象的命名要能做到见名知意,并且不超过32个字符。 数据库中用到的临时表以“tmp”为前缀并以日期为后缀。 数据库中用到的备份表以“bak”为前缀并以日期为后缀。 在不同的库或表中,要保证所有存储相同数据的列名和列类型必须一致。
  • 定时任务 原因及现象 如果实例负载随时间有规律性变化,可能是存在定时任务。 您可以在控制台查看实例的Delete语句执行频率、Insert语句执行频率、Insert_Select语句执行频率、Replace语句执行频率、Replace_Selection语句执行频率、Select语句执行频率、Update语句执行频率等指标,判断是否有规律性变化。具体操作,请参见查看监控指标。 解决方案 调整定时任务的执行时间,建议在业务低峰期执行,并修改可维护时间段为业务低峰期。具体操作,请参见设置可维护时间段。
  • 查看内存使用情况 通过智能DBA助手查看实例的内存使用情况,具体请参见查看性能指标。 您还可以使用performance_schema,设置相关的内存仪表(instrumentation),通过内存占用统计表查看内存占用。更多信息,请参见MySQL官方文档。 MySQL5.6不支持performance_schema内存检测,不能使用performance_schema进行内存检测。 参考修改当前实例参数,设置performance_schema = ON。 重启实例使参数修改生效。 查询sys.memory_global_total视图,查看当前总的内存使用。 select * from sys.memory_global_total; 查询sys.session视图。 查询sys.session视图,current_memory字段记录每个会话的内存使用。 select thd_id,conn_id, current_memory from sys.session; 查看performance_schema下占用内存较高的会话线程内存明细。 select * from memory_summary_by_thread_by_event_name where thread_id= "占用内存较高的线程id" order by CURRENT_NUMBER_OF_BYTES_USED; 查询sys.memory_by_thread_by_current_bytes。 查询sys.memory_by_thread_by_current_bytes,current_allocated字段记录每个后台线程的内存使用。 select thread_id, user, current_allocated from memory_by_thread_by_current_bytes; 查看performance_schema下占用内存较高的后台线程内存明细。 select * from memory_summary_by_thread_by_event_name where thread_id= "占用内存较高的线程id" order by CURRENT_NUMBER_OF_BYTES_USED; 查询memory_global_by_current_bytes视图,按照内存分配类型获取内存使用统计信息。 select event_name,current_alloc from sys.memory_global_by_current_bytes where event_name not like 'memory/performance_schema%' ; 根据查询结果分析内存使用过高原因。
  • 内存高常见原因 通常InnoDB Buffer Pool的内存占用是最大的,Buffer Pool的内存占用上限受到Buffer Pool配置参数的限制,但是还有很多内存是在请求执行中动态分配和调整的,例如内存临时表消耗的内存、prefetch cache、table cache、哈希索引、行锁对象等,详细的内存占用和相关参数限制,请参见MySQL官方文档。 多语句(multiple statements) MySQL支持将多个SQL语句用英文分号(;)分隔,然后一起发给MySQL,MySQL会逐条处理SQL,但是某些内存需要等到所有的SQL执行结束才释放。 这种multiple statements的发送方式,如果一次性发送的SQL非常多,例如达到数百兆,SQL实际执行过程中各种对象分配累积消耗的内存非常大,很有可能导致MySQL进程内存耗尽。 使用multiple statements发送方式,网络流量会有突增,可以从网络流量监控和SQL洞察,判断是否有这种现象。建议业务实现中尽量避免multiple statements的SQL发送方式。 缓冲池(Buffer Pool)问题 所有表的数据页都存放在缓冲池中,查询执行的时候如果需要的数据页直接命中缓冲池,就不会发生物理I/O,SQL执行的效率较高,缓冲池采用LRU算法管理数据页,所有的脏页放到Flush List链表中。 InnoDB Buffer Pool的内存通常是实例内存中占比最大的。 Buffer Pool相关的常见问题: 数据页预热不足导致查询的延迟较高。通常发生在实例重启、冷数据读取或缓冲池命中率较低的场景,建议升级实例规格或大促前预热数据。 脏页累积太多。当未刷新脏页的最旧LSN和当前LSN的距离超过76%时,会触发用户线程同步刷新脏页,导致实例性能严重下降。优化方式是均衡写入负载、避免写入吞吐过高、调整刷新脏页参数或升级实例规格等。 临时表 内存临时表大小受到参数tmp_table_size和max_heap_table_size限制,超过限制后将转化为磁盘临时表,如果瞬间有大量的连接创建大量的临时表,可能会造成内存突增。MySQL 8.0实现了新的temptable engine,所有线程分配的内存临时表大小之和必须小于参数temptable_max_ram,temptable_max_ram默认为1GB,超出后转换为磁盘临时表。 其他原因 如果实例内表特别多或QPS很高,Table Cache可能也会消耗内存,建议实例避免创建太多表或设置参数table_open_cache过大。 自适应哈希索引占用的内存默认是Bufffer Pool的1/64。如果查询或写入长度非常大的Blob大字段,会对大字段动态分配内存,也会造成内存增加。 如果出现内存使用率异常增加或实例内存耗尽,您可以参考官方文档排查上涨原因。
  • 创建RDS实例 本章节介绍创建RDS实例,该实例选择和自建MySQL服务器相同的VPC和安全组。 进入购买云数据库RDS页面。 配置实例名称和实例基本信息。选择区域“华东-上海一”。 选择实例规格。 选择实例所属的VPC和安全组、配置数据库端口。 配置实例密码。 单击“立即购买”。 进行规格确认。 如果需要重新选择实例规格,单击“上一步”,回到上个页面修改实例信息。 如果规格确认无误,单击“提交”,完成购买实例的申请。 返回云数据库实例列表。 当RDS实例运行状态为“正常”时,表示实例创建完成。 父主题: 上云操作
  • 解决方法一 分析慢SQL日志以及CPU使用率指标来定位效率低的查询,再优化查询效率低的语句。 查看慢SQL日志来确定是否存在运行缓慢的SQL查询以及各个查询的性能特征(如果有),从而定位查询运行缓慢的原因。 查询RDS for MySQL日志,请参见查询慢SQL。 查看实例的CPU使用率指标,协助定位问题。 请参见查看性能指标。 创建只读实例专门负责查询。减轻主实例负载,分担数据库压力。 多表关联查询时,关联字段要加上索引。 尽量避免用select*语句进行全表扫描,可以指定字段或者添加where条件。
  • 数据库索引设计规范 每个InnoDB表强烈建议有一个主键,且不使用更新频繁的列作为主键,不使用多列主键。不使用UUID、MD5、字符串列作为主键。最好选择值的顺序是连续增长的列作为主键,所以建议选择使用自增ID列作为主键。 限制每张表上的索引数量,建议单张表索引不超过5个。索引并不是越多越好,索引可以提高查询的效率,但会降低写数据的效率。有时不恰当的索引还会降低查询的效率。 禁止给表中的每一列都建立单独的索引。设计良好的联合索引比每一列上的单独索引效率要高出很多。 建议在下面的列上建立索引: 在SELECT,UPDATE,DELETE语句的WHERE从句上的列。 在ORDER BY,GROUP BY,DISTINCT上的列。 多表JOIN的关联列。 索引列顺序: 区分度最高的放在联合索引的最左侧。区分度=列中不同值的数量/列的总行数。 尽量把字段长度小的列放在联合索引的最左侧。因为字段长度越小,一页能存储的数据量越大,IO性能也就越好。 使用最频繁的列放到联合索引的左侧。这样可以比较少的建立一些索引。 避免冗余的索引,如:primary key(id),index(id),unique index(id) 避免重复的索引,如:index(a,b,c),index(a,b),index(a),重复的和冗余的索引会降低查询效率,因为RDS for MySQL查询优化器会不知道该使用哪个索引。 在VARCHAR字段上建立索引时,需指定索引长度,没必要对全字段建立索引,根据实际文本区分度决定索引长度即可。 一般对字符串类型数据,长度为20的索引,区分度会高达90%以上,可以使用 count(distinct left(列名, 索引长度))/count(*) 的区分度来确定。 对于频繁查询优先考虑使用覆盖索引。 覆盖索引指包含了所有查询字段的索引,不仅仅是WHERE从句GROUP BY从句中的列,也包含SELECT查询的列组合,避免InnoDB表进行索引的二次查询。 外键约束: 建立外键关系的对应列的字符集必须保持一致或者存在外键关系的子表父表的字符集保持一致。
  • 数据库命名规范 所有的数据库对象名称(包括库名、表名、列名等)建议以小写字母命名,每个单词之间用下划线分割。 所有的数据库对象名称禁止使用RDS for MySQL保留关键字。 MySQL官方保留字与关键字(MySQL 8.0):https://dev.mysql.com/doc/refman/8.0/en/keywords.html MySQL官方保留字与关键字(MySQL 5.7):https://dev.mysql.com/doc/refman/5.7/en/keywords.html MySQL官方保留字与关键字(MySQL 5.6):https://dev.mysql.com/doc/refman/5.6/en/keywords.html 数据库对象的命名要能做到见名知意,并且不超过32个字符。 数据库中用到的临时表以“tmp”为前缀并以日期为后缀。 数据库中用到的备份表以“bak”为前缀并以日期为后缀。 在不同的库或表中,要保证所有存储相同数据的列名和列类型必须一致。
  • 数据库字段设计规范 控制单表字段数量,字段上限50左右。 优先为表中的每一列选择符合存储需要的最小的数据类型。优先考虑数字类型,其次为日期或二进制类型,最后是字符类型。列的字段类型越大,建立索引占据的空间就越大,导致一个页中的索引越少,造成IO次数增加,从而影响性能。 整数型选择能符合需求的最短列类型,如果为非负数,声明需是无符号(UNSIGNED)类型。 每个字段尽可能具有NOT NULL属性,int等数字类型默认值推荐给0,varchar等字符类型默认值给空字符串。 避免使用ENUM类型,可以用TINYINT类型替换。 修改ENUM值需要使用ALTER语句,ENUM类型的ORDER BY操作效率低,需要额外操作。 如果定义了禁止ENUM的枚举值是数值,可使用其他数据类型(如char类型)。 实数类型使用DECIMAL,禁止使用FLOAT和DOUBLE类型。 FLOAT和DOUBLE在存储的时候,存在精度损失的问题,很可能在值的比较时,得到错误的结果。 使用datetime、timestamp类型来存储时间,禁止使用字符串替代。 使用数字类型INT UNSIGNED存储IP地址,用INET_ATON、INET_NTOA可以在IP地址和数字类型之间转换。 VARCHAR类型的长度应该尽可能短。VARCHAR类型虽然在硬盘上是动态长度的,但是在内存中占用的空间是固定的最大长度。 使用VARBINARY存储大小写敏感的变长字符串,VARBINARY默认区分⼤小写,没有字符集概念,速度快。
  • 数据库基本设计规范 所有表如果没有特殊需求,都要使用InnoDB存储引擎。InnoDB存储引擎支持事务、行级锁、具有更好的恢复性、高并发下性能更强。 数据库和表的字符集统一使用UTF8字符集,避免由于字符集的转换产生乱码。 所有的表和字段都需要添加注释。使用comment从句添加表和列的备注,从设计初期维护好数据字典。 表单行长度不得超过1024字节。 谨慎使用RDS for MySQL分区表,避免跨分区查询,否则查询效率会降低。分区表在逻辑上表现为一个表,但是在物理层面上将数据存储在多个文件。 表中的列不要太多,尽量做到冷热数据分离,减小表的宽度,以便在一页内存中容纳更多的行,进而减少磁盘IO,更有效的利用缓存。 经常一起使用的列尽量放到一个表中,避免过多的关联操作。 禁止在表中建立预留字段,否则修改列的类型会导致锁表,修改一个字段类型的成本要高于增加一个字段。 禁止在数据库中存储图片、文件等大的二进制数据。 不建议使用全文索引,社区MySQL全文索引局限性较多。
  • 线程池相关操作 查询线程池参数 通过show variables命令查询线程池参数。 show variables like 'threadpool%'; +-----------------------------------+------------+ | Variable_name | Value | +-----------------------------------+------------+ | threadpool_enabled | ON | | threadpool_high_prio_tickets | 4294967295 | | threadpool_idle_timeout | 60 | | threadpool_long_conn_time | 2 | | threadpool_max_threads | 100000 | | threadpool_oversubscribe | 3 | | threadpool_prio_kickup_timer | 1000 | | threadpool_rec_launch_time | ON | | threadpool_size | 128 | | threadpool_slow_conn_log | ON | | threadpool_slow_conn_log_interval | 30 | | threadpool_slow_launch_time | 2 | | threadpool_stall_limit | 500 | +-----------------------------------+------------+ 表1 线程池参数说明 参数名 参数描述 threadpool_enabled 开启或关闭线程池。 threadpool_high_prio_tickets 高优先级线程持有的tickets数量。 threadpool_idle_timeout 线程销毁前等待的空闲时间(单位:秒)。 threadpool_long_conn_time 登录时间超过此值,则在日志中打印此次登录信息。 threadpool_max_threads 线程池共能创建的线程上限。 threadpool_oversubscribe 每个线程组中最多能创建的额外线程数。 threadpool_prio_kickup_timer 低优先级队列中最长停留时间(单位:毫秒)。 threadpool_rec_launch_time 记录线程启动时间。 threadpool_size 线程组数量。 threadpool_slow_conn_log 是否记录慢登录到error log。 threadpool_slow_conn_log_interval 记录频率,记录一次慢登录后,在此时间间隔内不再记录。 threadpool_slow_launch_time 如登录、查询时间大于此值,则status中 threadpool_slow_launch_request增加1。 threadpool_stall_limit 线程组忙碌检查间隔。 表2 线程池可修改参数说明 参数名 是否是动态参数 数据类型 取值范围 说明 threadpool_enabled 是 boolean [ON,OFF] ON:开启线程池。 OFF:关闭线程池。 threadpool_oversubscribe 是 integer [1,50] 每个线程组中最多能创建的额外线程数。 threadpool_size 是 integer [1,512] 线程组数量。 查询线程池状态 通过show status命令查询线程池状态。 show status like 'threadpool%'; +------------------------------------+------------+ | Variable_name | Value | +------------------------------------+------------+ | Threadpool_active_connections | 65 | | Threadpool_active_threads | 5 | | Threadpool_avg_launch_time | 0 | | Threadpool_dump_threads | 0 | | Threadpool_idle_threads | 63 | | Threadpool_running | ON | | Threadpool_slow_launch_request | 0 | | Threadpool_threads | 237 | | Threadpool_threads_high_water_mark | 1075 | | Threadpool_waiting_threads | 57 | | Threadpool_worst_launch_time | 692548 | +------------------------------------+------------+ 表3 线程池状态说明 状态 说明 Threadpool_active_connections 线程池中活跃连接数。 Threadpool_active_threads 线程池中活跃线程数。 Threadpool_avg_launch_time 平均等待时间(单位:毫秒)。 Threadpool_dump_threads dump线程数量。 Threadpool_idle_thread 线程池空闲线程数。 Threadpool_running 线程池是否在运行。 Threadpool_slow_launch_request 超过slow_launch_request的次数。 Threadpool_threads 线程池总连接数。 Threadpool_threads_high_water_mark 历史高位线程数量。 Threadpool_waiting_threads waiting线程池状态。 Threadpool_worst_launch_time 最坏启动时间(单位:毫秒)。
  • 性能测试 表4 不同线程的性能测试 模型 线程 线程池开关是否开启 QPS(每秒查询数) latency(时延,单位是毫秒) oltp_update_non_index 32 是 5932.47 7.84 oltp_update_non_index 64 是 10074.11 9.39 oltp_update_non_index 128 是 18079.61 10.65 oltp_update_non_index 256 是 27439.38 14.46 oltp_update_non_index 512 是 33007.96 28.16 oltp_update_non_index 1024 是 30282.13 51.94 oltp_update_non_index 2048 是 29836.86 95.81
  • 逻辑订阅完整使用示例 发布端创建表 create table tableName(id int primary key, num int); 发布端创建发布 create publication pubName for table tableName; 发布端创建一个Failover Slot(若为PostgreSQL12或13版本,则去掉第四个参数false) SELECT * FROM pg_create_logical_replication_slot('slotname', 'pgoutput', false, false, true); 发布端插入数据 insert into tableName values(1,1); insert into tableName values(2,2); 订阅端创建表 create table tableName (id int primary key, num int); 订阅端创建订阅,指定创建的Failover Slot名称 create subscription subName connection 'host=192.168.0.10 dbname=postgres user=root port=5432 password=xxxxxxx' publication pubName with(copy_data=true,create_slot=false,slot_name= slotname); 订阅端查询数据,验证数据是否订阅到 select * from tableName; 主备切换 发布端继续插入数据,在订阅端查看,逻辑订阅不会断开。
  • 使用方法 在发布端执行如下SQL,创建一个Failover Slot: 表1 命令及参数说明 版本 命令 参数说明 PostgreSQL12和PostgreSQL13版本 SELECT * FROM pg_create_logical_replication_slot('slotname', 'pgoutput', false, true); slotname表示logical slot的名称。 pgoutput表示plugin的名称,也可以更改为其他支持的插件。 第三个参数(例如false),表示该slot是否为临时slot。 第四个参数(例如true),表示该slot是否为Failover Slot。 PostgreSQL14及以上版本 SELECT * FROM pg_create_logical_replication_slot('slotname', 'pgoutput', false, false, true); slotname表示logical slot的名称。 pgoutput表示plugin的名称,也可以更改为其他支持的插件。 第三个参数(例如false),表示该slot是否为临时slot。 第四个参数(例如true),表示是否启动两阶段提交选项。 第五个参数(例如true),表示该复制槽是否为Failover slot。 因此,如果要创建一个Failover Slot,在第三个参数临时slot字段必须为false,最后一个参数必须为true。如果最后一个参数省略不写,则创建的是非Failover Slot。
  • 使用场景 RDS for PostgreSQL的逻辑订阅故障转移(Failover Slot)功能用来将那些指定为failover slot的逻辑复制槽信息从主实例同步到备实例,当主备切换之后逻辑订阅能够继续进行,实现逻辑复制槽的故障转移。 当用户使用逻辑复制时,由于slot信息不会随着主备切换转移到新的主实例上,一旦实例发生主备切换,逻辑订阅就会断开,此时除非手动重新创建slot,否则逻辑订阅无法重新连接。RDS for PostgreSQL的Failover Slot功能可以将所有的logical slot从主实例同步到备实例,避免主备切换后逻辑订阅断开。
  • 应用场景 设置相关参数值如下,分别对下列参数适用的场景进行分析。 show variables like '%idle%'; +-----------------------------------+-------+ | Variable_name | Value | +-----------------------------------+-------+ | idle_readonly_transaction_timeout | 5 | | idle_transaction_timeout | 10 | | idle_write_transaction_timeout | 15 | +-----------------------------------+-------+ 设置idle_readonly_transaction_timeout 设置参数idle_readonly_transaction_timeout=5。 使用begin开启事务,执行查询语句,查询结果如下: begin; Query OK, 0 rows affected (0.00 sec) select * from t1; +-----+-----+-----+-----+ | a | b | c | d | +-----+-----+-----+-----+ | 1 | b | 303 | d | +-----+-----+-----+-----+ 1 row in set (0.00 sec) 在idle_readonly_transaction_timeout设置的5s范围以外执行一次查询操作,结果如下: select * from t1; +-----+-----+-----+-----+ | a | b | c | d | +-----+-----+-----+-----+ | 1 | b | 303 | d | +-----+-----+-----+-----+ 1 row in set (0.00 sec) select * from t1; ERROR 2006(HY000): MySQL server has gone away 设置idle_transaction_timeout,idle_readonly_transaction_timeout,idle_write_transaction_timeout 设置参数idle_transaction_timeout=10,idle_readonly_transaction_timeout=0,idle_write_transaction_timeout=0。 show variables like '%idle%'; +-----------------------------------+-------+ | Variable_name | Value | +-----------------------------------+-------+ | idle_readonly_transaction_timeout | 0 | | idle_transaction_timeout | 10 | | idle_write_transaction_timeout | 0 | +-----------------------------------+-------+ 3 rows in set (0.01 sec) 只读事务 当idle_readonly_transaction_timeout=0时,idle_transaction_timeout参数生效。 使用begin开启事务,查询表数据,结果如下: begin; Query OK, 0 rows affected (0.00 sec) select * from t1; +-----+-----+-----+-----+ | a | b | c | d | +-----+-----+-----+-----+ | 1 | b | 43 | d | +-----+-----+-----+-----+ 1 row in set (0.00 sec) 10s之后重复执行上述查询操作,结果如下: select * from t1; ERROR 2006(HY000): MySQL server has gone away 读写事务 当idle_write_transaction_timeout=0时,,idle_transaction_timeout参数生效。 使用begin开启事务后,插入数据,在10s以内执行一条查询语句,结果如下: begin; Query OK, 0 rows affected (0.00 sec) INSERT INTO t1(a,b,c,d) VALUES (1,'b',FLOOR( 1 + (RAND()*1000)) ,'d'); Query OK, 1 rows affected (0.00 sec) select * from t1; +-----+-----+-----+-----+ | a | b | c | d | +-----+-----+-----+-----+ | 1 | b | 425 | d | +-----+-----+-----+-----+ 1 row in set (0.00 sec) 10s后执行一条查询语句,结果如下: select * from t1; ERROR 2006(HY000): MySQL server has gone away 之后单独执行一条查表语句,结果如下,表示此时事务已经回滚。 select * from t1; Empty set (0.00 sec) 设置idle_write_transaction_timeout 设置参数idle_write_transaction_timeout=15。 首先由begin开启事务并插入一条数据,结果如下: begin; Query OK, 0 rows affected (0.00 sec) INSERT INTO t1(a,b,c,d) VALUES (1,'b',FLOOR( 1 + (RAND()*1000)) ,'d'); Query OK, 1 rows affected (0.00 sec) 在idle_write_transaction_timeout设置的15s范围以内执行一次查询操作,结果如下: select * from t1; +-----+-----+-----+-----+ | a | b | c | d | +-----+-----+-----+-----+ | 1 | b | 987 | d | +-----+-----+-----+-----+ 1 row in set (0.00 sec) 15s后再次执行查询,结果如下: select * from t1; ERROR 2006(HY000): MySQL server has gone away 重新连接数据库,执行一条查询语句,结果如下,表示此时事务已经回滚。 select * from t1; Empty set (0.00 sec)
  • 参数介绍 通过show variables查询相关参数。 show variables like '%idle%'; +------------------------------------+------+ | Variable_name | Value | +-----------------------------------+-------+ | idle_readonly_transaction_timeout | 0 | | idle_transaction_timeout | 0 | | idle_write_transaction_timeout | 0 | +-----------------------------------+-------+ 表1 参数总览 参数名 参数类型 取值范围 是否动态生效 参数描述 idle_readonly_transaction_timeout integer 正整数 是 控制只读事务连接的超时时间(单位:秒),参数设置为0时不生效,即表示只读事务连接的超时时间没有限制。 idle_transaction_timeout integer 正整数 是 控制一般空闲事务连接的超时时间(单位:秒),参数设置为0时不生效,即表示一般空闲事务的连接超时时间没有限制。 说明: “idle_readonly_transaction_timeout”和“idle_write_transaction_timeout”参数的优先级高于“idle_transaction_timeout”参数。 如果设置“idle_readonly_transaction_timeout”或“idle_write_transaction_timeout”参数值并使之生效,那么“idle_transaction_timeout”参数将会失效。 如果仅设置“idle_transaction_timeout”参数值并使之生效,那么在进行事务的读写操作时,超时时间均以“idle_transaction_timeout”设置的参数值为准。 idle_write_transaction_timeout integer 正整数 是 控制读写事务连接的超时时间(单位:秒),参数设置为0时不生效,即表示读写事务连接的超时时间没有限制。
  • MDL锁视图使用示例 使用场景:长时间未提交事务,阻塞DDL,继而阻塞所有同表的操作。 表2 MDL锁视图示例 表名 会话 session2 session3 session4 session5 t1 begin; select * from t1; - - - t2 - begin; select * from t2; - - t3 - - truncate table t2; (blocked) - t4 - - - begin; select * from t2; (blocked)
  • MDL锁视图详解 MDL锁视图以系统表的形式呈现,该表位于“information_schema”下,表名称是“metadata_lock_info”。表结构如下所示。 desc information_schema.metadata_lock_info; +---------------+-----------------------+---------+------+----------+--------| | Field | Type | Null |Key |Default | Extra | +---------------+-----------------------+---------+------+----------+--------| | THREAD_ID | bigint(20) unsigned | NO | | 0 | | | LOCK_STATUS | varchar(24) | NO | | | | | LOCK_MODE | varchar(24) | YES | | NULL | | | LOCK_TYPE | varchar(30) | YES | | NULL | | | LOCK_DURATION | varchar(30) | YES | | NULL | | | TABLE_SCHEMA | varchar(64) | YES | | NULL | | | TABLE_NAME | varchar(64) | YES | | NULL | | +---------------+-----------------------+---------+------+----------+--------| 表1 metadata_lock_info字段 序号 字段名 字段定义 字段说明 0 THREAD_ID bigint(20) unsigned 会话ID。 1 LOCK_STATUS varchar(24) MDL锁的两种状态。 PENDING:表示会话正在等待该MDL锁。 GRANTED:表示会话已获得该MDL锁。 2 LOCK_MODE varchar(24) 加锁的模式,如MDL_SHARED 、MDL_EXCLUSIVE 、MDL_SHARED_READ、MDL_SHARED_WRITE等。 3 LOCK_TYPE varchar(30) MDL锁的类型,如Table metadata lock、Schema metadata lock、Global read lock、Tablespace lock等。 4 LOCK_DURATION varchar(30) MDL锁范围,取值如下: MDL_STATEMENT:表示语句级别。 MDL_TRANSACTION:表示事务级别。 MDL_EXPLICIT:表示GLOBAL级别。 5 TABLE_SCHEMA varchar(64) 数据库名,对于部分GLOBAL级别的MDL锁,该值为空。 6 TABLE_NAME varchar(64) 表名,对于部分GLOBAL级别的MDL锁,该值为空。
共100000条