<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:webfeeds="http://webfeeds.org/rss/1.0" xmlns:media="http://search.yahoo.com/mrss/">
  <channel>
    <title>王强博客|程序员王强博客|JAVA王强博客|石家庄王强|软件开发|石家庄APP开发|石家庄小程序开发</title>
    <link>https://aweb.wang/feed</link>
    <description><![CDATA[王强博客，让一切变得简单！我是一个来自河北石家庄的程序员，用心写代码让世界变得更美好，欢迎洽谈合作。]]></description>
    <atom:link href="https://aweb.wang/feed" rel="self" type="application/rss+xml" />
                        <webfeeds:logo>https://aweb.wang/favicon.ico</webfeeds:logo>
    <image>
      <url>https://aweb.wang/favicon.ico</url>
      <title>王强博客|程序员王强博客|JAVA王强博客|石家庄王强|软件开发|石家庄APP开发|石家庄小程序开发</title>
      <link>https://aweb.wang/feed</link>
    </image>
                <language>zh-cn</language>
    <lastBuildDate>Sat, 14 Sep 2024 15:49:28 +0800</lastBuildDate>
        <item>
      <title><![CDATA[PHP 语言没落原因及未来走向分析报告]]></title>
            <link>https://aweb.wang/post/php-yu-yan-mo-luo-yuan-yin-ji-wei-lai-zou-xiang-fen-xi-bao-gao</link>
      <guid isPermaLink="true">https://aweb.wang/post/php-yu-yan-mo-luo-yuan-yin-ji-wei-lai-zou-xiang-fen-xi-bao-gao</guid>
      <description><![CDATA[一、核心观点 1.1 PHP 辉煌不再，下坡路明显 PHP 曾在 Web 开发领域辉煌一时，自 1995 年诞生以来，迅速发展成为一个庞大的生态系统，在电子商务、社交网络、内容管理等领域广泛应用。然而...]]></description>
            <content:encoded><![CDATA[<h1>一、核心观点</h1>
<h2>1.1 PHP 辉煌不再，下坡路明显</h2>
<p>PHP 曾在 Web 开发领域辉煌一时，自 1995 年诞生以来，迅速发展成为一个庞大的生态系统，在电子商务、社交网络、内容管理等领域广泛应用。然而，如今 PHP 的地位逐渐下滑。从各种编程语言排行榜可以看出，PHP 的排名不断下降，如在 TIOBE 编程榜指数中，PHP 仅排在第 17 位，达到历史最低排名。Stack Overflow 开发者调查和 JetBrains 调查也显示 PHP 使用占比大幅下滑。这一现状对 Web 开发行业格局产生了重大影响，新兴语言如 JavaScript、Python 等逐渐崛起，吸引了更多开发者的关注和使用。</p>
<h2>1.2 多因素致衰，转型挑战大</h2>
<p>多种因素共同导致了 PHP 的没落。一方面，WordPress 正逐步转向 JavaScript，作为 PHP 最著名的应用案例之一，其减少对 PHP 的依赖大大降低了 PHP 的重要性。另一方面，随着 Web 开发需求的复杂化，新兴语言提供了更为现代化的功能、更好的异步处理能力和更为强大的生态系统。PHP 自身存在一些局限性，如较弱的类型检查、设计缺陷和性能瓶颈，在竞争中显得愈发明显。此外，PHP 程序员转型面临诸多困难，如语法差异、并发编程挑战、包管理和依赖管理的转变等。对于习惯了 PHP 动态特性和单线程编程的程序员来说，Golang 等语言的强类型定义和并发编程模式较为陌生。</p>
<h2>1.3 未来发展待观察，机遇挑战并存</h2>
<p>PHP 的未来发展充满不确定性，机遇与挑战并存。虽然 PHP 目前面临诸多困境，但仍有一部分开发者群体在继续使用 PHP。PHP 基金会也在为其提供支持，努力扭转 PHP 在公众心目中的形象。然而，要想在竞争激烈的编程语言市场中重新崛起，PHP 需要不断改进和优化。一方面，要解决自身的局限性，提升性能和类型检查能力；另一方面，要积极适应新的技术趋势，如移动互联网、AI 大模型等，拓展应用场景。同时，PHP 程序员也需要不断学习和转型，以适应行业的变化。</p>
<h1>二、市场态势剖析</h1>
<h2>2.1 宏观环境对 PHP 的影响</h2>
<h3>2.1.1 编程语言市场动态影响</h3>
<p>编程语言市场不断发展，新的语言层出不穷。近年来，Python、JavaScript 等语言的崛起，对 PHP 造成了巨大的冲击。以 Python 为例，其在数据科学、人工智能等领域的广泛应用，吸引了大量开发者。根据相关数据，Python 语言的采用在今年取得了约 37% 的迅猛增长，岗位需求从去年的 45,000 个增长到今年的约 62,000 个。而 PHP 的岗位需求相对较为稳定，甚至有下降的趋势。这种市场动态的变化，使得开发者更倾向于选择具有更广阔应用前景和更多就业机会的语言，从而减少了对 PHP 的关注和使用。</p>
<h3>2.1.2 技术变革带来的挑战</h3>
<p>移动互联网和 AI 大模型等技术变革给 PHP 带来了严峻的挑战。在移动互联网时代，应用程序的开发更加注重跨平台性和性能优化。PHP 在移动应用开发方面的支持相对较弱，难以满足开发者的需求。而新兴的语言如 Flutter、React Native 等，在移动应用开发方面具有明显的优势。在 AI 大模型领域，Python 凭借其丰富的机器学习和深度学习库，成为了首选语言。PHP 在这方面的生态系统相对薄弱，难以与 Python 等语言竞争。</p>
<h2>2.2 行业全景解码</h2>
<h3>2.2.1 行业现状全维度扫描</h3>
<p>目前，PHP 在编程领域的地位逐渐下降。从市场份额来看，虽然有数据显示 PHP 在某些领域仍占有一定的份额，如在电子商务领域，根据 BuiltWith 2023 年 8 月对在线商店的报告，PHP 在电商领域仍然占 “统治地位”。但从整体趋势来看，PHP 的市场份额在逐渐减少。在 TIOBE 编程榜指数中，PHP 仅排在第 17 位，低于汇编语言、Ruby、Swift、Scratch 和 MATLAB 等语言。这表明 PHP 在当前编程领域的地位已经大不如前。</p>
<h3>2.2.2 竞争格局多维透视</h3>
<p>与新兴编程语言如 Python、JavaScript 等相比，PHP 在竞争中处于劣势。Python 以其简洁的语法、丰富的库和广泛的应用领域，吸引了大量开发者。JavaScript 则在网页开发中占据着主导地位，其强大的前端开发能力和众多的框架使得开发者能够快速构建交互性强的网页应用。而 PHP 在语法上相对较为复杂，生态系统也不如 Python 和 JavaScript 强大。此外，PHP 在性能和安全性方面也存在一些问题，这使得开发者在选择编程语言时更倾向于其他语言。</p>
<h2>2.3 市场趋向精准把脉</h2>
<h3>2.3.1 技术前沿动态追踪</h3>
<p>编程语言技术前沿不断发展，函数式编程、元编程、云计算和分布式系统等技术逐渐成为热门。然而，PHP 在这些技术方面的发展相对滞后。例如，函数式编程风格在 Python 等语言中变得越来越流行，它强调使用纯函数来构建程序，提高了代码的可推理性和可测试性。而 PHP 在函数式编程方面的支持相对较弱。在云计算和分布式系统方面，Python 和 Java 等语言具有更好的支持和集成度，能够更好地满足企业级应用的需求。</p>
<h3>2.3.2 开发者需求演变洞察</h3>
<p>随着技术的不断发展，开发者对编程语言的需求也在不断变化。开发者更加注重语言的性能、安全性、可扩展性和生态系统。Python 和 JavaScript 等语言在这些方面表现出色，满足了开发者的需求。而 PHP 在性能和可扩展性方面存在一定的局限性，难以满足开发者的高要求。此外，开发者也更加注重语言的学习成本和开发效率。Python 和 JavaScript 等语言的语法相对较为简洁，学习成本较低，开发效率较高。而 PHP 的语法相对较为复杂，学习成本较高，开发效率相对较低。这使得开发者在选择编程语言时更倾向于其他语言。</p>
<h1>三、产品研究</h1>
<h2>3.1 PHP 语言特性分析</h2>
<p>PHP 以其易学易用的特性在早期吸引了大量开发者。一方面，它的流行使得入门相对容易，有其他语言基础的程序员通常在二周左右的时间就可以入门，一个月左右的时间基本上就可以开发简单的项目。这种低门槛的特性为许多初学者打开了编程的大门，推动了 Web 开发领域的快速发展。例如，在一些小型企业或个人项目中，开发者可以快速上手 PHP，迅速搭建起功能基本的网站。
然而，易学易用的特性也带来了一些弊端。首先，PHP 的语法不太严谨，比如变量不需要定义就可以使用，这与 C、Java、C++ 等语言相比，增加了代码出错的风险。在大型项目中，这种不严谨的语法可能会导致难以排查的错误，影响项目的稳定性和可维护性。其次，PHP 对多线程支持不太好，大多数时候只能简单模拟去实现。在当今多任务处理需求日益增长的环境下，这一局限性使得 PHP 在处理高并发场景时显得力不从心。例如，在一些电商平台的促销活动中，高并发的用户请求可能会使 PHP 应用面临性能瓶颈。另外，PHP 对面向对象的支持也不太好，虽然 PHP5 之后有了不少提升，但相对其他语言如 Java 和 C++，仍存在差距。这使得在开发复杂的软件系统时，PHP 的面向对象特性可能无法满足开发者的需求。</p>
<h2>3.2 与其他语言对比</h2>
<p>与 Python 相比，PHP 在多个方面存在差异。在语法风格上，Python 的语法简洁明了，易于学习和理解，而 PHP 的语法风格更接近于 C 语言，相对较为复杂。例如，Python 的代码更具有可读性，其简洁的语法结构使得开发者能够更快速地理解代码的逻辑。在应用场景上，Python 不仅在 Web 开发中有广泛应用，还在数据分析、机器学习、人工智能等领域占据重要地位。相比之下，PHP 主要用于 Web 开发，应用场景相对较窄。根据相关数据，在数据科学领域，约 80% 的项目选择使用 Python 进行数据分析和处理。在性能方面，Python 的运行速度相对较慢，但由于其优异的设计和丰富的库，可以在许多情况下提供相当的性能。而 PHP 在处理 Web 请求方面速度较快，但在处理复杂计算任务时可能不如 Python 高效。
与 JavaScript 相比，PHP 是服务器端语言，而 JavaScript 是前端语言（除 Node.js 之外）。这意味着它们在应用场景上有很大的不同。JavaScript 主要用于使网页具有交互性和动态性，通过在浏览器中运行，为用户提供丰富的用户体验。而 PHP 在服务器端运行，主要负责处理动态网页的生成和数据库交互等任务。在字符串连接符方面，php 的字符串连接符是 “.”，JavaScript 的字符串连接符是 “+”。在数组相加操作上，php 数组相加用 “ + ”，可以直接相加，而 JavaScript 用拼接函数 concat ()。此外，PHP 只有变量名区分大小写，JavaScript 全部区分大小写。</p>
<h1>四、竞争格局</h1>
<h2>4.1 新兴语言崛起冲击</h2>
<p>Python 和 JavaScript 等新兴语言的崛起给 PHP 带来了巨大的冲击。Python 以其强大的数据科学和人工智能领域的应用优势，吸引了众多开发者。据统计，在全球范围内，超过 60% 的数据分析和机器学习项目选择使用 Python。Python 的简洁语法、丰富的库以及强大的生态系统，使其在处理复杂计算任务和大规模数据处理方面表现出色。例如，在人工智能领域，Python 的深度学习框架如 TensorFlow 和 PyTorch 被广泛应用，为开发者提供了高效的开发工具。相比之下，PHP 在这些领域的应用相对较少，其生态系统也不够完善。
JavaScript 在网页开发领域占据主导地位，尤其是随着前端技术的不断发展，JavaScript 的重要性日益凸显。根据相关数据，目前超过 90% 的网站都使用了 JavaScript 进行前端开发。JavaScript 的强大前端开发能力和众多的框架，使得开发者能够快速构建交互性强的网页应用。例如，React、Angular 和 Vue.js 等前端框架的流行，为开发者提供了高效的开发模式和丰富的功能。而 PHP 主要在服务器端运行，虽然也可以与 JavaScript 进行交互，但在前端开发方面的能力相对较弱。
新兴语言的崛起还吸引了大量的开发者社区支持。Python 和 JavaScript 拥有庞大的开发者社区，这意味着开发者可以更容易地找到问题的解决方案、学习资源和工具。相比之下，PHP 的开发者社区虽然也很活跃，但在规模和影响力上逐渐被新兴语言超越。</p>
<h2>4.2 WordPress 转向的影响</h2>
<p>WordPress 从 PHP 转向 JavaScript 对 PHP 产生了重大影响。作为 PHP 最著名的应用案例之一，WordPress 的转向大大降低了 PHP 的重要性。在 2024 年 WordCamp Asia 大会上，WordPress 联合创始人及 Automattic CEO Matt Mullenweg 确认，目前 WordPress 新开发的多数代码采用 JavaScript，Gutenberg 编辑器更是以 JavaScript 为主导，标志着整个项目正朝 “JavaScript 优先” 方向转变。</p>
<p>这一转变使得原本依赖 WordPress 进行开发的开发者不得不考虑学习和使用 JavaScript。对于那些致力于服务 WordPress 客户的开发人员来说，从 PHP 转向 JavaScript 成为必然趋势。这进一步减少了 PHP 的使用场景和市场需求。</p>
<p>同时，WordPress 的转向也影响了年轻开发者的选择。年轻开发者在选择编程语言时，会考虑到未来的发展趋势和就业机会。由于 WordPress 的影响力，他们更倾向于选择 JavaScript 等新兴语言，而不是 PHP。这使得 PHP 在吸引新开发者方面面临更大的困难。</p>
<p>然而，尽管 WordPress 的转向对 PHP 造成了重大影响，但仍有一部分开发者群体在继续使用 PHP。这些开发者可能是因为已经熟悉了 PHP 的开发环境和生态系统，或者是因为他们的项目仍然依赖于 PHP。此外，PHP 基金会也在努力为 PHP 提供支持，试图扭转 PHP 在公众心目中的形象。</p>
<h1>五、监管政策</h1>
<h2>5.1 编程语言监管环境</h2>
<p>编程语言领域通常没有特定的、直接针对某一具体语言的监管政策。PHP 在这样的监管环境下，几乎不受直接影响。与金融等受到严格监管的行业不同，编程语言的发展主要依赖市场需求、技术创新和开发者社区的推动。
在金融领域，监管政策对相关软件和技术的安全性、稳定性和合规性有严格要求。然而，PHP 在金融领域的应用相对较少，不像一些更具安全性和稳定性的语言如 Java 等受到广泛关注。根据相关统计，在金融领域的软件应用中，PHP 的占比不足 10%。这主要是因为 PHP 在处理复杂金融业务逻辑和高安全性要求方面存在一定的局限性。
虽然编程语言监管环境对 PHP 影响甚微，但在更广泛的互联网监管政策下，PHP 应用的网站和系统也需要遵守数据安全、隐私保护等规定。例如，随着全球数据保护法规的不断加强，如欧盟的《通用数据保护条例》（GDPR），网站开发者使用 PHP 构建的系统也需要确保用户数据的安全存储和处理，否则可能面临巨额罚款。
总体而言，编程语言监管环境对 PHP 的直接影响有限，但在更广泛的互联网和行业监管背景下，PHP 应用仍需适应各种合规要求。</p>
<h1>六、其他影响因素</h1>
<h2>6.1 社区与市场选择</h2>
<p>开发者社区兴趣转移对 PHP 产生了重大影响。随着技术的不断发展，开发者们更加倾向于选择那些被认为更具前瞻性和灵活性的编程语言。例如，Python 和 JavaScript 等语言在社区中受到了广泛的关注和喜爱。相比之下，PHP 的社区活跃度逐渐下降。许多开发者开始转向其他语言，导致 PHP 的开发者社区规模不断缩小。
市场选择也对 PHP 产生了重要影响。随着新兴语言的崛起，企业在选择编程语言时更加倾向于那些能够更好地适应新趋势和满足业务需求的语言。例如，在移动互联网、AI 大模型等领域，Python 和 JavaScript 等语言具有明显的优势。而 PHP 在这些领域的表现相对较弱，因此在市场竞争中逐渐失去了优势。
根据相关数据统计，近年来，PHP 在企业项目中的采用率逐渐下降。在一些新兴领域，如人工智能、大数据等，PHP 的应用更是少之又少。这表明市场对 PHP 的需求正在逐渐减少，而对其他语言的需求则在不断增加。
6.2 PHP 基金会的作用
PHP 基金会在 PHP 的发展中起到了重要的作用。首先，它为 PHP 的核心开发人员提供了资金支持，确保了 PHP 语言的持续发展。例如，JetBrains 等公司每年向 PHP 基金会捐款，使得更多的开发人员能够全职从事 PHP 的开发工作。
其次，PHP 基金会促进了 PHP 社区的发展。它为开发者提供了交流和合作的平台，使得开发者们能够更好地分享经验和技术。同时，基金会还组织各种活动和会议，提高了 PHP 的知名度和影响力。
然而，PHP 基金会也面临着一些挑战。一方面，基金会需要不断筹集资金，以确保能够持续为 PHP 的发展提供支持。另一方面，基金会需要吸引更多的开发者参与到 PHP 的开发中来，提高 PHP 的竞争力。
此外，PHP 基金会还需要应对新兴语言的挑战。随着 Python、JavaScript 等语言的不断发展，PHP 基金会需要不断改进和优化 PHP，以满足市场的需求。例如，加强 PHP 在性能、安全性和可扩展性等方面的表现，提高 PHP 在新兴领域的应用能力。</p>
<h1>七、风险分析</h1>
<h2>7.1 持续没落风险</h2>
<p>PHP 面临着持续没落的风险，其市场份额有可能进一步下降。随着新兴编程语言的不断涌现和发展，它们在性能、功能和生态系统方面不断提升，对 PHP 形成了持续的压力。例如，Go 语言以其高效的并发性能和简洁的语法，在云计算和分布式系统领域得到了广泛应用；TypeScript 在前端开发中凭借强大的类型系统和良好的可维护性，逐渐成为主流。相比之下，PHP 在这些方面的竞争力相对较弱。
根据行业趋势分析，未来的技术发展将更加注重高性能、高可扩展性和智能化。如果 PHP 不能及时跟上这些趋势，其市场份额很可能会继续被其他语言蚕食。例如，在人工智能和大数据领域，Python 的优势非常明显，几乎成为了行业标准。而 PHP 在这些领域的应用相对较少，缺乏相关的库和工具支持。
此外，开发者社区的活跃度也是影响语言发展的重要因素。如果 PHP 的开发者社区继续萎缩，那么将导致新的技术和工具的开发速度减慢，进一步降低 PHP 的吸引力。目前，Python 和 JavaScript 等语言的开发者社区非常活跃，不断推出新的库和框架，为开发者提供了更多的选择和便利。而 PHP 的开发者社区相对较小，创新能力不足，这也增加了 PHP 持续没落的风险。</p>
<h2>7.2 转型失败风险</h2>
<p>PHP 在转型过程中也面临着失败的风险。一方面，PHP 自身的历史包袱较重，其语法和设计存在一些问题，这使得转型变得困难。例如，PHP 的弱类型和松散的数据结构在一定程度上影响了代码的可维护性和可扩展性，而要改变这些问题需要对语言进行重大的改进，这可能会导致不兼容性和学习成本的增加。
另一方面，转型需要投入大量的时间和资源。PHP 开发者需要学习新的语法和技术，企业需要对现有项目进行重构和迁移，这都需要付出巨大的成本。如果转型过程中出现问题，可能会导致项目延误、成本超支和质量下降等风险。
此外，市场竞争也给 PHP 的转型带来了压力。在 PHP 转型的同时，其他语言也在不断发展和创新，它们可能会在某些方面超越 PHP，吸引更多的开发者和企业。例如，Go 语言在性能和并发方面的优势非常明显，如果 PHP 在转型过程中不能在这些方面取得突破，那么很可能会被 Go 语言等竞争对手超越。
最后，转型的成功还取决于开发者社区和市场的接受程度。如果开发者对 PHP 的转型不感兴趣或者不支持，那么转型很可能会失败。同样，如果企业对 PHP 的转型持怀疑态度，不愿意采用转型后的 PHP，那么也会影响 PHP 的转型效果。</p>]]></content:encoded>
            <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">wangqiang</dc:creator>
      <pubDate>2024-09-14T15:49:28+08:00</pubDate>
                                                    </item>
        <item>
      <title><![CDATA[数独解题PHP工具类]]></title>
            <link>https://aweb.wang/post/shu-du-jie-ti-php-gong-ju-lei</link>
      <guid isPermaLink="true">https://aweb.wang/post/shu-du-jie-ti-php-gong-ju-lei</guid>
      <description><![CDATA[定义sudoUtils类 &lt;?php namespace app\common\utils; class SudoUtils {     private $sudoArr;     public...]]></description>
            <content:encoded><![CDATA[<p>定义sudoUtils类</p>
<pre><code class="language-php">&lt;?php
namespace app\common\utils;
class SudoUtils
{
    private $sudoArr;
    public function __construct($sudoArr) {
        $this-&gt;sudoArr = $sudoArr;
    }
    public function solveSudoku() {
        $row = 0;
        $col = 0;
        if (!$this-&gt;findEmptyCell($row, $col)) {
            return true; // 数独已经解决
        }
        for ($num = 1; $num &lt;= 9; $num++) {
            if ($this-&gt;isSafe($row, $col, $num)) {
                $this-&gt;sudoArr[$row][$col] = $num;
                if ($this-&gt;solveSudoku()) {

                    echo '行数:'.$row.'列数:'.$col.'值为:'.$num.'&lt;br/&gt;';
                    return true;
                }
                $this-&gt;sudoArr[$row][$col] = 0; // 回溯
            }
        }
        return false; // 无解
    }
    private function findEmptyCell(&amp;$row, &amp;$col) {
        for ($row = 0; $row &lt; 9; $row++) {
            for ($col = 0; $col &lt; 9; $col++) {
                if ($this-&gt;sudoArr[$row][$col] == 0) {
                    return true; // 找到一个空单元格
                }
            }
        }
        return false; // 数独已经填满
    }
    private function isSafe($row, $col, $num) {
        return !$this-&gt;usedInRow($row, $num) &amp;&amp; !$this-&gt;usedInCol($col, $num) &amp;&amp; !$this-&gt;usedInBox($row - $row % 3, $col - $col % 3, $num);
    }
    private function usedInRow($row, $num) {
        for ($col = 0; $col &lt; 9; $col++) {
            if ($this-&gt;sudoArr[$row][$col] == $num) {
                return true; // 数字已经在行中存在
            }
        }
        return false;
    }
    private function usedInCol($col, $num) {
        for ($row = 0; $row &lt; 9; $row++) {
            if ($this-&gt;sudoArr[$row][$col] == $num) {
                return true; // 数字已经在列中存在
            }
        }
        return false;
    }
    private function usedInBox(int $boxStartRow,int $boxStartCol, int $num):bool{
        for ($row = 0; $row &lt; 3; $row++) {
            for ($col = 0; $col &lt; 3; $col++) {
                if ($this-&gt;sudoArr[$row + $boxStartRow][$col + $boxStartCol] == $num) {
                    return true;
                }
            }
        }
        return false;
    }
    public function printsudoArr() {
        return $this-&gt;sudoArr;

    }

}</code></pre>]]></content:encoded>
            <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">wangqiang</dc:creator>
      <pubDate>2024-09-06T10:20:40+08:00</pubDate>
                                                    </item>
        <item>
      <title><![CDATA[一种数独的自动解题思路]]></title>
            <link>https://aweb.wang/post/yi-zhong-shu-du-de-zi-dong-jie-ti-si-lu</link>
      <guid isPermaLink="true">https://aweb.wang/post/yi-zhong-shu-du-de-zi-dong-jie-ti-si-lu</guid>
      <description><![CDATA[&lt;template&gt;     &lt;view class="content"&gt;         &lt;view class="slogen"&gt;史上最牛的数独解体工具&lt;...]]></description>
            <content:encoded><![CDATA[<pre><code class="language-javascript">&lt;template&gt;
    &lt;view class="content"&gt;
        &lt;view class="slogen"&gt;史上最牛的数独解体工具&lt;/view&gt;
        &lt;table&gt;
            &lt;tbody&gt;
                &lt;tr v-for="(item,index) in sudoArr" :class="(index+1)%3==0?'redR':''"&gt;
                    &lt;td v-for="(it,idx) in item"&gt;
                        &lt;input type="text"  v-model="sudoArr[index][idx]" :class="(idx+1)%3==0?'redC':''" placeholder="" :value="sudoArr[index][idx]==0?'':sudoArr[index][idx]"&gt;&lt;/input&gt;
                    &lt;/td&gt;
                &lt;/tr&gt;
            &lt;/tbody&gt;

        &lt;/table&gt;
        &lt;button @tap="solveSudo"&gt;解&lt;/button&gt;
        &lt;button @tap="refresh" class="refresh"&gt;重来&lt;/button&gt;
    &lt;/view&gt;
&lt;/template&gt;

&lt;script&gt;
    export default {
        data() {
            return {
                sudoArr:[
                        [0,0,0,0,0,0,0,0,0],
                        [0,0,0,0,0,0,0,0,0],
                        [0,0,0,0,0,0,0,0,0],
                        [0,0,0,0,0,0,0,0,0],
                        [0,0,0,0,0,0,0,0,0],
                        [0,0,0,0,0,0,0,0,0],
                        [0,0,0,0,0,0,0,0,0],
                        [0,0,0,0,0,0,0,0,0],
                        [0,0,0,0,0,0,0,0,0]
                    ]
            }
        },
        onLoad() {
            this.refresh();
        },
        methods: {
            refresh(){
                this.sudoArr=[
                        [0,0,0,0,0,0,0,0,0],
                        [0,0,0,0,0,0,0,0,0],
                        [0,0,0,0,0,0,0,0,0],
                        [0,0,0,0,0,0,0,0,0],
                        [0,0,0,0,0,0,0,0,0],
                        [0,0,0,0,0,0,0,0,0],
                        [0,0,0,0,0,0,0,0,0],
                        [0,0,0,0,0,0,0,0,0],
                        [0,0,0,0,0,0,0,0,0]
                    ];
            },
            solveSudo(){
                var row=0;
                var col=0;
                var res=this.findEmptyCell();
                if(!res){
                    return true;
                }else{
                    row=res[0];
                    col=res[1];
                }

                for(var num =1;num&lt;=9;num++){
                    if(this.isSafe(row,col,num)){
                        this.sudoArr[row][col]=num;
                        if(this.solveSudo()){
                            //console.log('行数:'+(row+1)+'列数:'+(col+1)+'值为:'+num);
                            return true;//找到一个可行解
                        }
                        this.sudoArr[row][col]=0;//回溯
                    }
                }
                return false;//无解
            },
            findEmptyCell(){
                for(var row=0;row&lt;9;row++){
                    for(var col=0;col&lt;9;col++){
                        if(this.sudoArr[row][col]==0){
                            return [row,col];
                        }
                    }
                }
                return false;
            },
            isSafe(row,col,num){
                return !this.usedInRow(row,num) &amp;&amp; !this.usedInCol(col,num) &amp;&amp; !this.usedInBox(row-row%3,col-col%3,num);
            },
            usedInRow(row,num){
                for(var col=0;col&lt;9;col++){
                    if(this.sudoArr[row][col]==num){
                        return true;
                    }
                }
                return false;
            },
            usedInCol(col,num){
                for(var row=0;row&lt;9;row++){
                    if(this.sudoArr[row][col]==num){
                        return true;
                    }
                }
                return false;
            },
            usedInBox(boxStartRow,boxStartCol,num){
                for(var row=0;row&lt;3;row++){
                    for(var col=0;col&lt;3;col++){
                        if(this.sudoArr[boxStartRow+row][boxStartCol+col]==num){
                            return true;
                        }
                    }
                }
                return false;
            },

        }
    }
&lt;/script&gt;

&lt;style&gt;
    table{width:630upx; height:630upx;border-collapse: collapse;margin:10upx auto;}
    td{width:70upx;height:70upx;border:1px solid #000;}
    td input{width:70upx;height:70upx;text-align: center;}
    .slogen{color:#00adef;text-align: center;width:100%;height:100upx;line-height: 100upx;}
    button{width:700upx;height:80upx;border-radius:20upx;background:#00adef;color:#fff;}
    .redR td{border-bottom:1px solid #f00;}
    .redC{border-right:1px solid #f00;}
    .refresh{background:#eee;margin-top:20px;}
&lt;/style&gt;
</code></pre>]]></content:encoded>
            <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">wangqiang</dc:creator>
      <pubDate>2024-09-06T10:18:49+08:00</pubDate>
                                                    </item>
        <item>
      <title><![CDATA[BASE64编码知识整理]]></title>
            <link>https://aweb.wang/post/base64-bian-ma-zhi-shi-zheng-li</link>
      <guid isPermaLink="true">https://aweb.wang/post/base64-bian-ma-zhi-shi-zheng-li</guid>
      <description><![CDATA[在我们进行互联网应用开发时，针对项目优化，常会提到一条：针对较小图片，合理使用Base64字符串替换内嵌，可以减少页面http请求。 并且还会特别强调下，必须是小图片，大小不要超过多少KB，等等。 那...]]></description>
            <content:encoded><![CDATA[<p>在我们进行互联网应用开发时，针对项目优化，常会提到一条：针对较小图片，合理使用Base64字符串替换内嵌，可以减少页面http请求。
并且还会特别强调下，必须是小图片，大小不要超过多少KB，等等。
那么，Base64又到底是什么呢？</p>
<h1>base64日常中的应用</h1>
<p>下面的这段字符串，应该是大家都很常见的。通过这种固定的格式，来表示一张图片，并被浏览器识别，可以完整的展示出图片：</p>
<p><code>data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0c......</code></p>
<p>这里展示的是一个svg格式的图片，当然我们还可以加载任何浏览器支持的格式的图片。</p>
<p>这段字符串就是基于Base64编码得来的，其中base64,后面那一长串的字符串，就是Base64编码字符串。</p>
<h1>base64的前世今生</h1>
<p>互联网发展早起，电子邮件是最有效的应用。</p>
<p>而电子邮件的SMTP传输协议在早期，只能用于传送7位的ASCII码，而ASCII码就是基于英语设计的，对于非英语国家的文字等资源就无法发送。</p>
<p>为了解决这个问题，后来有了通用互联网邮件扩充MIME，增加了邮件的主体结构，定义了非ASCII码的编码传输规则，这就是Base64。</p>
<p>关于字符编码的知识，请查看前端开发中需要搞懂的字符编码知识</p>
<h1>base64基础定义</h1>
<p>Base64是基于64个可打印字符来表示二进制数据的编解码方式。
正因为可编解码，所以它主要的作用不在于安全性，而在于让内容能在各个网关间无错的传输。</p>
<p>这64个可打印字符包括大写字母A-Z、小写字母a-z、数字0-9共62个字符，再加上另外2个 + 和 /。</p>
<p>Base64是一种索引编码，每个字符都对应一个索引，具体的关系图，如下：</p>
<p><img src="/storage/image/KLGSZ69kDf3VKnnFIPLH6ytOFR0nzIcSihliJclw.png" alt="" /></p>
<p>显而易见基本的64个字符，所以显而易见就base64.</p>
<h1>编码方式</h1>
<p>由于64等于2的6次方，所以一个Base64字符实际上代表着6个二进制位(bit)。
然而，二进制数据1个字节(byte)对应的是8比特(bit)，因此，3字节（3 x 8 = 24比特）的字符串/二进制数据正好可以转换成4个Base64字符(4 x 6 = 24比特)。
为什么是3个字节一组呢？ 因为6和8的最小公倍数是24，24比特正好是3个字节。</p>
<p>具体的编码方式：</p>
<p>将每3个字节作为一组，3个字节一共24个二进制位</p>
<p>将这24个二进制位分为4组，每个组有6个二进制位</p>
<p>在每组的6个二进制位前面补两个00，扩展成32个二进制位，即四个字节</p>
<p>每个字节对应的将是一个小于64的数字，即为字符编号</p>
<p>再根据字符索引关系表，每个字符编号对应一个字符，就得到了Base64编码字符</p>
<p><img src="/storage/image/k9CwV8Ltss5tnlsfYf7jVtooAzQmRcfluim58dhC.png" alt="" /></p>
<p>上图中的字符串 'you'，经过转换后，得到的编码为： 'eW91'。</p>
<h1>= 等号</h1>
<p>3个英文字符，正好能转成4个Base64字符。那如果字符长度不是3的倍数，那应该使用什么样的规则呢？</p>
<p>其实也简单，我们在实际使用Base编码时，常会发现有第65个字符的存在，那就是 '=' 符号，这个等于号就是针对这种特殊情况的一种处理方式。</p>
<p>对于不足3个字节的地方，实际都会在后面补0，直到有24个二进制位为止。</p>
<p>但要注意的是，在计算字节数时，会直接使用总长度除以3，如果余数为1则会直接在最后补一个=，如果余数为2则补两个=。</p>
<p>因此，转码后的字符串需要补的后缀等号，要么是1个，要么是2个。</p>
<h1>非ASCII码字符</h1>
<p>由于 Base64 仅可对 ASCII 字符进行编码，如果是中文字符等非ASCII码，就需要先将中文字符转换为ASCII字符后，再进行编码才行。</p>]]></content:encoded>
            <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">wangqiang</dc:creator>
      <pubDate>2024-01-10T17:29:05+08:00</pubDate>
                                                    </item>
        <item>
      <title><![CDATA[PHP列表转树非递归]]></title>
            <link>https://aweb.wang/post/php-lie-biao-zhuan-shu-fei-di-gui</link>
      <guid isPermaLink="true">https://aweb.wang/post/php-lie-biao-zhuan-shu-fei-di-gui</guid>
      <description><![CDATA[public function listToTree($list)     {         usort($list,             function($a, $b) {         ...]]></description>
            <content:encoded><![CDATA[<pre><code class="language-php">public function listToTree($list)
    {
        usort($list,
            function($a, $b) {
                if ($a['pid'] == $b['pid']) {
                    return 0;
                } elseif ($a['pid'] &lt; $b['pid']) {
                    return 1;
                } else {
                    return 0;
                }
            });
        $tree=[];
        $indexedData=[];
        foreach($list as $item){
            $indexedData[$item['id']]=$item;
        }
        foreach($indexedData as $id=&gt;$item){
            if($item['pid']==0){
                $tree[]=$indexedData[$id];
            }else{
                $indexedData[$item['pid']]['children'][]=$indexedData[$id];
            }
        }

        return $tree;
    }</code></pre>]]></content:encoded>
            <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">wangqiang</dc:creator>
      <pubDate>2024-01-03T11:33:30+08:00</pubDate>
                                                    </item>
        <item>
      <title><![CDATA[JAVA bean生命周期]]></title>
            <link>https://aweb.wang/post/java-bean-sheng-ming-zhou-qi</link>
      <guid isPermaLink="true">https://aweb.wang/post/java-bean-sheng-ming-zhou-qi</guid>
      <description><![CDATA[1、Spring启动，查找并加载需要被Spring管理的bean，进行Bean的实例化、 2、Bean实例化后对将Bean的引入和值注入到Bean的属性中 3、如果Bean实现了BeanNameAwa...]]></description>
            <content:encoded><![CDATA[<p>1、Spring启动，查找并加载需要被Spring管理的bean，进行Bean的实例化、</p>
<p>2、Bean实例化后对将Bean的引入和值注入到Bean的属性中</p>
<p>3、如果Bean实现了BeanNameAware接口的话，Spring将Bean的Id传递给setBeanName()方法</p>
<p>4、如果Bean实现了BeanFactoryAware接口的话，Spring将调用setBeanFactory()方法，将BeanFactory容器实例传入</p>
<p>5、如果Bean实现了ApplicationContextAware接口的话，Spring将调用Bean的setApplicationContext()方法，将bean所在应用上下文引用传入进来</p>
<p>6、如果Bean实现了BeanPostProcessor接口，Spring就将调用他们的postProcessBeforeInitialization()方法。</p>
<p>7、如果Bean 实现了InitializingBean接口，Spring将调用他们的afterPropertiesSet()方法。类似的，如果bean使用init-method声明了初始化方法，该方法也会被调用</p>
<p>8、如果Bean 实现了BeanPostProcessor接口，Spring就将调用他们的postProcessAfterInitialization()方法。</p>
<p>9、此时，Bean已经准备就绪，可以被应用程序使用了。他们将一直驻留在应用上下文中，直到应用上下文被销毁。</p>
<p>10、如果bean实现了DisposableBean接口，Spring将调用它的destory()接口方法，同样，如果bean使用了destory-method 声明销毁方法，该方法也会被调用</p>]]></content:encoded>
            <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">wangqiang</dc:creator>
      <pubDate>2023-12-01T13:46:44+08:00</pubDate>
                                                    </item>
        <item>
      <title><![CDATA[centos系统下RabbitMQ安装教程]]></title>
            <link>https://aweb.wang/post/centos-xi-tong-xia-rabbitmq-an-zhuang-jiao-cheng</link>
      <guid isPermaLink="true">https://aweb.wang/post/centos-xi-tong-xia-rabbitmq-an-zhuang-jiao-cheng</guid>
      <description><![CDATA[centos系统下RabbitMQ安装教程 我这里的环境用的腾讯云的轻应用环境 RabbitMQ是一个开源的遵循AMQP协议实现的基于Erlang语言编写，即需要先安装部署Erlang环境再安装Rab...]]></description>
            <content:encoded><![CDATA[<p>centos系统下RabbitMQ安装教程
我这里的环境用的腾讯云的轻应用环境</p>
<p>RabbitMQ是一个开源的遵循AMQP协议实现的基于Erlang语言编写，即需要先安装部署Erlang环境再安装RabbitMQ环境。需加注意的是，读者若不想跟着我的版本号下载安装，可根据两者版本号的对应表进行下载，安装相应版本的Erlang和RabbitMQ，只需在下文修改命令里面的版本号即可，如下:
一、Erlang下载安装
在确定了RabbitMQ版本号后，先下载安装Erlang环境和rabbitMQ安装包。
下载：</p>
<pre><code class="language-shell">wget --content-disposition https://packagecloud.io/rabbitmq/erlang/packages/el/7/erlang-22.3.4.12-1.el7.x86_64.rpm/download.rpm
wget --content-disposition https://packagecloud.io/rabbitmq/rabbitmq-server/packages/el/7/rabbitmq-server-3.8.13-1.el7.noarch.rpm/download.rpm</code></pre>
<p>安装：</p>
<pre><code class="language-shell">yum localinstall erlang-22.3.4.12-1.el7.x86_64.rpm
yum localinstall rabbitmq-server-3.8.13-1.el7.noarch.rpm</code></pre>
<p>二、跑起来
启动命令：</p>
<pre><code class="language-shell"> systemctl start rabbitmq-server</code></pre>
<p>开机自启：</p>
<pre><code class="language-shell"> systemctl enable rabbitmq-server</code></pre>
<p>三、web管理界面（默认不支持的，需开启支持）</p>
<pre><code class="language-shell">rabbitmq-plugins enable rabbitmq_management</code></pre>
<p>开启后需重启rabbitMQ:</p>
<pre><code class="language-shell">systemctl restart rabbitmq-server</code></pre>
<p>注意，请在服务器上开放 15672 端口</p>
<p>新增用户：</p>
<pre><code class="language-shell">rabbitmqctl add_user admin 123456</code></pre>
<p>设置用户分配权限：</p>
<pre><code class="language-shell">rabbitmqctl set_user_tags admin administrator</code></pre>
<p>设置vhosts的权限：</p>
<pre><code class="language-shell">rabbitmqctl set_permissions -p / admin ".*" ".*" ".*"</code></pre>
<p>最后就可以输入IP:15672登录web界面了</p>]]></content:encoded>
            <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">wangqiang</dc:creator>
      <pubDate>2023-09-13T15:16:15+08:00</pubDate>
                                                    </item>
        <item>
      <title><![CDATA[java基于Spring Cloud的eureka微服务搭建及网关使用]]></title>
            <link>https://aweb.wang/post/java-ji-yu-spring-cloud-de-eureka-wei-fu-wu-da-jian-ji-wang-guan-shi-yong</link>
      <guid isPermaLink="true">https://aweb.wang/post/java-ji-yu-spring-cloud-de-eureka-wei-fu-wu-da-jian-ji-wang-guan-shi-yong</guid>
      <description><![CDATA[java基于Spring Cloud的eureka微服务搭建及网关使用起步 手把手教你撸SpringCloud微服务 传送门...]]></description>
            <content:encoded><![CDATA[<p>java基于Spring Cloud的eureka微服务搭建及网关使用起步
手把手教你撸SpringCloud微服务
<a href="https://blog.csdn.net/u012612234/article/details/132230845" title="传送门">传送门</a></p>]]></content:encoded>
            <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">wangqiang</dc:creator>
      <pubDate>2023-08-11T15:19:16+08:00</pubDate>
                                                    </item>
        <item>
      <title><![CDATA[MYSQL架构简介]]></title>
            <link>https://aweb.wang/post/mysql-jia-gou-jian-jie</link>
      <guid isPermaLink="true">https://aweb.wang/post/mysql-jia-gou-jian-jie</guid>
      <description><![CDATA[MySQL主要分为 Server 层和存储引擎层： Server 层：主要包括连接器、查询缓存、分析器、优化器、执行器等，所有跨存储引擎的功能都在这一层实现，比如存储过程、触发器、视图，函数等，还有一...]]></description>
            <content:encoded><![CDATA[<p>MySQL主要分为 Server 层和存储引擎层：
Server 层：主要包括连接器、查询缓存、分析器、优化器、执行器等，所有跨存储引擎的功能都在这一层实现，比如存储过程、触发器、视图，函数等，还有一个通用的日志模块 binglog 日志模块。
存储引擎： 主要负责数据的存储和读取。server 层通过api与存储引擎进行通信。</p>
<h2>Server 层基本组件：</h2>
<ol>
<li>连接器： 当客户端连接 MySQL 时，server层会对其进行身份认证和权限校验。</li>
<li>查询缓存: 执行查询语句的时候，会先查询缓存，先校验这个 sql 是否执行过，如果有缓存这个 sql，就会直接返回给客户端，如果没有命中，就会执行后续的操作。</li>
<li>分析器: 没有命中缓存的话，SQL 语句就会经过分析器，主要分为两步，词法分析和语法分析，先看 SQL 语句要做什么，再检查 SQL 语句语法是否正确。</li>
<li>优化器： 优化器对查询进行优化，包括重写查询、决定表的读写顺序以及选择合适的索引等，生成执行计划。</li>
<li>执行器： 首先执行前会校验该用户有没有权限，如果没有权限，就会返回错误信息，如果有权限，就会根据执行计划去调用引擎的接口，返回结果。</li>
</ol>]]></content:encoded>
            <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">wangqiang</dc:creator>
      <pubDate>2023-07-05T15:34:16+08:00</pubDate>
                                                    </item>
        <item>
      <title><![CDATA[Mysql日志详解]]></title>
            <link>https://aweb.wang/post/mysql-ri-zhi-xiang-jie</link>
      <guid isPermaLink="true">https://aweb.wang/post/mysql-ri-zhi-xiang-jie</guid>
      <description><![CDATA[MySQL日志主要包括查询日志、慢查询日志、事务日志、错误日志、二进制日志等。其中比较重要的是 bin log（二进制日志）和 redo log（重做日志）和 undo log（回滚日志）。 bin ...]]></description>
            <content:encoded><![CDATA[<p>MySQL日志主要包括查询日志、慢查询日志、事务日志、错误日志、二进制日志等。其中比较重要的是 bin log（二进制日志）和 redo log（重做日志）和 undo log（回滚日志）。</p>
<h2>bin log</h2>
<p>bin log是MySQL数据库级别的文件，记录对MySQL数据库执行修改的所有操作，不会记录select和show语句，主要用于恢复数据库和同步数据库。</p>
<h2>redo log</h2>
<p>redo log是innodb引擎级别，用来记录innodb存储引擎的事务日志，不管事务是否提交都会记录下来，用于数据恢复。当数据库发生故障，innoDB存储引擎会使用redo log恢复到发生故障前的时刻，以此来保证数据的完整性。将参数innodb_flush_log_at_tx_commit设置为1，那么在执行commit时会将redo log同步写到磁盘。</p>
<h2>undo log</h2>
<p>除了记录redo log外，当进行数据修改时还会记录undo log，undo log用于数据的撤回操作，它保留了记录修改前的内容。通过undo log可以实现事务回滚，并且可以根据undo log回溯到某个特定的版本的数据，实现MVCC。</p>
<h2>bin log和redo log有什么区别？</h2>
<ol>
<li>bin log会记录所有日志记录，包括InnoDB、MyISAM等存储引擎的日志；redo log只记录innoDB自身的事务日志。</li>
<li>bin log只在事务提交前写入到磁盘，一个事务只写一次；而在事务进行过程，会有redo log不断写入磁盘。</li>
<li>bin log是逻辑日志，记录的是SQL语句的原始逻辑；redo log是物理日志，记录的是在某个数据页上做了什么修改。</li>
</ol>]]></content:encoded>
            <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">wangqiang</dc:creator>
      <pubDate>2023-07-05T14:58:50+08:00</pubDate>
                                                    </item>
        <item>
      <title><![CDATA[快照读和当前读以及幻读]]></title>
            <link>https://aweb.wang/post/kuai-zhao-du-he-dang-qian-du-yi-ji-huan-du</link>
      <guid isPermaLink="true">https://aweb.wang/post/kuai-zhao-du-he-dang-qian-du-yi-ji-huan-du</guid>
      <description><![CDATA[表记录有两种读取方式。 快照读：读取的是快照版本。普通的SELECT就是快照读。通过mvcc来进行并发控制的，不用加锁。 当前读：读取的是最新版本。UPDATE、DELETE、INSERT、SELEC...]]></description>
            <content:encoded><![CDATA[<p>表记录有两种读取方式。</p>
<p>快照读：读取的是快照版本。普通的SELECT就是快照读。通过mvcc来进行并发控制的，不用加锁。
当前读：读取的是最新版本。UPDATE、DELETE、INSERT、SELECT … LOCK IN SHARE MODE、SELECT … FOR UPDATE是当前读。
快照读情况下，InnoDB通过mvcc机制避免了幻读现象。而mvcc机制无法避免当前读情况下出现的幻读现象。因为当前读每次读取的都是最新数据，这时如果两次查询中间有其它事务插入数据，就会产生幻读。</p>
<p>下面举个例子说明下：</p>
<p>1、首先，user表只有两条记录，
user_id user_name user_pwd
1   张三  123456
2   李四  123456
2、事务a和事务b同时开启事务start transaction；
3、事务a插入数据然后提交；</p>
<p><code>insert into user(user_name, user_password, user_mail, user_state) values('tyson', 'a', 'a', 0);</code></p>
<p>4、事务b执行全表的update；</p>
<p><code>update user set user_name = 'a';</code></p>
<p>5、事务b然后执行查询，查到了事务a中插入的数据。（下图左边是事务b，右边是事务a。事务开始之前只有两条记录，事务a插入一条数据之后，事务b查询出来是三条数据）
以上就是当前读出现的幻读现象。</p>
<h2>MySQL是如何避免幻读？</h2>
<p>在快照读情况下，MySQL通过mvcc来避免幻读。
在当前读情况下，MySQL通过next-key来避免幻读（加行锁和间隙锁来实现的）。
next-key包括两部分：行锁和间隙锁。行锁是加在索引上的锁，间隙锁是加在索引之间的。</p>
<p>Serializable隔离级别也可以避免幻读，会锁住整张表，并发性极低，一般不会使用。</p>
<h2>共享锁和排他锁</h2>
<p>SELECT 的读取锁定主要分为两种方式：共享锁和排他锁。</p>
<pre><code class="language-sql">select * from table where id&lt;6 lock in share mode;--共享锁
select * from table where id&lt;6 for update;--排他锁</code></pre>
<p>这两种方式主要的不同在于LOCK IN SHARE MODE多个事务同时更新同一个表单时很容易造成死锁。</p>
<p>申请排他锁的前提是，没有线程对该结果集的任何行数据使用排它锁或者共享锁，否则申请会受到阻塞。在进行事务操作时，MySQL会对查询结果集的每行数据添加排它锁，其他线程对这些数据的更改或删除操作会被阻塞（只能读操作），直到该语句的事务被commit语句或rollback语句结束为止。</p>
<p>SELECT... FOR UPDATE 使用注意事项：</p>
<ol>
<li>for update 仅适用于innodb，且必须在事务范围内才能生效。</li>
<li>根据主键进行查询，查询条件为like或者不等于，主键字段产生表锁。</li>
<li>根据非索引字段进行查询，会产生表锁。</li>
</ol>]]></content:encoded>
            <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">wangqiang</dc:creator>
      <pubDate>2023-07-05T14:55:52+08:00</pubDate>
                                                    </item>
        <item>
      <title><![CDATA[MVCC的概念作用以及实现原理]]></title>
            <link>https://aweb.wang/post/mvcc-de-gai-nian-zuo-yong-yi-ji-shi-xian-yuan-li</link>
      <guid isPermaLink="true">https://aweb.wang/post/mvcc-de-gai-nian-zuo-yong-yi-ji-shi-xian-yuan-li</guid>
      <description><![CDATA[概念 MVCC(Multiversion concurrency control) 就是同一份数据保留多版本的一种方式，进而实现并发控制。在查询的时候，通过read view和版本链找到对应版本的数据...]]></description>
            <content:encoded><![CDATA[<h2>概念</h2>
<p>MVCC(Multiversion concurrency control) 就是同一份数据保留多版本的一种方式，进而实现并发控制。在查询的时候，通过read view和版本链找到对应版本的数据。</p>
<h2>作用</h2>
<p>提升并发性能。对于高并发场景，MVCC比行级锁开销更小。</p>
<h2>实现原理</h2>
<p>MVCC 的实现依赖于版本链，版本链是通过表的三个隐藏字段实现。
DB_TRX_ID：当前事务id，通过事务id的大小判断事务的时间顺序。
DB_ROLL_PTR：回滚指针，指向当前行记录的上一个版本，通过这个指针将数据的多个版本连接在一起构成undo log版本链。
DB_ROW_ID：主键，如果数据表没有主键，InnoDB会自动生成主键。
使用事务更新行记录的时候，就会生成版本链，执行过程如下：</p>
<ol>
<li>用排他锁锁住该行；</li>
<li>将该行原本的值拷贝到undo log，作为旧版本用于回滚；</li>
<li>修改当前行的值，生成一个新版本，更新事务id，使回滚指针指向旧版本的记录，这样就形成一条版本链。</li>
</ol>
<h2>read view概念</h2>
<p>read view可以理解成将数据在每个时刻的状态拍成“照片”记录下来。在获取某时刻t的数据时，到t时间点拍的“照片”上取数据。</p>
<p>在read view内部维护一个活跃事务链表，表示生成read view的时候还在活跃的事务。这个链表包含在创建read view之前还未提交的事务，不包含创建read view之后提交的事务。</p>
<p>不同隔离级别创建read view的时机不同。</p>
<ol>
<li>read committed：每次执行select都会创建新的read_view，保证能读取到其他事务已经提交的修改。</li>
<li>repeatable read：在一个事务范围内，第一次select时更新这个read_view，以后不会再更新，后续所有的select都是复用之前的read_view。这样可以保证事务范围内每次读取的内容都一样，即可重复读。</li>
</ol>
<h2>read view的记录筛选方式</h2>
<p>前提：DATA_TRX_ID 表示每个数据行的最新的事务ID；up_limit_id表示当前快照中的最先开始的事务；low_limit_id表示当前快照中的最慢开始的事务，即最后一个事务。</p>
<ol>
<li>
<p>如果DATA_TRX_ID &lt; up_limit_id：说明在创建read view时，修改该数据行的事务已提交，该版本的记录可被当前事务读取到。</p>
</li>
<li>
<p>如果DATA_TRX_ID &gt;= low_limit_id：说明当前版本的记录的事务是在创建read view之后生成的，该版本的数据行不可以被当前事务访问。此时需要通过版本链找到上一个版本，然后重新判断该版本的记录对当前事务的可见性。</p>
</li>
<li>
<p>如果up_limit_id &lt;= DATA_TRX_ID &lt; low_limit_i：</p>
</li>
<li>
<p>需要在活跃事务链表中查找是否存在ID为DATA_TRX_ID的值的事务。</p>
</li>
<li>
<p>如果存在，因为在活跃事务链表中的事务是未提交的，所以该记录是不可见的。此时需要通过版本链找到上一个版本，然后重新判断该版本的可见性。</p>
</li>
<li>
<p>如果不存在，说明事务trx_id 已经提交了，这行记录是可见的。</p>
</li>
</ol>
<p>总结：InnoDB 的MVCC是通过 read view 和版本链实现的，版本链保存有历史版本记录，通过read view 判断当前版本的数据是否可见，如果不可见，再从版本链中找到上一个版本，继续进行判断，直到找到一个可见的版本。</p>]]></content:encoded>
            <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">wangqiang</dc:creator>
      <pubDate>2023-07-05T14:46:58+08:00</pubDate>
                                                    </item>
        <item>
      <title><![CDATA[索引的七七八八]]></title>
            <link>https://aweb.wang/post/suo-yin-de-qi-qi-ba-ba</link>
      <guid isPermaLink="true">https://aweb.wang/post/suo-yin-de-qi-qi-ba-ba</guid>
      <description><![CDATA[什么是索引？ 索引是存储引擎用于提高数据库表的访问速度的一种数据结构。 索引的优缺点？ 优点： 加快数据查找的速度 为用来排序或者是分组的字段添加索引，可以加快分组和排序的速度 加快表与表之间的连接 ...]]></description>
            <content:encoded><![CDATA[<h2>什么是索引？</h2>
<p>索引是存储引擎用于提高数据库表的访问速度的一种数据结构。</p>
<h2>索引的优缺点？</h2>
<h3>优点：</h3>
<ol>
<li>加快数据查找的速度</li>
<li>为用来排序或者是分组的字段添加索引，可以加快分组和排序的速度</li>
<li>加快表与表之间的连接</li>
</ol>
<h3>缺点：</h3>
<ol>
<li>建立索引需要占用物理空间</li>
<li>会降低表的增删改的效率，因为每次对表记录进行增删改，需要进行动态维护索引，导致增删改时间变长</li>
</ol>
<h2>索引的作用？</h2>
<p>数据是存储在磁盘上的，查询数据时，如果没有索引，会加载所有的数据到内存，依次进行检索，读取磁盘次数较多。有了索引，就不需要加载所有数据，因为B+树的高度一般在2-4层，最多只需要读取2-4次磁盘，查询速度大大提升。</p>
<h2><a href="https://aweb.wang/post/mysql-zhong-shi-yong-suo-yin-de-chang-jing-he-bu-shi-he-shi-yong-suo-yin-de-chang-jing" title="使用索引的场景和不使用索引的场景">使用索引的场景和不使用索引的场景</a></h2>
<h2>索引的数据结构</h2>
<p>索引的数据结构主要有B+树和哈希表，对应的索引分别为B+树索引和哈希索引。InnoDB引擎的索引类型有B+树索引和哈希索引，默认的索引类型为B+树索引。</p>
<h3>B+树索引</h3>
<p>B+ 树是基于B 树和叶子节点顺序访问指针进行实现，它具有B树的平衡性，并且通过顺序访问指针来提高区间查询的性能。</p>
<p>在 B+ 树中，节点中的 key 从左到右递增排列，如果某个指针的左右相邻 key 分别是 keyi 和 keyi+1，则该指针指向节点的所有 key 大于等于 keyi 且小于等于 keyi+1。
进行查找操作时，首先在根节点进行二分查找，找到key所在的指针，然后递归地在指针所指向的节点进行查找。直到查找到叶子节点，然后在叶子节点上进行二分查找，找出key所对应的数据项。</p>
<p>MySQL 数据库使用最多的索引类型是BTREE索引，底层基于B+树数据结构来实现。</p>
<pre><code class="language-sql">mysql&gt; show index from blog\G;
*************************** 1. row ***************************
        Table: blog
   Non_unique: 0
     Key_name: PRIMARY
 Seq_in_index: 1
  Column_name: blog_id
    Collation: A
  Cardinality: 4
     Sub_part: NULL
       Packed: NULL
         Null:
   Index_type: BTREE
      Comment:
Index_comment:
      Visible: YES
   Expression: NULL</code></pre>
<h3>哈希索引</h3>
<p>哈希索引是基于哈希表实现的，对于每一行数据，存储引擎会对索引列进行哈希计算得到哈希码，并且哈希算法要尽量保证不同的列值计算出的哈希码值是不同的，将哈希码的值作为哈希表的key值，将指向数据行的指针作为哈希表的value值。这样查找一个数据的时间复杂度就是O(1)，一般多用于精确查找。</p>
<h2>Hash索引和B+树索引的区别？</h2>
<ol>
<li>哈希索引不支持排序，因为哈希表是无序的。</li>
<li>哈希索引不支持范围查找。</li>
<li>哈希索引不支持模糊查询及多列索引的最左前缀匹配。</li>
<li>因为哈希表中会存在哈希冲突，所以哈希索引的性能是不稳定的，而B+树索引的性能是相对稳定的，每次查询都是从根节点到叶子节点。</li>
</ol>
<h2>为什么B+树比B树更适合实现数据库索引？</h2>
<ol>
<li>由于B+树的数据都存储在叶子结点中，叶子结点均为索引，方便扫库，只需要扫一遍叶子结点即可，但是B树因为其分支结点同样存储着数据，我们要找到具体的数据，需要进行一次中序遍历按序来扫，所以B+树更加适合在区间查询的情况，而在数据库中基于范围的查询是非常频繁的，所以通常B+树用于数据库索引。</li>
<li>B+树的节点只存储索引key值，具体信息的地址存在于叶子节点的地址中。这就使以页为单位的索引中可以存放更多的节点。减少更多的I/O支出。</li>
<li>B+树的查询效率更加稳定，任何关键字的查找必须走一条从根结点到叶子结点的路。所有关键字查询的路径长度相同，导致每一个数据的查询效率相当。</li>
</ol>
<h2>索引有什么分类？</h2>
<ol>
<li>主键索引：名为primary的唯一非空索引，不允许有空值。</li>
<li>唯一索引：索引列中的值必须是唯一的，但是允许为空值。唯一索引和主键索引的区别是：唯一索引字段可以为null且可以存在多个null值，而主键索引字段不可以为null。唯一索引的用途：唯一标识数据库表中的每条记录，主要是用来防止数据重复插入。创建唯一索引的SQL语句如下：ALTER TABLE table_name ADD CONSTRAINT constraint_name UNIQUE KEY(column_1,column_2,...);</li>
<li>组合索引：在表中的多个字段组合上创建的索引，只有在查询条件中使用了这些字段的左边字段时，索引才会被使用，使用组合索引时需遵循最左前缀原则。</li>
<li>全文索引：只能在CHAR、VARCHAR和TEXT类型字段上使用全文索引。</li>
<li>普通索引：普通索引是最基本的索引，它没有任何限制，值可以为空。</li>
</ol>
<h2>什么是最左匹配原则？</h2>
<p>如果 SQL 语句中用到了组合索引中的最左边的索引，那么这条 SQL 语句就可以利用这个组合索引去进行匹配。当遇到范围查询(&gt;、&lt;、between、like)就会停止匹配，后面的字段不会用到索引。</p>
<p>对(a,b,c)建立索引，查询条件使用 a/ab/abc 会走索引，使用 bc 不会走索引。</p>
<p>对(a,b,c,d)建立索引，查询条件为a = 1 and b = 2 and c &gt; 3 and d = 4，那么a、b和c三个字段能用到索引，而d无法使用索引。因为遇到了范围查询。</p>
<p>对(a, b) 建立索引，a 在索引树中是全局有序的，而 b 是全局无序，局部有序（当a相等时，会根据b进行排序）。直接执行b = 2这种查询条件无法使用索引。
当a的值确定的时候，b是有序的。例如a = 1时，b值为1，2是有序的状态。当a = 2时候，b的值为1，4也是有序状态。 当执行a = 1 and b = 2时a和b字段能用到索引。而执行a &gt; 1 and b = 2时，a字段能用到索引，b字段用不到索引。因为a的值此时是一个范围，不是固定的，在这个范围内b值不是有序的，因此b字段无法使用索引。</p>
<h2>什么是聚集索引？</h2>
<p>InnoDB使用表的主键构造主键索引树，同时叶子节点中存放的即为整张表的记录数据。聚集索引叶子节点的存储是逻辑上连续的，使用双向链表连接，叶子节点按照主键的顺序排序，因此对于主键的排序查找和范围查找速度比较快。</p>
<p>聚集索引的叶子节点就是整张表的行记录。InnoDB 主键使用的是聚簇索引。聚集索引要比非聚集索引查询效率高很多。</p>
<p>对于InnoDB来说，聚集索引一般是表中的主键索引，如果表中没有显示指定主键，则会选择表中的第一个不允许为NULL的唯一索引。如果没有主键也没有合适的唯一索引，那么InnoDB内部会生成一个隐藏的主键作为聚集索引，这个隐藏的主键长度为6个字节，它的值会随着数据的插入自增。</p>
<h2>什么是前缀索引？</h2>
<p>有时需要在很长的字符列上创建索引，这会造成索引特别大且慢。使用前缀索引可以避免这个问题。</p>
<p>前缀索引是指对文本或者字符串的前几个字符建立索引，这样索引的长度更短，查询速度更快。</p>
<p>创建前缀索引的关键在于选择足够长的前缀以保证较高的索引选择性。索引选择性越高查询效率就越高，因为选择性高的索引可以让MySQL在查找时过滤掉更多的数据行。</p>
<p>建立前缀索引的方式：</p>
<p>// email列创建前缀索引</p>
<p><code>ALTER TABLE table_name ADD KEY(column_name(prefix_length));</code></p>
<h2>什么是覆盖索引？</h2>
<p>select的数据列只用从索引中就能够取得，不需要回表进行二次查询，也就是说查询列要被所使用的索引覆盖。对于innodb表的二级索引，如果索引能覆盖到查询的列，那么就可以避免对主键索引的二次查询。</p>
<p>不是所有类型的索引都可以成为覆盖索引。覆盖索引要存储索引列的值，而哈希索引、全文索引不存储索引列的值，所以MySQL使用b+树索引做覆盖索引。</p>
<p>对于使用了覆盖索引的查询，在查询前面使用explain，输出的extra列会显示为using index。</p>
<p>比如user_like 用户点赞表，组合索引为(user_id, blog_id)，user_id和blog_id都不为null。</p>
<p><code>explain select blog_id from user_like where user_id = 13;</code></p>
<p>explain结果的Extra列为Using index，查询的列被索引覆盖，并且where筛选条件符合最左前缀原则，通过索引查找就能直接找到符合条件的数据，不需要回表查询数据。</p>
<p><code>explain select user_id from user_like where blog_id = 1;</code></p>
<p>explain结果的Extra列为Using where; Using index， 查询的列被索引覆盖，where筛选条件不符合最左前缀原则，无法通过索引查找找到符合条件的数据，但可以通过索引扫描找到符合条件的数据，也不需要回表查询数据。</p>
<h2>索引的设计原则？</h2>
<ol>
<li>索引列的区分度越高，索引的效果越好。比如使用性别这种区分度很低的列作为索引，效果就会很差。</li>
<li>尽量使用短索引，对于较长的字符串进行索引时应该指定一个较短的前缀长度，因为较小的索引涉及到的磁盘I/O较少，查询速度更快。</li>
<li>索引不是越多越好，每个索引都需要额外的物理空间，维护也需要花费时间。</li>
<li>利用最左前缀原则。</li>
</ol>
<h2>索引什么时候会失效？</h2>
<p>导致索引失效的情况：</p>
<ol>
<li>对于组合索引，不是使用组合索引最左边的字段，则不会使用索引</li>
<li>以%开头的like查询如%abc，无法使用索引；非%开头的like查询如abc%，相当于范围查询，会使用索引</li>
<li>查询条件中列类型是字符串，没有使用引号，可能会因为类型不同发生隐式转换，使索引失效</li>
<li>判断索引列是否不等于某个值时</li>
<li>对索引列进行运算</li>
<li>查询条件使用or连接，也会导致索引失效</li>
</ol>]]></content:encoded>
            <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">wangqiang</dc:creator>
      <pubDate>2023-07-05T14:35:54+08:00</pubDate>
                                                    </item>
        <item>
      <title><![CDATA[Mysql中使用索引的场景和不适合使用索引的场景]]></title>
            <link>https://aweb.wang/post/mysql-zhong-shi-yong-suo-yin-de-chang-jing-he-bu-shi-he-shi-yong-suo-yin-de-chang-jing</link>
      <guid isPermaLink="true">https://aweb.wang/post/mysql-zhong-shi-yong-suo-yin-de-chang-jing-he-bu-shi-he-shi-yong-suo-yin-de-chang-jing</guid>
      <description><![CDATA[什么情况下需要建索引？ 经常用于查询的字段 经常用于连接的字段建立索引，可以加快连接的速度 经常需要排序的字段建立索引，因为索引已经排好序，可以加快排序查询速度 什么情况下不建索引？ where条件中...]]></description>
            <content:encoded><![CDATA[<h2>什么情况下需要建索引？</h2>
<ul>
<li>经常用于查询的字段</li>
<li>经常用于连接的字段建立索引，可以加快连接的速度</li>
<li>经常需要排序的字段建立索引，因为索引已经排好序，可以加快排序查询速度</li>
</ul>
<h2>什么情况下不建索引？</h2>
<ul>
<li>where条件中用不到且不经常需要查询的字段不适合建立索引</li>
<li>表记录较少。比如只有几百条数据，没必要加索引。</li>
<li>需要经常增删改。需要评估是否适合加索引</li>
<li>参与列计算的列不适合建索引</li>
<li>区分度不高的字段不适合建立索引，如性别，只有男/女/未知三个值。加了索引，查询效率也不会提高。</li>
</ul>]]></content:encoded>
            <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">wangqiang</dc:creator>
      <pubDate>2023-07-05T14:09:26+08:00</pubDate>
                                                    </item>
        <item>
      <title><![CDATA[事务隔离级别有哪些？]]></title>
            <link>https://aweb.wang/post/shi-wu-ge-li-ji-bie-you-nei-xie</link>
      <guid isPermaLink="true">https://aweb.wang/post/shi-wu-ge-li-ji-bie-you-nei-xie</guid>
      <description><![CDATA[先了解下几个概念：脏读、不可重复读、幻读。 脏读 脏读是指在一个事务处理过程里读取了另一个未提交的事务中的数据。 不可重复读是指在对于数据库中的某行记录，一个事务范围内多次查询却返回了不同的数据值，这...]]></description>
            <content:encoded><![CDATA[<p>先了解下几个概念：脏读、不可重复读、幻读。</p>
<h2>脏读</h2>
<p>脏读是指在一个事务处理过程里读取了另一个未提交的事务中的数据。
不可重复读是指在对于数据库中的某行记录，一个事务范围内多次查询却返回了不同的数据值，这是由于在查询间隔，另一个事务修改了数据并提交了。</p>
<h2>幻读</h2>
<p>幻读是当某个事务在读取某个范围内的记录时，另外一个事务又在该范围内插入了新的记录。对幻读的正确理解是一个事务内的读取操作的结论不能支撑之后业务的执行。假设事务要新增一条记录，主键为id，在新增之前执行了select，没有发现id为xxx的记录，但插入时出现主键冲突，这就属于幻读，读取不到记录却发现主键冲突是因为记录实际上已经被其他的事务插入了，但当前事务不可见。</p>
<h2>不可重复读</h2>
<p>不可重复读和脏读的区别是，脏读是某一事务读取了另一个事务未提交的脏数据，而不可重复读则是读取了前一事务提交的数据。</p>
<p>事务隔离就是为了解决上面提到的脏读、不可重复读、幻读这几个问题。</p>
<p>MySQL数据库为我们提供的四种隔离级别：</p>
<p>Serializable (串行化)：通过强制事务排序，使之不可能相互冲突，从而解决幻读问题。
Repeatable read (可重复读)：MySQL的默认事务隔离级别，它确保同一事务的多个实例在并发读取数据时，会看到同样的数据行，解决了不可重复读的问题。
Read committed (读已提交)：一个事务只能看见已经提交事务所做的改变。可避免脏读的发生。
Read uncommitted (读未提交)：所有事务都可以看到其他未提交事务的执行结果。
查看隔离级别：</p>
<pre><code class="language-sql">select @@transaction_isolation;</code></pre>
<p>设置隔离级别：</p>
<pre><code class="language-sql">set session transaction isolation level read uncommitted;</code></pre>
<p>生产环境数据库一般用的什么隔离级别呢？
生产环境大多使用RC。为什么不是RR呢？</p>
<p><code>可重复读(Repeatable Read)，简称为RR 读已提交(Read Commited)，简称为RC</code></p>
<p>缘由一：在RR隔离级别下，存在间隙锁，导致出现死锁的几率比RC大的多！ 、</p>
<p>缘由二：在RR隔离级别下，条件列未命中索引会锁表！而在RC隔离级别下，只锁行!</p>
<p>也就是说，RC的并发性高于RR。</p>
<p>并且大部分场景下，不可重复读问题是可以接受的。毕竟数据都已经提交了，读出来本身就没有太大问题！</p>]]></content:encoded>
            <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">wangqiang</dc:creator>
      <pubDate>2023-07-05T14:05:15+08:00</pubDate>
                                                    </item>
        <item>
      <title><![CDATA[MYSQL数据库的三大范式]]></title>
            <link>https://aweb.wang/post/mysql-shu-ju-ku-de-san-da-fan-shi</link>
      <guid isPermaLink="true">https://aweb.wang/post/mysql-shu-ju-ku-de-san-da-fan-shi</guid>
      <description><![CDATA[第一范式1NF 确保数据库表字段的原子性。 比如字段 userInfo: 张三 18311111111' ，依照第一范式必须拆分成 userInfo: 张三 和 userTel:18311111111...]]></description>
            <content:encoded><![CDATA[<h2>第一范式1NF</h2>
<p>确保数据库表字段的原子性。</p>
<p>比如字段 userInfo: 张三 18311111111' ，依照第一范式必须拆分成 userInfo: 张三 和 userTel:18311111111两个字段。</p>
<h2>第二范式2NF</h2>
<p>首先要满足第一范式，另外包含两部分内容，一是表必须有一个主键；二是非主键列必须完全依赖于主键，而不能只依赖于主键的一部分。</p>
<p>举个例子。假定选课关系表为student_course(student_no, student_name, age, course_name, grade, credit)，主键为(student_no, course_name)。其中学分完全依赖于课程名称，姓名年龄完全依赖学号，不符合第二范式，会导致数据冗余（学生选n门课，姓名年龄有n条记录）、插入异常（插入一门新课，因为没有学号，无法保存新课记录）等问题。</p>
<p>应该拆分成三个表：学生：student(stuent_no, student_name, 年龄)；课程：course(course_name, credit)；选课关系：student_course_relation(student_no, course_name, grade)。</p>
<h2>第三范式3NF</h2>
<p>首先要满足第二范式，另外非主键列必须直接依赖于主键，不能存在传递依赖。即不能存在：非主键列 A 依赖于非主键列 B，非主键列 B 依赖于主键的情况。</p>
<p>假定学生关系表为Student(student_no, student_name, age, academy_id, academy_telephone)，主键为&quot;学号&quot;，其中学院id依赖于学号，而学院地点和学院电话依赖于学院id，存在传递依赖，不符合第三范式。</p>
<p>可以把学生关系表分为如下两个表：学生：(student_no, student_name, age, academy_id)；学院：(academy_id, academy_telephone)。</p>
<p>2NF和3NF的区别？</p>
<p>2NF依据是非主键列是否完全依赖于主键，还是依赖于主键的一部分。
3NF依据是非主键列是直接依赖于主键，还是直接依赖于非主键。</p>]]></content:encoded>
            <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">wangqiang</dc:creator>
      <pubDate>2023-07-05T14:01:03+08:00</pubDate>
                                                    </item>
        <item>
      <title><![CDATA[MYSQL事务的四大特性]]></title>
            <link>https://aweb.wang/post/mysql-shi-wu-de-si-da-te-xing</link>
      <guid isPermaLink="true">https://aweb.wang/post/mysql-shi-wu-de-si-da-te-xing</guid>
      <description><![CDATA[事务的四大特性？ 事务特性ACID：原子性（Atomicity）、一致性（Consistency）、隔离性（Isolation）、持久性（Durability）。 原子性是指事务包含的所有操作要么全部...]]></description>
            <content:encoded><![CDATA[<p>事务的四大特性？
事务特性ACID：原子性（Atomicity）、一致性（Consistency）、隔离性（Isolation）、持久性（Durability）。</p>
<p>原子性是指事务包含的所有操作要么全部成功，要么全部失败回滚。
一致性是指一个事务执行之前和执行之后都必须处于一致性状态。比如a与b账户共有1000块，两人之间转账之后无论成功还是失败，它们的账户总和还是1000。
隔离性。跟隔离级别相关，如read committed，一个事务只能读到已经提交的修改。
持久性是指一个事务一旦被提交了，那么对数据库中的数据的改变就是永久性的，即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。</p>]]></content:encoded>
            <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">wangqiang</dc:creator>
      <pubDate>2023-07-05T13:58:36+08:00</pubDate>
                                                    </item>
        <item>
      <title><![CDATA[GC如何判断对象可以被回收]]></title>
            <link>https://aweb.wang/post/gc-ru-he-pan-duan-dui-xiang-ke-yi-bei-hui-shou</link>
      <guid isPermaLink="true">https://aweb.wang/post/gc-ru-he-pan-duan-dui-xiang-ke-yi-bei-hui-shou</guid>
      <description><![CDATA[GC判断对象可被回收有两种方法：引用计数算法和根可达算法 引用计数算法 引用计数算法是一个已经被淘汰的算法，它是给每个对象加一个计数器，当有其他对象引用该对象时，该对象的计数器加一当这个引用失效时，计...]]></description>
            <content:encoded><![CDATA[<p>GC判断对象可被回收有两种方法：引用计数算法和根可达算法</p>
<h2>引用计数算法</h2>
<p>引用计数算法是一个已经被淘汰的算法，它是给每个对象加一个计数器，当有其他对象引用该对象时，该对象的计数器加一当这个引用失效时，计数器就会减一，当该对象的计数器为零时，就会认为该对象可以被所回收。
引用计数算法是一个简单并且高效的算法，但这种算法却有一个非常大的弊端。就是这种算法会造成对象的循环引用，导致即南这人对象不再被需要，仍然存在一个一直指向它的引用，使得计数器不为零，导致该对象无法被回收，造成内存空间的浪费</p>
<h2>根可达性算法</h2>
<p>根可达性算法是JVM默认的算法，他的原理就是定义一系列的根，我们把这些根称为:GC Rots。从GC Roots开始向下搜中间查找的路径被称为: 引用链索，
当一个对象到GC Roots之间没有任何引用链相连接时，我们就认为这个对象可以被GC回收。根可达性很好的解决了对象循环引用问题</p>
<h2>JAVA的四种引用类型</h2>
<p>在上述判断对象在何时能被GC收是，我们多次提到了引用，那大家一定好奇什么是引用? 引用有分为那些类型?一下我们就一一列举。
什么是引用?
举个例子: 假设我们创建了一个创建了一个User对象，就像这样: new User0，但是也仅仅是创建了一个对象，并没有办法访问它，如果我们要访问这个对象，就必须指定一个User类型变量来表示这个对象: User user = new User，user又被叫做”用”，&quot;=&quot;是指user这个引用指向User对象。 所以，java里对象的引用就是一个类类型的变量指向创建出来的对象。
引用有分为那些类型?
在Java中引用被分为四种类型: 强引用、软引用、弱引用、虚引用.</p>]]></content:encoded>
            <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">wangqiang</dc:creator>
      <pubDate>2023-07-05T08:36:44+08:00</pubDate>
                                                    </item>
        <item>
      <title><![CDATA[MySQL的复合索引与覆盖索引]]></title>
            <link>https://aweb.wang/post/mysql-de-fu-he-suo-yin-yu-fu-gai-suo-yin</link>
      <guid isPermaLink="true">https://aweb.wang/post/mysql-de-fu-he-suo-yin-yu-fu-gai-suo-yin</guid>
      <description><![CDATA[复合索引前导列特性 在MySQL中，如果创建了复合索引(name, salary, dept)，就相当于创建了(name, salary, dept)、(name, salary)、(name)三个索...]]></description>
            <content:encoded><![CDATA[<p>复合索引前导列特性
在MySQL中，如果创建了复合索引(name, salary, dept)，就相当于创建了(name, salary, dept)、(name, salary)、(name)三个索引，这被称为复合索引前导列特性，因此在创建复合索引时应将最常用作查询条件的列放在最左边，依次递减。
未使用索引</p>
<pre><code class="language-sql">SELECT * FROM employee WHERE salary = 8800;
SELECT * FROM employee WHERE dept = '部门A';
SELECT * FROM employee WHERE salary = 8800 and dept = '部门A';</code></pre>
<p>使用索引</p>
<pre><code class="language-sql">SELECT * FROM employee WHERE name = '刘峰';
SELECT * FROM employee WHERE name = '刘峰' and salary = 8800;
SELECT * FROM employee WHERE name = '刘峰' and salary = 8800 and dept = '部门A';</code></pre>
<p>覆盖索引
覆盖索引又称之为索引覆盖，即select的数据列只从索引中就能得到，不必读取数据行，也就是只需扫描索引就可以得到查询结果。</p>
<p>关于覆盖索引的几点说明：</p>
<pre><code>1.使用覆盖索引，只需从索引中就能检索到需要的数据，而不要再扫描数据表。
2.索引的体量往往比数据表小很多，因此只读取索引速度会非常快，也会极大减少数据访问量。
3.MySQL的查询优化器会在执行查询前判断，是否有一个索引可以覆盖所有的查询列。
4.并非所有类型的索引都可以作为覆盖索引，覆盖索引必须要存储索引列的值。像哈希索引、空间索引、全文索引等并不会真正存储索引列的值。</code></pre>
<p>分析索引使用情况关键字：EXPLAIN</p>]]></content:encoded>
            <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">wangqiang</dc:creator>
      <pubDate>2023-07-05T08:24:08+08:00</pubDate>
                                                    </item>
        <item>
      <title><![CDATA[PHPExcel的导出和识别]]></title>
            <link>https://aweb.wang/post/phpexcel-de-dao-chu-he-shi-bie</link>
      <guid isPermaLink="true">https://aweb.wang/post/phpexcel-de-dao-chu-he-shi-bie</guid>
      <description><![CDATA[首先需要安装phpexcel依赖： composer require phpoffice/phpexcel 定义一个上传类： &lt;?php namespace app\common\utils; ...]]></description>
            <content:encoded><![CDATA[<p>首先需要安装phpexcel依赖：
<code>composer require phpoffice/phpexcel</code>
定义一个上传类：</p>
<pre><code class="language-php">&lt;?php

namespace app\common\utils;

use app\common\exception\BusinessException;
use app\common\exception\BusinessExceptionEnum;
use Qiniu\Auth;
use think\facade\Config;
use think\facade\Request;
use Qiniu\Storage\UploadManager;

class UploadUtils
{
    public static function Excelupload($file,$path)
    {
            if ($file==null) {
                throw new BusinessException(BusinessExceptionEnum::FILE_EMPTY);
            }
            else{

                $temp = explode(".", $_FILES["file"]["name"]);
                $extension = end($temp);
                // 判断文件是否合法
                if(!in_array($extension, array('xls','xlsx'))){
                    throw new BusinessException(BusinessExceptionEnum::FILE_WRONG);
                }else{
                    $savename = \think\facade\Filesystem::disk('public')-&gt;putFile( $path, $file);
                    $savename=str_replace('\\', '/', $savename);
                    $data['sendurl']=app()-&gt;getRootPath().'/public/storage/'.$savename;
                    $data['savename']=$savename;
                    $data['ext']=$extension;
                    return $data;
                }

        }

    }
}</code></pre>
<p>Excel工具类：</p>
<pre><code class="language-php">&lt;?php

namespace app\common\utils;
use app\common\exception\BusinessException;
use app\common\exception\BusinessExceptionEnum;
use PHPExcel_IOFactory;
use think\facade\Filesystem;

class ExcelUtils
{
    public static function shibie($excelFile){
        $retData=UploadUtils::excelupload($excelFile,'/excel');
        $inputFileName=$retData['sendurl'];
        $inputFileType=PHPExcel_IOFactory::identify($inputFileName);
        if($retData['ext']=='xlsx'){
            $objReader=\PHPExcel_IOFactory::createReader('Excel2007');
        }else if($retData['ext']=='xls'){
            $objReader=\PHPExcel_IOFactory::createReader('Excel5');
        }

        $objPHPExcel=$objReader-&gt;load($inputFileName);

        $data['excelArray']=$objPHPExcel-&gt;getSheet(0)-&gt;toArray();
        $data['highestRow']=$objPHPExcel-&gt;getSheet(0)-&gt;getHighestRow()-1;
        unlink($retData['sendurl']);
        return $data;
    }
    public static function makeExcel($data,$filename,$title,$description,$keywords,$category){
        //初始化
        $filename=str_replace('.xls', '', $filename).'.xls';
        $phpexcel = new \PHPExcel();
        $phpexcel-&gt;getProperties()
            -&gt;setCreator("CMSSYSTEM")
            -&gt;setLastModifiedBy("CMSSYSTEM")
            -&gt;setTitle($title)
            -&gt;setSubject($title)
            -&gt;setDescription($description)
            -&gt;setKeywords($keywords)
            -&gt;setCategory($category);
        $phpexcel-&gt;getActiveSheet()-&gt;fromArray($data);
        $phpexcel-&gt;getActiveSheet()-&gt;setTitle('Sheet1');
        $phpexcel-&gt;setActiveSheetIndex(0);
        header('Content-Type: application/vnd.ms-excel');
        header("Content-Disposition: attachment;filename=$filename");
        header('Cache-Control: max-age=0');
        header('Cache-Control: max-age=1');
        header ('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); // Date in the past
        header ('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); // always modified
        header ('Cache-Control: cache, must-revalidate'); // HTTP/1.1
        header ('Pragma: public'); // HTTP/1.0
        $objwriter = PHPExcel_IOFactory::createWriter($phpexcel, 'Excel5');
        $objwriter-&gt;save('php://output');
        exit;
    }
}</code></pre>
<p>识别：</p>
<pre><code class="language-php">$data=ExcelUtils::shibie(request()-&gt;file('file'));//返回数组形式的数据</code></pre>
<p>生成并下载：</p>
<pre><code class="language-php">$data=[['年龄','性别','姓名'],[12,'女','张三'],[11,'男','李四']];
ExcelUtils::makeExcel($data,'人员','人员','人员列表','人员','OA');</code></pre>]]></content:encoded>
            <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">wangqiang</dc:creator>
      <pubDate>2023-03-08T09:58:15+08:00</pubDate>
                                                    </item>
      </channel>
</rss>
