标签: tidb cloud

  • 从40秒到11毫秒:TiDB Cloud一次SQL深潜优化实战

    从40秒到11毫秒:TiDB Cloud一次SQL深潜优化实战

    此文参加了 TiDB 社区第四届专栏征文大赛,获得二等奖。感谢 TiDB,感谢 Gemini,感谢跟我一起做 Awesome Comment 的某同学。

    有空的同学麻烦帮我投个票:https://asktug.com/t/topic/1046966 搜索“深潜”就能找到给我投票的地方

    在数据库应用中,慢SQL是常见的性能瓶颈。本文将详细记录一次针对TiDB Cloud v7.5.2环境中复杂评论查询的SQL优化过程,如何通过分析执行计划、添加索引、改写SQL(使用EXISTSUNION)等手段,将一个40多秒的查询逐步优化到11毫秒,希望能为读者提供有价值的实战参考。

    不知道什么时候,TiDB Cloud 升级到 v7.5.2,于是我们的评论应用 RU 消耗开始起飞,达到以往月份的 3 倍左右。没办法,只好拖着病榻之躯来 Debug。还好 Gemini 2.5 Pro 给力,很快我就完成了这次优化,记录在这篇博客里。另外,这篇博客也是 Gemini 2.5 Pro 帮我写的,AI 之力,恐怖如斯 😱😱。

    (更多…)
  • 系列视频制作中:Next.js 全栈开发每日一签网站

    系列视频制作中:Next.js 全栈开发每日一签网站

    之前的几个系列基本都连载完毕。前些天开了一个新坑:全栈架构师系列。但是这个系列比较难录:讲得深,应用场景就窄,观众就少,像我这种发免费课的方式就比较亏;讲得浅,我觉得也没啥意思,担心将来大家一看,“就这?”。还有一点,技术难题不是每天都能遇到,要积累适合的话题,还要不担心泄漏技术秘密,其实比较难搞。所以只录了一期,因为第二期迟迟没想好怎么录,一直也没发。

    前阵子跟 201 老同事吃饭,了解到她也“毕业”了,正在小红书上创作“每日一签”,突然我觉得可以跟以前做的 拜拜 应用结合起来,搞个每日一签网站:

    1. 固定更新能提供 SEO 价值
    2. 拜佛和求签本身就是强关联的功能,可以把用户留住

    于是我们一拍即合,就有了 每日一签 这个网站。这个网站用到以下技术栈:

    1. 后台使用 Strapi 快速搭建,部署在 Zeabur 的 Docker 服务上
    2. 全栈开发使用 Next.js,App Router,全部 SSR,数据使用 Strapi RESTful API
    3. CSS 使用 TailwindCSS,并且基于 TailwindCSS 家的 Commit 模版二次开发
    4. 网站部署在 Vercel 上
    5. 数据库使用 TiDB Cloud Serverless (这次 TiDB Cloud 在我学习 Strapi 的过程中立了大功)
    6. 存储和 CDN 使用 Cloudflare R2
    7. 统计服务也用 Vercel

    这套技术栈完全免费,各种云服务的免费额度可以跑到一个很高的量。是我非常推荐的全栈技术栈,也是各种独立开发、出海创业的首选。所以从开始我就想把它做成一套全栈开发课程。正好可以作为之前几套全栈开发系列教程的补充:

    1. 基于 Next.js + React,便于大家学习和扩展技术栈
    2. 更复杂的网站,更丰富的功能
    3. 更复杂的数据结构和 API,但是更简单的后端处理
    4. 更全面的基础设施服务

    那么说干就干,我就一边开发一边制作视频。新的课程可能包含以下章节:

    1. 项目介绍,技术栈简介(视频制作中)
    2. 使用 Monorepo 管理复杂项目
    3. 使用 Docker 部署 Strapi 应用到 Zeabur (下周一 10-14 录制,正好填上欠 Zeabur 的推广)
    4. 使用 TiDB Cloud 提供数据库服务
    5. 使用 Cloudflare R2 提供存储和 CDN
    6. 使用 Next.js + Commit 模版开发网站
    7. 使用 Shadcn 组件库
    8. 添加每日页,制作 sitemap,提升 SEO
    9. 使用前端推送功能
    10. 使用 og 开发分享功能

    希望新的系列课程能帮助大家学会现代化的全栈开发,学会使用各种现代化的研发平台,给大家开展独立开发和技术出海带来新的机会。如果你对全栈开发、独立开发、技术出海感兴趣,请关注我的系列视频和博客更新。如果你对相关技术有疑问想寻求解答,欢迎留言。

  • 记一次不成功的数据库搬家

    记一次不成功的数据库搬家

    我这个博客始建于 2011 年,当时我从 ZOL 离职,表达欲旺盛的我,希望找个地方继续写博客,于是就买了台集成 WordPress 的虚拟主机,开始写技术博客。除博客之外,我也利用这个平台学习 Linux、nginx、PHP、MySQL 等服务器端软件的使用,从中受益良多。

    后来忘记哪年,从虚拟主机换到 VPS,终于可以拥有自己全权控制的服务器了。当时很兴奋,觉得自己应该在后端取得更深入的发展,于是开始研究 PHP + WordPress 开发,准备基于 WordPress 做一些尝试,模版、插件、WooCommerce,感觉能做的东西很多。

    不过坦率地说,WordPress 历史太久,历史包袱太多,代码架构不好,二次开发很难受,属于能用但是不好用的状态,所以我的学习进度一直不快。另一方面,我的职业机会也不少,所以真正投入在这个方向的时间并不多,拖着拖着就黄了。

    不过我一直在坚持更新博客,后来我发现数据库也不小,每次导出 + 备份都需要不少时间。正巧当时我了解到 TiDB,还恰逢 TiDB Cloud 初次发布,感觉似乎可以把数据库放在云端,这样就一下解决了自动扩容和自动备份这两个需求(其实都没需要)。于是就把数据库迁移过去。

    最初使用 TiDB Cloud 时,因为刚刚参加完 TiDB Hackathon,账户上有免费赠送的额度,所以没感觉有什么问题。但是从去年开始,我发现每个月都要付给 TiDB 几美金费用。我研究了一下 TiDB Cloud 的计费体系,主要分成两大项:

    1. 容量。免费 5G,我的用量远远不够。
    2. 计算用量,RU。不太确定怎么计算,但是我用超甚多,所以费用都产生自这里。

    我首先排除 Slow SQL 的问题。WordPress 这点做得挺好,毕竟是世界上用户数量最大的软件,我也没有用很多来源不明的插件,所以并没有 Slow SQL。

    接下来通过 Metrics 面板查阅,看不太出来用量和时间有什么关系,偶尔我的博客访问量大的时候,RU 会涨一点点,但其实影响不大。

    然后我试图增加 CDN,给页面增加缓存时间等,意图通过降低访问压力降低数据库消耗,但也都没什么实际作用。

    然后我尝试移除一些插件,比如 Jetpack 系列,避免来自 WordPress 官方的请求消耗掉我的额度,也失败了。

    最后我只好诉诸代码,研究来研究去,得到结论:

    1. 我这个博客时间很久,收录也不错,多半已经处于各种垃圾营销的大列表里面
    2. 于是会有很多自动化的机器人来抓取、注册、灌垃圾评论等等
    3. 这些机器人大部分不走网页,没法被缓存,而且每次都会大量读写 wp_options
    4. 不知道具体原因,不过对于 TiDB Cloud 来说,这种对小表的高频请求会消耗大量的 RU
    5. 在不改变架构的前提下,这个问题无法解决

    于是,在九月份被刷掉 $15 之后,我终于忍不了了,利用国庆,把数据库迁回本地。随便刷吧,nnd。下一步,肯定是要重新建站了,把 WordPress 当成纯 C MS,前端用现代化架构重新构建。正好也督促自己把 Awesome Comment 的 SaaS 做起来。

    最后强调一下,这个跟 TiDB Cloud 没关系,纯粹因为项目不合适。我现在有好几个服务都在蹭他们家的免费流量,一点问题没有,非常顺利。

  • 记一次 TiDB Cloud Serverless 超额导致的博客超时故障

    记一次 TiDB Cloud Serverless 超额导致的博客超时故障

    今天早上起来,习惯性地刷新博客统计页面,发现 502。这可不妙,好不容易我坚持到现在终于有点流量,于是赶紧想办法修复。

    博客基础架构

    我的博客架构大体是:

    • 小主机,本地跑 php8.2-fpm 和 nginx
    • 使用 nginx 代理提供服务
    • 数据库使用 TiDB Cloud Serverless
    • 外面套上腾讯云 CDN

    仔细一看,错误页面的 Nginx 是 1.18.0,正是我服务器上的版本,貌似是本地上游 php-fpm 的问题,不是腾讯云 CDN 的问题。ssh 登录服务器,基本正常,验证了判断。查看错误日志,tail /var/log/nginx/blog-error.log,大量的 upstream 错误。top 查看进程负载,服务器本身很闲,但是 5 个 php-fpm 进程虽然不忙,但是都跑了很久。

    重启治百病,先升级系统

    以前没见过这种情况,本着遇事不决先重启的思路,我想先升级重启试试。于是 apt update & apt upgrade 升级系统,接着 reboot。停了一会儿打开网页还是不行。

    登录服务器 service --status-all 查看服务,貌似都正常。查看博客服务器的配置文件,也没写错。输入 service nginx restartservice php8.2-fpm restart 重启服务,还是不行。

    然后 Google PHP8.2、php-fpm、502 等关键词,限制最近一周,看看会不会是新版本引入了新 bug。没有找到结果,应该也不是。

    会不会是 TiDB Cloud 没钱了?

    看过前面博客:💪 WordPress 使用 TiDB Cloud 替换 MySQL 💪 的同学可能知道,我不久前把博客从本地 MySQL 迁移到了 TiDB Cloud Serverless。会不会是欠费停机了?我觉得不应该呀,免费额度 5GB,我的博客数据库前几天看过才区区 300+MB,不会突然就突破限制。

    不过我还是打开了 TiDB Cloud,姑且看一眼吧。没成想,果然是 TiDB Cloud 额度用完了,不过不是容量问题,而是 RU(Request Units,请求单位,TiDB 的某个计费单位)超限。TiDB Serverless Tier 每个月有 50M 的免费 RU 额度,我当时已经用掉 59.6M,所以被停止服务了。

    不过这里有个问题:我的 php-fpm 整个被卡死了,因为 TiDB Cloud 没有及时断开连接,导致我的 PHP 一直在等待数据库响应,但实际上数据库是不会响应的,相当于我的 PHP 线程都在守活寡。直到超时,才得到解脱。这样一来,我的服务器也无法响应任何其它 PHP 请求,包括 phpinfo() 等未使用 TiDB Cloud,甚至那些只有简单 PHP 功能的请求。

    按理说我超额,他们应该拒绝连接,让我看到数据库连接失败的错误,而不是卡住我直到超时。这样一来,

    1. 至少我的服务器还可以响应别的 PHP 请求
    2. 有了数据库连接失败的错误提示,我也能尽快找到问题根源。

    上调预算

    TiDB 还是蛮大方的,不需要预付款、预充值,只要将来按用量结账即可。于是我把预算上调到 $5/月,然后,重启了服务器。结果,又发生了第二个意外。

    我以为上调预算之后,连接、请求就能恢复,结果没有,还是白屏。而且,从 TiDB Cloud 的统计页面来看,在上调预算之后,数据库经历了一大波密集的请求:

    直到约 30 分钟之后,我的博客才渐渐缓过来,我也才有机会写这篇复盘文章。

    我不太确定这波请求是哪里产生的,是我的博客还是 TiDB 的网关,这个有待进一步研究学习。

    请教 TiDB 大佬

    我带着问题在推上请教了大 V 能哥。他告诉我,按照目前的产品策略,超额用户也可以以很低的频率访问数据库,因为有人是占了太多空间,需要有机会让他删数据。

    那这就是彼之蜜糖,吾之砒霜了。我的情况是存储量少(不到400mB),但是请求数高(我看的时候为 60M),所以除非调高预算,我不存在继续使用的可能性。但是他们又不会拒绝我的服务,于是就变成了我服务器上的 php-fpm 被卡死在连接数据库阶段,无法响应任何 PHP 请求。

    我把这个问题反馈给了 TiDB 的社区工作人员,期待他们能解决这个问题。

    下一步:解决方案

    这次故障的修复过程对我来说还是挺新鲜的,也还算顺利。给我在系统设计领域提供了新的经验,所以我拿出来分享给大家。

    不过话说回来,从白嫖的角度来看,迁移到 TiDB Cloud Serverless Tier 上似乎不是很明智。按照 WordPress Jetpack 的统计,我现在每月访问量大约是 7k UV,10k PV,如果这个级别就要花钱,而且动辄几刀的话,那我可能还是迁回去用本地数据库好一些。

    当然,解决方案还是有的。

    首先,Serverless Tier 可以创建多个节点,每个账号的最初的 5 个节点都有 5GB 空间+50M RUs/M 的免费用量。所以我可以写一个自动化脚本,每周将数据同步到下一个节点,然后自动修改链接类到新的节点。嘿嘿嘿,不知道这样的作品能不能参加 Hackathon 😂😂

    其次,好好查查 slow sql,找找优化点。减少每次请求产生的 RU 消耗。

    再次,升级 CDN 配置。我目前的网页都是 30d 缓存,按理说不应该有很多消耗才对。后台只有我一个人在用,不应该有很多数据库访问。我怀疑还是这块儿没搞好。应该有不小的优化空间。

    总结

    TiDB 正在举办新一年的 Hackathon,我也厚着脸皮去要了 $100 的赞助,白嫖大业应该还能再坚持一段时间。如果你也对数据库应用开发感兴趣,我强烈推荐你也来参加这次 Hackathon:【TiDB Future App Hackathon 2023 】TiDB 首届全球黑客马拉松,开发者的狂欢夏日盛会!快来一起 Coding 吧!这次 Hackathon 针对应用层,基本上使用 TiDB Cloud Serverless 就可以,适合各个领域的开发者参加。

    无论如何,感谢 TiDB 提供给我们免费、好用的数据库产品,也推荐大家使用 TiDB Cloud Serverless。如果大家有什么意见建议,欢迎留言讨论。

  • 💪 WordPress 使用 TiDB Cloud 替换 MySQL 💪

    💪 WordPress 使用 TiDB Cloud 替换 MySQL 💪

    白嫖使我快乐。一直白嫖,一直快乐,😊。感谢 TiDB,感谢 TiDB Cloud,你们让我的博客内容更丰富多彩。

    前言

    我这个博客从 2011 年开始写,如今已经 12 年。最早,从 ZOL 离职后,我需要换个新平台写博客;另一方面,我也想学习 Linux、PHP、MySQL,这些原本不熟悉的技术,于是选择了 WordPress。这么多年来,服务器从共享主机搬迁到 VPS,又升级到云主机;PHP 从 5.5、5.6 升级到 7,又升级到 8;Apache 被换成 Nginx;唯独数据库没有变化,基本一套老框架沿用至今。

    我前阵子发现:因为编码问题,无法在标题里或正文里插入表情符号。于是升级数据库也被提上日程。刚好我一直关注的 TiDB 开始提供云数据库服务,采用 Serverless 模式,Free Tier 有 5GB 可以用,足够我写博客。于是我就准备趁此机会切换到 TiDB Cloud 上。

    注册 TiDB Cloud

    打开 tidbcloud.com 注册即可。

    TiDB Cloud 很大方,不需要绑卡,不需要繁琐的操作,直接第三方登录就可以使用。Serverless(Free Tier)只支持 AWS 机房,可选的位置也不多,因为我 ECS 买在美西,所以就把数据库也买在美西,这样速度应该会快一些。

    每个账号可以创建若五个 Serverless 节点,有免费额度,超过限制用量则开始收费。只要稍稍点两下,就建好了,体验很流畅,这里不再赘述。接下来开始使用。

    连接 TiDB Cloud

    数据库准备就绪之后,我们可以进入数据库详情页。点击右上角的“Connect”按钮,即可打开连接信息窗口。

    TiDB 贴心的准备了各种客户端、各种平台的连接方式,对于我这种数据库准小白来说非常有帮助。第一次使用,需要点击右下角的“Reset password”按钮生成数据库密码,生成后,这个按钮会变成复制密码。记得要妥善保存密码,因为我们不能再次查看或者获取。

    这里发现一个设计缺陷:点击 Reset password 按钮没有确认过程,所以我的数据库密码直接就被改掉了……这种破坏性操作还是应该多一个确认比较好,回头反馈给 TiDB 的工作人员。

    导入数据

    TiDB Cloud 只支持导入 CSV 文件,比较难用。可能因为我数据库知识储备不足,我甚至想象不出 CSV 该怎么支持表结构😅。不过没关系,我有 JetBrains 全家桶,命令行操作也还凑合,所以直接从本地跑就行。

    首先在服务器上执行 mysqldump -u USER -p --database BLOG_DB > backup.sql 把整个博客数据库包含数据结构都导出到 backup.sql 文件里。

    打开 sql 文件,把数据库的编码全部改成 utf8mb4 或者 utf8mb4_unicode_ci,这样就可以支持表情符号咯 🎉🎉。

    打开 DataGrip,按照上一节介绍建立数据库连接,右键,选择“SQL Scripts”,然后执行刚才的 SQL,等一会儿,数据即可完成导入。当然,实际过程肯定没有这么顺利,不过云数据库嘛,有问题就直接整个节点干掉再重建就好。不留脏数据。

    WordPress 连接

    TiDB Cloud 要求必须使用 TLS 安全连接,所以我们需要修改 wp-config.php

    /** WordPress数据库的名称 */
    define('DB_NAME', 'blog');
    
    /** MySQL数据库用户名 */
    define('DB_USER', '用户名');
    
    /** MySQL数据库密码 */
    define('DB_PASSWORD', '密码');
    
    /** MySQL主机 */
    define('DB_HOST', '主机域名:端口');
    
    /** 使用 SSL 连接 */
    define('MYSQL_CLIENT_FLAGS', MYSQLI_CLIENT_SSL);
    

    因为我的服务器是 Ubuntu 22.04 系统,证书放在默认位置,可以自动加载。如果你使用其它系统,可能需要修改一下证书路径。

    解决 SQL_CALC_FOUND_ROWS 导致翻页丢失的问题

    完成上述操作,打开博客,500 😱。查看 error.log,原来是不支持 SQL_CALC_FOUND_ROWS 导致报错。我并不知道这个东西是干嘛用的,丢到 Google 里搜索,找到这个 issue,原来 WordPress 的 SQL 要使用这个函数,但是 TiDB 并不支持。

    解决方案是在数据库里执行 SET GLOBAL tidb_enable_noop_functions=1。之后 WordPress 不会再报错,但是相应的,翻页功能也没有了,因为 WordPress 无法统计博文数量。

    继续搜索。看起来 SQL_CALC_FOUND_ROWS 并不是什么好东西,不知道为什么 WordPress 至今都不愿意把它移除。还好,WP 留有开关,我们可以关闭这个函数的使用。我自己创建了一个 WP 小插件,用来给博客加广告、调整页面,所以我就在里面添加函数,关掉 SQL_CALC_FOUND_ROWS

    if ( ! function_exists( 'wpartisan_set_no_found_rows' ) ) :
      function wpartisan_set_no_found_rows( \WP_Query $wp_query ) {
        $wp_query->set( 'no_found_rows', true );
      }
    endif;
    add_filter( 'pre_get_posts', 'wpartisan_set_no_found_rows', 10, 1 );
    
    if ( ! function_exists( 'wpartisan_set_found_posts' ) ) :
      function wpartisan_set_found_posts( $clauses, \WP_Query $wp_query ) {
        // Don't proceed if it's a singular page.
        if ( $wp_query->is_singular()  ) {
          return $clauses;
        }
    
        global $wpdb;
    
        // Check if they're set.
        $where = isset( $clauses[ 'where' ] ) ? $clauses[ 'where' ] : '';
        $join = isset( $clauses[ 'join' ] ) ? $clauses[ 'join' ] : '';
        $distinct = isset( $clauses[ 'distinct' ] ) ? $clauses[ 'distinct' ] : '';
    
        // Construct and run the query. Set the result as the 'found_posts'
        // param on the main query we want to run.
        $wp_query->found_posts = $wpdb->get_var( "SELECT $distinct COUNT(*) FROM {$wpdb->posts} $join WHERE 1=1 $where" );
    
        // Work out how many posts per page there should be.
        $posts_per_page = ( ! empty( $wp_query->query_vars['posts_per_page'] ) ? absint( $wp_query->query_vars['posts_per_page'] ) : absint( get_option( 'posts_per_page' ) ) );
    
        // Set the max_num_pages.
        $wp_query->max_num_pages = ceil( $wp_query->found_posts / $posts_per_page );
    
        // Return the $clauses so the main query can run.
        return $clauses;
      }
    endif;
    add_filter( 'posts_clauses', 'wpartisan_set_found_posts', 10, 2 );

    调整完毕,翻页功能恢复正常。

    使用体验小结

    Serverless 讲究一个即用即起,平时是休眠状态,有人用才会启动。所以最好避免冷启动,否则第一个用的人要等很长时间,体验很差。作为博客,更简单的方式是配置 CDN,给首页外的页面添加长期缓存,就能改善大部分用户的体验。

    实际上我目前使用了一个多月,并没有冷启动的感觉。我的博客日均几百的访问量,感觉相当可以。另外一个实例,作为 NoCoDB 的数据库,因为访问量很小,所以冷启动感觉很明显。

    其它方面,速度和效率都不比本地数据库差,以后我再搞产品,应该都不会自己数据库了,嘿嘿嘿。


    后记

    我已经两次报名参加 TiDB Hackathon,第一次止步外围筛选第二次虽然进入决赛圈,但是因为对云数据库不了解,挑战开发 NoCoDB+TiDB Cloud 失败,铩羽而归。

    但是借用群里同学的说法:人生没有白走的路,每一步,都算数。虽然 Hackathon 没能获得好成绩,但是我获得了更多关于 TiDB Cloud 的知识,于是这次可以顺利完成迁移。

    目前博客网站使用体验良好,速度不比之前使用本地数据库慢。前两天,我发现 TiDB Cloud 开放了 Data Service,提供 HTTP Endpoint,这表示着他们向 DaaS(Database as a Service)更近一步,也意味着我们在 Edge Function 里使用 TiDB Cloud 成为可能。那么接下来,我打算好好利用一下这个薅羊毛机会,把几个 Side Project 的数据端放在 TiDB Cloud 上,跟 Supabase 比一比,看哪个更好用,更适合新项目、小公司从零到一。

    到时候也会写成更多文章,或许做成视频,跟大家分享。

    如果各位读者老爷对数据库、云开发、薅羊毛感兴趣,欢迎留言讨论,可提出问题,亦可指点一二,均非常欢迎。