<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://congjyu.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://congjyu.github.io/" rel="alternate" type="text/html" hreflang="en" /><updated>2026-06-01T09:47:18+00:00</updated><id>https://congjyu.github.io/feed.xml</id><title type="html">Rain Chen&apos;s Blog</title><subtitle>One Personal Site.</subtitle><author><name>Rain Chen</name></author><entry><title type="html">Autonomous Driving Review</title><link href="https://congjyu.github.io/blog/2026/05/04/autonomous-driving/" rel="alternate" type="text/html" title="Autonomous Driving Review" /><published>2026-05-04T00:00:00+00:00</published><updated>2026-05-04T00:00:00+00:00</updated><id>https://congjyu.github.io/blog/2026/05/04/autonomous-driving</id><content type="html" xml:base="https://congjyu.github.io/blog/2026/05/04/autonomous-driving/"><![CDATA[<h1 id="heading-autonomous-driving-review-problems-and-explanations">Autonomous Driving Review: Problems and explanations</h1>

<h2 id="heading-multiple-choice-1">Multiple Choice 1</h2>

<h3 id="heading-problem-descriptions">Problem Descriptions</h3>

<p>What is a key weakness of data-driven track-based prediction?</p>

<p>A. Over-reliance on hand-engineered physical models.</p>

<p>B. Inability to use HD Map context.</p>

<p>C. High computational cost and low explainability.</p>

<p>D. Cannot handle partial observation scenarios.</p>

<h3 id="heading-solution">Solution</h3>

<p>C.</p>

<h3 id="heading-explanations">Explanations</h3>

<p>For high computational cost, unlike simple kinematic models like Constant Velocity or Constant Steering Angle, data-driven models, such as LSTMs, Transformers, or Graph Neural Networks, require substantial memory and processing power. In the context of an autonomous vehicle, where predictions must be updated every few milliseconds, this "compute tax" is a major hurdle.</p>

<p>For low explainability, data-driven models operates as black boxes. If a model predics that a cyclist will suddenly veer into traffic, it is often impossible to trace the specific logic behind that decision within the millions of neural network weights. This makes it difficult to validate for safety-critical applications compared to model-based approaches where you can see the physical vectors.</p>

<p>Why option A is inccorect? Over-reliance on hand-engineered physical models, this is actually a weakness of model-based prediction, not data-driven prediction. Data-driven models specifically aim to move away from hand-engineered rules.</p>

<p>Why option B is inccorect? Inability to use HD Map context. Many data-driven tracked-based models like VectorNet are specifically designed to fuse agent tracks with HD map data. It is a strength of modern data-driven architectures, not a inherent weakness.</p>

<p>Why option D is inccorect? Cannot handle partial observation scenarios. Modern data-driven models are actually quite robust at "filling in the gaps" using learned temporal patterns via Recurrent Neural Networks or Attention mechanisms, often performing better than rigid physical models when sensor data is noisy or occluded.</p>

<h2 id="heading-multiple-choice-2">Multiple Choice 2</h2>

<h3 id="heading-problem-descriptions-1">Problem Descriptions</h3>

<p>What is a key advantage of the linear quadratic regulator (LQR) compared to PID control?</p>

<p>A. Requires no tuning of parameters.</p>

<p>B. Optimizeds a quadratic cost function for linear systems (optimal control).</p>

<p>C. Works without a mathematical model of the system.</p>

<p>D. Is computationally cheaper for real-time deployment.</p>

<h3 id="heading-solution-1">Solution</h3>

<p>B.</p>

<h3 id="heading-explanations-1">Explanations</h3>

<p>While PID control is a model-free apprach based on trial and error (tuning gains), LQR is a model-based approach that uses the physics of the system to find the mathematically best possible control strategy.</p>

<p>LQR is the gold standard for Optimal Control. It works by minimizing a specific quadratic cost function $J$.</p>

\[J = \int_{0}^{\infty} (x^T Q x + u^T R u)dt\]

<p>$x^TQx$ represents the penalty for the state being away from the target (error).</p>

<p>$u^TRu$ represents the penalty for using control effort (energy/actuation).</p>

<p>By adjusting the $Q$ and $R$ matrices, an engineer can mathematically decide the exact trade-off between how fast the system reaches the target versus how much energy it consumes. PID control cannot provide this "guaranteed optimality" because it does not know the physics of the system.</p>

<p>Why other choices are not correct?</p>

<p>For A, requires no tuning of parameters, this is false. While we do not tune $P$, $I$, and $D$ gains directly, we must tune the weighting matrices ($Q$ and $R$).</p>

<p>For C, works without a mathematical model, this is inccorect. LQR requires a precise State-Space Model (in the form of $\dot{x} = Ax + Bu$). PID is the one that can work without a model.</p>

<p>For D, is computational cheaper, which is generally false. PID is a very simple calculation. LQR requires solving the Algebraic Riccati Equation and performing matrix multiplications online. While modern computers handle both easily, LQR is technically more complex.</p>

<p>In summary,</p>

<table>
  <thead>
    <tr>
      <th>Feature</th>
      <th>PID Control</th>
      <th>LQR Control</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Model Needed?</td>
      <td>No (Model-free)</td>
      <td>Yes (State-space model)</td>
    </tr>
    <tr>
      <td>Optimality?</td>
      <td>No (Heuristic)</td>
      <td>Yes (Mathematically optimal)</td>
    </tr>
    <tr>
      <td>Handling MIMO?</td>
      <td>Difficult (needs multiple loops)</td>
      <td>Natural (handles multiple inputs/outputs)</td>
    </tr>
    <tr>
      <td>Complexity</td>
      <td>Low</td>
      <td>Moderate</td>
    </tr>
  </tbody>
</table>

<h2 id="heading-multiple-choice-3">Multiple Choice 3</h2>

<h3 id="heading-problem-descriptions-2">Problem Descriptions</h3>

<p>In closed-loop learning for planning, what is the key advantage over open-loop learning?</p>

<p>A. No need for expert demonstration data.</p>

<p>B. Eliminates distribution shift between training and inference.</p>

<p>C. Lower computational cost during training.</p>

<p>D. Does not require simulation or real-world deployment.</p>

<h3 id="heading-solution-2">Solution</h3>

<p>B.</p>

<h3 id="heading-explanations-2">Explanations</h3>

<p>Eliminates distribution shift between training and inference. This is a fundamental concept in imitation learning and autonomous robotics. The "loop" refers to whether the model's actions affect the next state it observes during the learning process.</p>

<p>In open-loop learning, a model is trained on a fixed dataset of expert demonstrations. The model learns: "In state $s$, do action $a$."</p>

<p>However, at ingerence time, the model will inevitably make a tiny mistake. Because it is in a "closed loop" with the real world, that mistake leads to a slightly different state that what was in the training data. These errors compound quickly.</p>

<ol>
  <li>The model makes a small error.</li>
  <li>It enters a state it never saw during training (distribution shift).</li>
  <li>Because it hasn't learned what to do in that "weird" state, it makes an even bigger error.</li>
  <li>The system "drifts" off the path and eventrally crashes.</li>
</ol>

<p>For choice B, closed-loop learning such as Reinforcement Learning or DAgger allows the model to interact with the environment during training. It exxperiences the consequences of its own actions. It learns how to recover from its own mistakes. By training on the states it actually visits rather than just the expert's perfect states the "training distribution" matches the "inference distribution," effectively eliminating the shift.</p>

<p>For choice A, no need for expert demonstration data. It is incorrect. Many closed-loop methods, like DAgger, specifically rely on an expert to "label" the new states the model visits.</p>

<p>For choice C, lower computational cost during training. It is also incorrect. Closed-loop learning is significantly more expensive because you have to constantly run a simulator or the real robot and potentally query an expert for new labels.</p>

<p>For choice D, does not require simulation or real-world deployment. This is the opposite of the truth. A simulator or a real-world environment is a must to close the loop; otherwise, it may stack with a static (open-loop) dataset.</p>

<h2 id="heading-multiple-choice-4">Multiple Choice 4</h2>

<h3 id="heading-problem-description">Problem Description</h3>

<p>What is a core challenge of using Reinforcement Learning (RL) for real-world autonomous driving planning?</p>

<p>A. Cannot incorporate expert demonstration data.</p>

<p>B. Prohibitive sample inefficiency and safety risks.</p>

<p>C. Lacks interpretability compared to rule-based methods.</p>

<p>D. Cannot handle dynamic obstacle interactions.</p>

<h3 id="heading-solution-3">Solution</h3>

<p>B.</p>

<h3 id="heading-explanations-3">Explanations</h3>

<p>While Reinforcement Learning (RL) has shown incredible success in virtual environments, transitioning that success to the "real-world" roads is difficult because of how RL agents learn.</p>

<p>Why Choice B is correct? Reiniforcement Learning is fundatmentally based on trial and error. In an autonomous driving context, this leads two massive roadblocks.</p>

<p>One is sample inefficiency. To learn a complex behavior, like navigating a busy intersection, an RL agent might need millions of training episodes. In the real world, we cannot afford the time of the fleet size required to gather that much data through "discovery".</p>

<p>The other one is safety risks (the exploration problem). For an RL agent to learn that "crashing into a wall is bad," it technically has to "explore" that action or get very close to it to receive a negative reward. In a simulator, this is fine; in a 4000-pound vehicle on a public road, "exploring" a collision is catastrophic. This makes safe exploration one of the biggest hurdles in the field.</p>

<p>Why other choices are incorrect?</p>

<p>For A, cannot incorporate expert demonstration data. This is false. Techniques like Imitation Learning or Pre-training with Expert Demonstrations are frequently used to kickstart an RL agent so it does not start from zero.</p>

<p>For C, lacks interpretaility compared to rule-based methods. While true that RL is a "black box" compared to "if-then" rules, this is a general weakness of all deep learning methods. Choice B is considered a more specific core challenge for deployment because even a "black box" that worked perfectly would be deployed if it weren't so dangerous and slow to train.</p>

<p>For D, cannot handle dynamic obstacle interactions. This is incorrect. One of the main reasons researchers want to use RL is that it is actually better at handling complex, dynamic interactions than rigid, rule-based systems. The problem is not the capability, it is the training process.</p>

<p>The "Sim-to-REal" Gap</p>

<p>To solve these issues, researchers often train RL agents in high-fidelity simulators. However, this introduces the Reality Gap, a policy that learns to drive perfectly in a simulation may fail in the real world because the simulator's physics or sensor noise is not 100% accurate.</p>

<h2 id="heading-multiple-choice-5">Multiple Choice 5</h2>

<h3 id="heading-problem-description-1">Problem Description</h3>

<p>In camera-based 3D perception, what is the core idea of the "Lift to 3D at Input" approach?</p>

<p>A. Convert image to pseudo-LiDAR via dense depth prediction.</p>

<p>B. Re-parameterize output of off-the-shelf 2D detectors.</p>

<p>C. Project 2D feature map to 3D/BEV space via camera intrinsic/extrinsic</p>

<p>D. Detect objects in 2D first then localize them in 3D.</p>

<h3 id="heading-solution-4">Solution</h3>

<p>C.</p>

<h3 id="heading-explanations-4">Explanations</h3>

<p>The correct answer is C. Project 2D feature map to 3D/BEV space via camera intrinsic/extrinsic.</p>

<p>This approach is the foundation of modern Bird's Eye View (BEV) perception stacks, famouly popularized by architectures like LSS (Lift, Splat, Shoot). Instead of detecting objects in individual images and trying to "switch" them together later, the model transforms the raw visual data into a 3D-consistent space early in the pipeline.</p>

<p>The "code idea" is a geometric transformation. Because 2D images lack depth, the model must "lift" the 2D features into 3D. This involves:</p>

<ol>
  <li>Feature Extraction: Running a standard backbone on the 2D images.</li>
  <li>Lifting: Assigning a depth estimate (often a probability distribution) to each feature.</li>
  <li>Geometric Projection: Using the Camera Intrinsics ($K$) and Extrinsics ($[R \vert t]$) to map those 2D features into a 3D voxel grid of BEV plane. THe math follows the standard pinhole camera projection model:</li>
</ol>

\[z = \begin{bmatrix}
    u \\ v \\ 1
\end{bmatrix} = K (R \mathbf{X} + t)\]

<p>where $\mathbf{X}$ is the 3D coordinate and $(u, v)$ is the image coordinate.</p>

<p>Why the others are incorrect?</p>

<p>For A, convert image to pseudo-LiDAR. THis is specifically the Pseudo-LiDAR paradigm. While it is a form of lifting, it focusses on creating a point cloud from depth maps rather than projecting features maps into a unified latent space. It is considered a subset/precursor to the more general feature-lifting in Choice C.</p>

<p>For B, Re-parameterize output of 2D detectors. This is known as "Lifting at the Output." Models like FCOS3D detect objects in 2D first and then use geometric constraints to "guess" the 3D bounding box dimensions. This is the opposite of Choice C, which lifts at the input/feature stage.</p>

<p>For D, dtect objects in 2D first. This refers to "2D-Driven 3D Detection". It relies on a 2D crop to narrow down the search space in 3D, rather than creating a global 3D representation from the start.</p>

<p>Key Advantage</p>

<p>By lifting to 3D at the input/feature level, the system can naturally fuse data from multiple cameras into a single, seamless top-down view. This eliminates the "double-counting" of objects that appear in the overlap between two camera feeds.</p>

<h2 id="heading-multiple-choice-6">Multiple Choice 6</h2>

<h3 id="heading-descriptions">Descriptions</h3>

<p>What is a major disadvantage of Model Predictive Control (MPC) for autonomous driving?</p>

<p>A. Cannot handle hard constraints (e.g. max steering rate)</p>

<p>B. Slower than traditional methods (e.g. PID) due to optimization</p>

<p>C. Requires no vehicle model to operate</p>

<p>D. Fails to minimize tracking errors for dynamic trajectories</p>

<h3 id="heading-solution-5">Solution</h3>

<p>B.</p>

<h3 id="heading-explanations-5">Explanations</h3>

<p>Slower than traditional methods (e.g. PID) due to optimization. Model Predictive Control (MPC) is incredibly powerful, but its "brain" has to work much harder than a PID controller or a simple Pure Pursuit algorithm.</p>

<p>Unlike PID, which simply reacts to the current error, MPC is an online optimizer. At every single time step, the vehicle must:</p>

<ol>
  <li>Predict the future state of the car over a "horizon" (e.g. the next 2 - 5 seconds).</li>
  <li>Solve a complex mathematical optimization problem to find the best sequence of steering and acceleration commands.</li>
  <li>Repeat this entire process for the next time step.</li>
</ol>

<p>If you have a complex non-linear vehicle model or a long prediction horizon, this requires significant CPU/GPU power. In the high-spped world of autonomous driving, if the optimization takes too long to solve, the "optimal" command arrives too late to be useful.</p>

<p>Why the others are incorrect?</p>

<p>For A, cannot handle hard constraints. This is actually the main advantage of MPC. It is specifically designed to respect physical limits, like "don't turn the steering wheel faster than 40 degrees/sec" or "don't exceed 65 mph."</p>

<p>For C, requires no vehicle model to operate. This is incorrect. MPC is a model-based controller. It absolutely requires a mathematical model (kinematic or dynamic) to predict how the car will behave in the future.</p>

<p>For D, fails to minimize tracking errors. Incorrect. MPC is generally better at minimizing tracking errors than PID because it "sees" the curves comming in the trajectory and can start turning on the path perfectly.</p>

<p>Summary, the MPC trade-off.</p>

<p>Think of PID as a driver who only looks at the road 1 foot in front of the bumper, fast and simple, but twitchy. Think of MPC as a driver looking 100 feet ahead and calculating the perfect line, smooth and safe, but requires a lot of "mental" effort to process.</p>

<h2 id="heading-multiple-choice-7">Multiple Choice 7</h2>

<h3 id="heading-descriptions-1">Descriptions</h3>

<p>What is the key function of the Derivative (D) element in a PID controller?</p>

<p>A. Amplify the current error to speed up response.</p>

<p>B. Correct steady-state errors to speed up response.</p>

<p>C. Introduce damping to reduce overshoot and oscillation.</p>

<p>D. Switch abruptly between two control states.</p>

<h3 id="heading-solution-6">Solution</h3>

<p>C.</p>

<h3 id="heading-explanations-6">Explanations</h3>

<p>While the Proportional (P) term looks at the present and the Integral (I) term looks at the past, the Derivative (D) term is the "prophet" of the PID controller, it looks at the future.</p>

<p>The Derivative term calculates the rate of change of the error. Its primary job is to act as a "brake" or a shock absorber for the system.</p>

<ul>
  <li>Anticipation: If the error is decreasing rapidly (meaning the system is approaching the target very fast), the derivative of the error becomes negative. This substracts from the total control output.</li>
  <li>Damping: By pushing back against the motion as the system nears its goal, the D term prevents the system from "sprinting" past the target. This reduces overshoot and settles the oscillations that a high P-gain would otherwise cause.</li>
</ul>

<p>Why the others are incorrect?</p>

<p>For A, amplify the current error. THis is the job of the Proportional (P) term. A higher P-gain makes the system more aggresive in the present but oftern leads to overshoot.</p>

<p>For B, correct steady-state errors. This is the job of the Integral (I) term. The I term accumulates small errors over time to ensure the system eventually reaches the exact target, even if there is a constant bias (like wind pushing against a drone).</p>

<p>For D, switch abruptly between two states. This describes a Bang-Bang controller (like a basic thermostat that is either 100% ON or 100% OFF), not a PID controller, which provides a smooth, continuous output.</p>

<p>The "D" Trade-off: Noise</p>

<p>While the D term is great for stability, its biggest weakness is high-frequency noise. Sins the derivative is the slope of the error, a tiny bit of "jitter" in a sensor reading can result in a massive, spikey derivative. This is why, in real-world applications, the D-term is almost always paired with a low-pass filter to keep the "brakes" from stuttering.</p>

<p>The standard PID formula in the time domain is:</p>

\[u(t) = K_p e(t) + K_i \int_{0}^{t} e(\tau) d \tau + K_d \frac{de(t)}{dt}\]

<h2 id="heading-simple-answer-question-1">Simple Answer Question 1</h2>

<h3 id="heading-descriptions-2">Descriptions</h3>

<p>Compare the core differences between GNSS-based and Environment Perception-Based localization in update rate and drift characteristics, and analyze the impact of these differences on the real-time control of autonomous driving.</p>

<h3 id="heading-explanations-7">Explanations</h3>

<table>
  <thead>
    <tr>
      <th>Feature</th>
      <th>GNSS</th>
      <th>Environment Perception</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Update Rate</td>
      <td>Low (Typically 1Hz - 10Hz)</td>
      <td>High (Typically 10Hz - 100Hz)</td>
    </tr>
    <tr>
      <td>Drift</td>
      <td>Zero cumulative drift (Global reference)</td>
      <td>Cumulative drift (Incremental relative motion)</td>
    </tr>
    <tr>
      <td>Accuracy</td>
      <td>Decimeters (RTK) to Meters (Standard)</td>
      <td>Centimeters (Relative to surroundings)</td>
    </tr>
    <tr>
      <td>Environment</td>
      <td>Needs clear sky (Fails in tunnels/canyons)</td>
      <td>Needs features (Fails in fog/featureless roads)</td>
    </tr>
  </tbody>
</table>

<p><strong>GNSS: The "Noisy Truth"</strong></p>

<p>GNSS provides a global coordinate. Its greatest strength is that it does not "drift." If one is at Point A, and he drive 100 miles to Point B, the error at Point B is roughly the same as it was at Point A.</p>

<ul>
  <li>The Problem: The update rate is slow (10Hz is standard for high-end units). In a caar moving at 65 mph (approx. 29 m/s), a 10 Hz sensor only gives a position every 2.9 meters. For high-speed control, that is a massive, dangerous gap. Furthermore, GNSS suffers from multipath errors in cities, causing the position to "jump" suddenly by several meters.</li>
</ul>

<p><strong>Environment Perception: The "Smooth Liar"</strong></p>

<p>EPB methods (like LiDAR mapping or Visual Odometry) compare current sensor data to previous frames or a pre-built HD map.</p>

<ul>
  <li>The Strengh: Because these sensors (Cameras/LiDAR) operate at high frequencies and prvide dense data, the localization is incredibly smooth and precise in the short term.</li>
  <li>The Problem: Because they calculate motion relative to the last know position, tiny errors add up. This is cumulative drift. Without a global reset, a car relying solely on odometry will think it is in a different zip code than it actually is.</li>
</ul>

<p><strong>Impact on Real-Time Control</strong></p>

<p>The differences between these two systems dictate how the vehicle actually "feels" on the road.</p>

<p><strong>The "Control Latency" Problem</strong></p>

<p>Vehicle controllers (like the PID or MPC mentioned earlier) usually require feedback at 50Hz to 100Hz to maintain stability, especially at high speeds.</p>

<ul>
  <li>If used GNSS alone, the controller would be "blind" between those 10Hz updates. The car would likely oscillate or "hunt" for the lane center because the feedback is too slow to catch small deviations.</li>
  <li>EPB and IMUs (Inertial Meaurement Units) provide the high-frequency "filter" that allows the controller to make micro-adjustments to steering and braking every few milliseconds.</li>
</ul>

<p><strong>Glabal vs. Local Consistency</strong></p>

<ul>
  <li>Real-Time Steering: This relies on Local Consistency. The car doesn't care if its Latitude/Longitude is off by a meter as long as it knows it is exactly 20cm from the left lane line. Perception-based localization excels here.</li>
  <li>Path Planning: This relies on Global Consistency. if the car is navigating to a specific highway exit 5 miles away, it needs GNSS to ensure it does not drift off the global map and miss the turn entirely.</li>
</ul>

<h2 id="heading-simple-answer-question-2">Simple Answer Question 2</h2>

<h3 id="heading-descriptions-3">Descriptions</h3>

<p>Explain the three main approaches of "Lift to 3D" in camera-based 3D perception (Output/Input/Feature Map), and list one strength for each approach.</p>

<h3 id="heading-explanations-8">Explanations</h3>

<p>In camera-based 3D perception, "lifting" refers to the mathematical and architectural process of recovering the missing depth dimension to transform 2D image data into a 3D spatial representation. The three approaches differ primarily in where in the neural network pipeline this transformation occurs.</p>

<p><strong>1. Lift at Output (Image-to-3D)</strong></p>

<p>In this approach, the network processes the image entirely in 2D using standard convolutional backbones. The "lifting" happens at the very end (the prediction heads), where the model attempts to regress 3D properties directly from 2D features.</p>

<ul>
  <li>How it works: A 2D detector identifies an object and its bounding box. The model then predcts additional 3D attributes like the object's depth ($d$), 3D dimensions $(w, h, l)$, and orientation ($\theta$) based on visual cues (e.g. the object's size in the image or perspective lines).</li>
  <li>Key Strength: Low Computational Latency. Since the heavy lifting (feature extraction) happens in 2D, these models are extremely fast and can often run in real-time on low-power hardware.</li>
</ul>

<p><strong>2. Lift at Input (Pseudo-LiDAR)</strong></p>

<p>This approach transforms the 2D data into a 3D representation before the main detection of perception task begins. It essentially tries to make a camera "act" like a LiDAR sensor.</p>

<ul>
  <li>How it works: The system uses a dedicated sub-network to perform dense depth estimation for every pixel in the image. This depth map is then projected into 3D space using camera intrinsics to create a Pseudo-LiDAR point cloud. A standard 3D detection network (designed for LiDAR) is then run on this point cloud.</li>
  <li>Key Strength: High Interpretability. Because the intermediate output is a physical 3D point cloud, engineers can visually inspect the depth accuracy, making it easier to debug sensor calibration or depth estimation errors.</li>
</ul>

<p><strong>3. Lift at Feature Map (BEV/Latent Space)</strong></p>

<p>This is the state-of-the-art approach used by systems like Tesla's FSD and various BEV (Bird's Eye View) models. It lifts intermediate latent features rather than raw pixels or final detections.</p>

<ul>
  <li>How it works: A 2D backbone extracts feature maps from multiple camera images. These feature maps are then projected into a unified 3D "Voxel" or 2D "BEV" grid using geometric transformations (like the Lift-Splat-Shoot method). THe network then performs detection or motion planning directly in this unified top-down space.</li>
  <li>Key Strenght: native Multi-View Fusion. Because the features are lifted into a common 3D space, the model can seamlessly "switch" together information from different cameras. An object partially visible in the front-left and side-left cameras is seen as a single coherent entity in the BEV feature map.</li>
</ul>

<p><strong>Summary Table</strong></p>

<table>
  <thead>
    <tr>
      <th>Approach</th>
      <th>Where Lifting Occurs</th>
      <th>Core Mechanism</th>
      <th>Main Strength</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Output</td>
      <td>Prediction Heads</td>
      <td>Regression of 3D attributes</td>
      <td>Speed &amp; Efficiency</td>
    </tr>
    <tr>
      <td>Input</td>
      <td>Pre-processing</td>
      <td>Dense Depth $\rightarrow$ Point Cloud</td>
      <td>Physical Interpretability</td>
    </tr>
    <tr>
      <td>Feature Map</td>
      <td>intermediate Layers</td>
      <td>Geometric Projection to BEV</td>
      <td>Seamless Multi-Camera Fusion</td>
    </tr>
  </tbody>
</table>

<h2 id="heading-simple-answer-question-3">Simple Answer Question 3</h2>

<h3 id="heading-descriptions-4">Descriptions</h3>

<p>What are affordances in motion planning, and what are their key advantages and limitations compared to direct actuation output (steering/acceleration)?</p>

<h3 id="heading-explanations-9">Explanations</h3>

<p>In the context of motion planning, affordances are intermediate, high-level features that represent the "state of the world" in a way that is directly relevant to driving tasks.</p>

<p>Instead of a model trying to go straight from raw camera pixels to a steering wheel angle (End-to-End) or trying to identify every single pixel in the scene (Semantic Segmentation), it extracts specific "labels" that matter for decision-making.</p>

<p>What do they look like?</p>

<p>Typical affordances in autonomous driving include:</p>

<ul>
  <li>Distance to the lane center: How far left or right am I?</li>
  <li>Heading angle: Is my car pointed parallel to the road or at an angle?</li>
  <li>Distance to the preceding vehicle: How much "gap" do I have?</li>
  <li>Relative speed: is the car in front pulling away or braking?</li>
</ul>

<p><strong>Advantages Over Direct Actuation (End-to-End)</strong></p>

<p>The "Direct Acuation" approach, where a neural network takes an image and outputs a torque value, is often called a black box. Affordances offer a more transparent middle ground.</p>

<ul>
  <li>Interpretability: If the car suddenly swerves, we can check the affordances. Did it think the lane center was 2 meters to the left? This makes debugging and safety validation much easier.</li>
  <li>Reduced Distribution Shigt: Steering commands are specific to a single car's physics (tires, weight, steering rack). Affordances (like "I am 1 meter from the curb") are universal. A model trained to find affordances on a Toyota can theoretically be used on a Tesla without starting from scratch.</li>
  <li>Easier Learning Task: It is mathematically "easier" for a network to learn geometry (lines and distances) than it is to learn the complex mapping between a blurry pixel and the exact voltage needed for a steering motor.</li>
</ul>

<p><strong>Limitations of Affordances</strong></p>

<p>While they sound like the perfect compromise, affordances introduce their own set of "robotic" headaches.</p>

<ul>
  <li>
    <p>Information Bottlenecks: By condensing a rich, 4K camera feed into just 10 or 15 numbers (affordances), you might throw away the "reason" for an action. For example, if there is a massive pothole in the lane, a standard affordance model might not have a "distance to pothole" label, forcing the car to drive right through it because it only cares about "distance to lane center."</p>
  </li>
  <li>
    <p>Cascading Errors: If the affordance extractor makes a mistake (e.g., misidentifying a guardrail as a lane line), the motion planner will perfectly execute a "safe" path right into the rail. In direct actuation, the model might implicitly learn to avoid the visual texture of a guardrail.</p>
  </li>
  <li>
    <p>Manual Engineering: Someone has to decide which affordances matter. If you forget to include "pedestrian intent" as an affordance, your planner will be "blind" to the person about to step off the curb until they are physically in the way.</p>
  </li>
</ul>

<p><strong>Comparison Summary</strong></p>

<table>
  <thead>
    <tr>
      <th>Feature</th>
      <th>Direct Actuation (End-to-End)</th>
      <th>Affordance-Based Planning</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Logic</td>
      <td>"See pixel, move wheel"</td>
      <td>"See world, calculate distances, move wheel"</td>
    </tr>
    <tr>
      <td>Transparency</td>
      <td>Low (Black Box)</td>
      <td>High (Explainable)</td>
    </tr>
    <tr>
      <td>Generalization</td>
      <td>Specific to vehicle dynamics</td>
      <td>Generic across different vehicles</td>
    </tr>
    <tr>
      <td>Failure Mode</td>
      <td>Unpredictable "weird" behaviors</td>
      <td>Predictable but limited by labels</td>
    </tr>
  </tbody>
</table>

<h2 id="heading-simple-answer-question-4">Simple Answer Question 4</h2>

<h3 id="heading-descriptions-5">Descriptions</h3>

<p>Given the pseudocode of the traditional CV lane detection algorithm, answer the following questions:</p>

<p>(1) What is the purpose of the ROI masking step?
(2) Which line converts edge pixels into detected lane segments?</p>

<p>Algorithm 1 CV Lane Detection</p>

<p>Input: RGB Image $I \in \mathbb{R}^{H \times W \times 3}$</p>

<p>Parameters: Gaussian kernel $k$, Canny thresholds $T$, ROI vertices $V$, Hough parameters $\rho$</p>

<p>Output: Set of detected lane segments $\mathcal{L} = {(x_1, y_1, x_2, y_2)}$</p>

<p>1 $G \leftarrow \text{ConvertToGrayscale}(I)$</p>

<p>2 $B \leftarrow \text{GaussianBlur}(G, k)$</p>

<p>3 $E \leftarrow \text{CannyEdgeDetection}(B, T)$</p>

<p>4 $M_{roi} \leftarrow \text{GenerateMask}(V, shape(E))$</p>

<p>5 $E_{masked} \leftarrow E \bigodot M_{roi}$</p>

<p>6 $\mathcal{L} \leftarrow \text{HoughLineTransform}(E_{masked}, \rho)$</p>

<p>7 $\text{return } \mathcal{L}$</p>

<h3 id="heading-explanations-10">Explanations</h3>

<p>(1) Purpose of the ROI Masking Step</p>

<p>Th Region of Interest (ROI) masking step is designed to eliminate visual noise and irrelecant data from the image.</p>

<p>In a standard driving scene, the top half of the frame typically contains the sky, trees, or buildings. While these areas may contain "edges" (captured by the Canny detector in Line 3), they are not relevant to lane tracking. By applying the mask $M_{roi}$ via element-wise multiplication ($\bigodot$).</p>

<ul>
  <li>Accuracy: It prevents the Hough Transform from accidentally detecting the horizon, power lines, or guardrails as lanes.</li>
  <li>Efficiency: It restricts the search space for the line dtection algorithm to the specific portion of the road where lanes are physically expected to appear.</li>
</ul>

<p>(2) Conversion of Edge Pixels into Lane Segments</p>

<p>The line that performs this conversion is Line 6:</p>

\[\mathcal{L} \leftarrow \text{HoughLineTransform}(E_{masked}, \rho)\]

<p>Until this point in the algorithm, the data consists of a "mask" or a "map" of pixels, $E_{masked}$ is essentially a binary image where $1$ is an edge and $0$ is nothing. The computer doesn't "know" these are lines yet, it just sees a collection of scattered points.</p>

<p>The Hough Line Transform is the mathematical bridge. It uses a voting procedure in a parameter space (Hough Space) to determine which sets of edge pixels align into a coherent linear shape. It then outputs the coordinates ($\mathcal{L}$) of the start and end points of those segments, effectively turning "dots on a grid" into "vector geometry".</p>]]></content><author><name>Rain</name></author><category term="Autonomous Driving" /><category term="Review" /><category term="Lectures" /><summary type="html"><![CDATA[Autonomous Driving Review: Problems and explanations]]></summary></entry><entry><title type="html">AI Security Topic Review</title><link href="https://congjyu.github.io/blog/2026/04/21/ai-security/" rel="alternate" type="text/html" title="AI Security Topic Review" /><published>2026-04-21T00:00:00+00:00</published><updated>2026-04-21T00:00:00+00:00</updated><id>https://congjyu.github.io/blog/2026/04/21/ai-security</id><content type="html" xml:base="https://congjyu.github.io/blog/2026/04/21/ai-security/"><![CDATA[<h1 id="heading-ai-security-topic-review">AI Security Topic Review</h1>

<p>Updated 3 May 2026.</p>

<h2 id="heading-introduction-to-ai-security">Introduction to AI Security</h2>

<h3 id="heading-part-i-introduction-to-the-ai-security-and-mlai-basic-recap">Part I. Introduction to the AI Security and ML/AI Basic Recap</h3>

<p>The AI lifecycle: Data Collection, Model Training / Fine Tuning, Model Deployment, Model Use.</p>

<p>An AI security threat model contains that: Attacker's goals (AI security properties), for example, privacy, security, safety, transparency, fairness; Attacker's capability, for example, blackbox attacks and whitebox attacks; Attacker's techniques.</p>

<h4 id="heading-sample-question-1-overfitting">Sample Question 1: Overfitting</h4>

<p>A classifier $f(x; \theta)$ is trained on a dataset $D \sim P_{\text{train}}$, but deployed in an environment where inputs follow a different distribution $P_{\text{adv}}$, potentially influenced by an adversary.</p>

<p>The generalized gap is defined as:</p>

\[\mathbb{E}_{(x, y) \sim P_{\text{adv}}} [\mathcal{L}(f(x; \theta), y)] - \mathbb{E}_{(x, y) \sim P_{\text{train}}} [\mathcal{L}(f(x; \theta), y)]\]

<p>(i) Explain how overfitting increases the generalization gap under adversarial distribution shift.</p>

<p>Explanation:</p>

<p>Overfitting occurs when a model $f(x; \theta)$ learns the noise or spurious correlations sepcific to $P_{\text{train}}$ rather than the underlying signal. Under an adversarial shift $P_{\text{adv}}$, this becomes a massive liability.</p>

<ol>
  <li>Sensitivity to brittle features. An overfitted model relies on "non-robust" features such as pixels or patterns that happen to correlate with the label in the training set but are not essential.</li>
  <li>Exploitation. An adversary specifically seaches for these brittle features. Because the model has "memorized" the training manfold so tightly, even a tiny movement $\delta$ in the input space can push the input across a jagged decision boundary.</li>
  <li>The gap. While the training loss $\mathbb{E}<em>{P</em>{\text{train}}}[\mathcal{L}]$ reamins very low, the adversarial loss $\mathbb{E}_{\text{adv}}[\mathcal{L}]$ skyrockets because the adversary is effectively guiding the model into its own blind spots created by overfitting.</li>
</ol>

<p>即係，過擬合係指模型學得太過死板以致於記住咗 $P_{\text{train}}$ 之中嘅噪聲或者啱啱好嘅特徵，而並非真正嘅邏輯。對抗性分布 $P_{\text{adv}}$ 之下會令到 generalization gap 極劇升高。</p>

<blockquote>
  <p>Overfitting causes the model to learn patterns specific to $P_{\text{train}}$, including noise, rather than generalizable features. When evaluated on $P_{\text{adv}}$, these patterns do not hold, resulting in increased loss and a larger generalization gap.</p>
</blockquote>

<p>(ii) Robust optimization:</p>

\[\min_{\theta} \max_{\delta \in \Delta} \mathcal{L}(f(x + \delta; \theta), y)\]

<p>is a framework that explicitly addresses overfitting. Explain the purpose of the inner maximization and the outer minimization, respectively. Justify why this reduces overfitting.</p>

<p>Explanation:</p>

<p>The objective $\min_{\theta} \max_{\delta \in \Delta} \mathcal{L}(f(x + \delta; \theta), y)$ is a Zero-Sum Game between an attacker and a defender.</p>

<p>The Inner Maximization $\max_{\delta}$. This represents the Attacker. Its goal is to find the "worst-case" perturbation $\delta$ within a defined constraint $\Delta$ (like an $\ell_{p}$ ball) that maximizes the loss. It essentially looks for the model's weakest point near every data point.</p>

<p>The Outer Minimization $\min_{\theta}$. This represents the Learner or Defender. Its goal is to find parameters $\theta$ that minimize the loss even when the attacker does their worst.</p>

<p>Why it reduces overfitting is that the robust optimization acts as a powerful form of regularization. Instead of fitting a single point $(x, y)$, the model is forced to fit an entire "neighborhood" $(x + \Delta, y)$. This prevents the decision boudary from being too close to the data points or being too jagged, as it must remain constant over the entire perturbation set $\Delta$. It shifts the model's focus from "brittle" high-frequency features to "robust" features that remain stable even under attack.</p>

<blockquote>
  <p>The inner maximization models adversarial perturbations, while the outer minimization is the regular loss optimization. This reduces overfitting by smoother decision boundaries.</p>
</blockquote>

<h3 id="heading-part-ii-adversarial-examples-and-attacks">Part II. Adversarial Examples and Attacks</h3>

<h4 id="heading-adversarial-examples">Adversarial Examples</h4>

<p>Given a ML model with a decision function $f: X \rightarrow Y$ that maps an input sample $x \in X$ to a true class label $y_{tree} \in Y$, $x' = x + \delta$ is called an adversarial sample with an adversarial perturbation $\delta$ if $f(x') = y' \neq y_{true}$, $\vert\vert\delta\vert\vert &lt; \epsilon$, where $\vert\vert.\vert\vert$ is a distance metric and $\epsilon$ is the maximum allowable perturbation that results in misclassification while preserving semantic integrity of $x$.</p>

<p>DNN Training: $\min_{\theta} \sum_{(x_i, y_i) \in D_{train}} L(f_{\theta}(x_i), y_i)$</p>

<p>Adversarial Attack: $\max_{x'} L(f_{\theta}(x'), y)$ subject to $\vert\vert x' - x\vert\vert_p \leq \epsilon$ for $x \in D_{test}$.</p>

<p>Small perturbation: $\vert\vert x' - x\vert\vert \frac{8}{255}$</p>

<h4 id="heading-i_p-norm">$I_p$-norm</h4>

\[{\vert\vert x \vert\vert}_{1} = \sum_{i = 1}^{n} \vert\vert x_i\vert\vert\]

\[{\vert\vert x \vert\vert}_{2} = \sqrt{\sum_{i = 1}^{n} x_{i}^2}\]

\[{\vert\vert x \vert\vert}_{\infty} = \max \vert\vert x_i \vert\vert\]

<h4 id="heading-crafting-adversarial-examples-whitebox">Crafting Adversarial Examples: Whitebox</h4>

<p>The basic idea is: given an input, add non-random perturbation that induces classification mistake.</p>

<p>Attacker's knowledge: Loss</p>

<ul>
  <li>Fast Gradient Sign Method (FGSM)</li>
  <li>Projected Gradient Descent (PGD)</li>
  <li>Szegedy's L-BFGS Attack</li>
</ul>

<p>Attacker's knowledge: Logit</p>

<ul>
  <li>C&amp;W Attack</li>
</ul>

<h4 id="heading-fast-gradient-sign-method-fgsm">Fast Gradient Sign Method (FGSM)</h4>

<p>The key idea is to perturb input in the direction of the sign of the gradient of the loss w.r.t. the input, scaled by a perturbation budget.</p>

\[x' = x + \epsilon \cdot sign(\nabla_x J(f(x), y_{true}))\]

<ul>
  <li>$\nabla_x J(f(x), y_{true})$ tell us how to change the input to increase the loss</li>
  <li>$sign()$ keep only the direction ($+ 1$ or $- 1$) of each input dimension</li>
  <li>$\epsilon$ control how laarge the perturbation is.</li>
</ul>

<h4 id="heading-projected-gradient-descent-pgd">Projected Gradient Descent (PGD)</h4>

<p>FGSM but perturbation is done in multiple smaller steps.</p>

\[x_{(i)} = clip(x_{(i - 1)} + \alpha \cdot sign(\nabla_x J(f(x_{(i - 1)}), y_{true})))\]

<ul>
  <li>Multi-step version of FGSM</li>
  <li>Taking samll gradient steps that increase loss, while $clip(…)$ projects the result back into a constrained region around the original input, so perturbation is kept bounded</li>
  <li>$clip(…)$ projects the value to a value satisfying ${\vert\vert x^t - x\vert\vert}_p \leq \epsilon$, i.e., within the $l_p$ ball centerer around $x$; $\alpha$ is the step size, e.g., a pixel unit.</li>
</ul>

<h4 id="heading-optimization-based-attack-szegedys-l-bfgs-attack">Optimization-based Attack: Szegedy's L-BFGS Attack</h4>

<p>Target attack is given an input $x$ and a target lable $t$, the attack solves $\min_{\delta} {\vert\vert\delta\vert\vert}_2$ so that $f(x + \delta) = t$.</p>

<p>However the constraint is hard to enforce L-BFGS attack relaxes it to $\min_{\delta} c \cdot {\vert\vert\delta\vert\vert}_2^2 + J(f(x + \delta), t)$. Solving this optimization problem means we find an $x'$ which is close to $x$ and its loss to lable $t$ is small.</p>

<h4 id="heading-optimization-based-attack-cw-attack">Optimization-based Attack: C&amp;W Attack</h4>

<p>L-BFGS Attack minimize distance and loss, loss does not directly represent misclassification and maybe flip the label.</p>

<p>C&amp;W attack operates on logits $Z(x)$.</p>

\[\min_{\delta} c \cdot {\vert\vert\delta\vert\vert}_2^2 + (\max_{i \neq t} Z_i (x + \delta) - Z_t (x + \delta))\]

<p>(Target class $t$)</p>

\[\min_{\delta} c \cdot {\vert\vert\delta\vert\vert}_2^2 + (\max_{i \neq y} Z_i (x + \delta) - Z_y (x + \delta))\]

<p>(Untargetted attack)</p>

<p>Directly enforces a decision boudary margin.</p>

<h4 id="heading-crafting-adversarial-examples-blackbox">Crafting Adversarial Examples: Blackbox</h4>

<p>The basic idea is <strong>search</strong>.</p>

<p>Attacker's knowledge: confidence scores</p>

<ul>
  <li>SimBA: Simple Black-box Adversarial Attacks</li>
</ul>

<p>Attacker's knowledge: predicted labels</p>

<ul>
  <li>RayS: A Ray Searching Method for Hand-label Adversarial Attack</li>
</ul>

<h3 id="heading-part-iii-adversarial-defenses">Part III. Adversarial Defenses</h3>

<p>Main defense mechanisms against adversarial examples are listed below:</p>

<ul>
  <li>Input preprocessing &amp; transformation</li>
  <li>Gradient masking</li>
  <li>Advesarial Example Detection (AED)</li>
  <li>Robustness Training (Adversarial Training)</li>
  <li>Robustness Verification</li>
</ul>

<h4 id="heading-gradient-masking">Gradient Masking</h4>

<p>Insight: Most attacks are based on the model's gradient information. Therefore, gradient masking methods hide the gradient of the model from being used by an adversary. Shattered Gradients Methods and Stochastic/Randomized Gradients Methods are the approaches.</p>

<h4 id="heading-shattered-gradients">Shattered Gradients</h4>

<p>Goal: To prevent the flow of information from the inputs to the outputs in the model, by preventing the attacker from calculating the gradients.</p>

<p>How: Applying a non-smooth or non-differentiable preprocessor $g(.)$ to the inputs, and then training a DNN model $f$ on the preprocessed inputs $g(x)$.</p>

<p>Why: The trained target classifier $f(g(x))$ is not differentiable w.r.t. the inputs $x$, causing the failure of adversarial attacks.</p>

<h5 id="heading-shattered-gradients-thermometer-encoding">Shattered Gradients: Thermometer Encoding</h5>

<p>Thermometer Encoding's idea is to convert the continuous space (of input) to discrete one. It applies discretization of the intensity level of each pixel into an $l$-dimensional vector.</p>

<p>The target classifier is trained using discrete vectors for all pixels, which breaks the calculation of the gradients.</p>

<p>Why does it not affect training (gradient descent)?</p>

<ul>
  <li>Gradients needed by training $\frac{\partial{L}}{\partial{W}}$</li>
  <li>Gradients needed by adversarial attacks $\frac{\partial{L}}{\partial{x}}$</li>
</ul>

<h4 id="heading-stochasticrandomized-gradients-methods">Stochastic/Randomized Gradients Methods</h4>

<p>Goal: To apply some form of randomization of the DNN model as a defense strategy to fool the advesary.</p>

<p>How: Add randomness, e.g., training a set of classifiers, and during the testing phase randomly selecting one classifier to predict the class labels.</p>

<p>Why: Because the adversary does not know which model was used for prediction, the attack success rate is reduced.</p>

<ul>
  <li>In a standard deterministic model, the gradient is $\nabla J(f(x), y)$.</li>
  <li>By adding randomness, the prediction depends on a random variable $r: f(x, r)$.</li>
  <li>The gradient becomes $\nabla J(f(x, r), y)$, which change every time the same input $x$ is computed.</li>
</ul>

<h3 id="heading-part-iv-adversarial-training">Part IV. Adversarial Training</h3>

<p>Adversarial training trains the model on adversarial examples so that it learns to classify them correctly.</p>

<ul>
  <li>Explicitly reshapes the decision boundary making it more robust to worst-case perturbations.</li>
  <li>Adversarial training approach</li>
  <li>
    <ul>
      <li>Recall that in standard training, a model minimizes the loss $\min_W \mathcal{L}(f_W(x + \delta), y)$.</li>
    </ul>
  </li>
  <li>
    <ul>
      <li>
        <ul>
          <li>Inner maximization finds the worst-case adversarial perturbation.</li>
        </ul>
      </li>
    </ul>
  </li>
  <li>
    <ul>
      <li>
        <ul>
          <li>outer minimization updats model parameters to resist that pertubation.</li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

<h4 id="heading-the-min-max-optimization-in-adversarial-training">The min-max Optimization in Adversarial Training</h4>

<p>Approximated by alternating optimization, where the inner maximization (attack) and the outer minimization (training) are interleaved at every minibatch.</p>

<ul>
  <li>The adversarial examples are produced to attck the latest iterate of the model.</li>
  <li>By adding adversarial example $x'$ with true label $y$ to the training set, the model eill learn that $x'$ belongs to the class $y$.</li>
  <li>Similar to the training of GAN.</li>
</ul>

<h4 id="heading-sample-question-2-adversarial-examples">Sample Question 2: Adversarial Examples</h4>

<p>(i) Assume each image in a dataset is a $8 \times 8$ pixel grayscale image. Each pixel in an image is denoted by a single integer between 0 and 31. Given an image, we construct its advesarial example within $l_2$ ball. Assume the budget $\epsilon = 2$, what is the maximum number of images within the $l_2$ balls? Show your working. Set up the calculation; you do not need to calculate the final value.</p>

<p>Solution:</p>

<ol>
  <li>Problem Definition</li>
</ol>

<p>An image can be represented as a vector $(X = (x_1, x_2, …, x_{64}))$ in a 64-dimensional space,where each pixel $x_i$, is an integer such that $0 \neq x_i \neq 31$.</p>

<p>An adversarial example $X'$ within the $l_2$ ball of radius $\epsilon$ satificies:</p>

\[{\vert\vert X' - X \vert\vert}_2 \neq \epsilon\]

<p>Substituting the given values ($\epsilon = 2$ and 64 pixels):</p>

\[\sqrt{\sum_{i = 1}^{64} (x_i' - x_i)^2} \leq 2\]

<ol>
  <li>Constraints and Maximization</li>
</ol>

<p>To maximize the number of valid images $X'$, we must choose a base image $X$ such that the perturbation $\delta_i$ are not restricted by the image boundaries ($0$ and $31$). If a pixel $x_i$ is $0$, then $\delta_i$ cannot be negative; if $x_i$ is $31$, $\delta_i$ cannot be positive.</p>

<p>The maximum number of images is achieved when $X$ is in the "middle" of the range (e.g., $2 \le x_i \le 29$ for all $i$), allowing $\delta_i$ to take any value in ${ -2, -1, 0, 1, 2 }$ as long as the sum of squares is $\le 4$.</p>

<ol>
  <li>Enumerating Integer Solutions</li>
</ol>

<p>We need to find all integer vectors $(\delta_1, \dots, \delta_{64})$ such that $\sum \delta_i^2 \le 4$. The possible values for $\delta_i^2$ are $0, 1^2=1,$ and $2^2=4$. Any $\delta_i^2 &gt; 4$ is impossible.</p>

<p>We categorize the solutions based on the value of the sum $S = \sum \delta_i^2$:</p>

<p>Case 1: $S = 0$All $\delta_i = 0$.</p>

<ul>
  <li>Number of ways: $1$ (The original image itself)</li>
</ul>

<p>Case 2: $S = 1$</p>

<ul>
  <li>One pixel changes by $\pm 1$, all others are $0$.</li>
  <li>Ways to choose the pixel: $\binom{64}{1}$</li>
  <li>Values for $\delta_i$: $\pm 1$ ($2$ choices)</li>
  <li>Calculation: $\binom{64}{1} \times 2$</li>
</ul>

<p>Case 3: $S = 2$</p>

<ul>
  <li>Two pixels change by $\pm 1$ each, or one pixel changes by $\pm \sqrt{2}$ (impossible for integers).</li>
  <li>Ways to choose 2 pixels: $\binom{64}{2}$</li>
  <li>Values for each: $\pm 1$ ($2^2 = 4$ combinations)</li>
  <li>Calculation: $\binom{64}{2} \times 2^2$</li>
</ul>

<p>Case 4: $S = 3$</p>

<ul>
  <li>Three pixels change by $\pm 1$ each.</li>
  <li>Ways to choose 3 pixels: $\binom{64}{3}$</li>
  <li>Values for each: $\pm 1$ ($2^3 = 8$ combinations)</li>
  <li>Calculation: $\binom{64}{3} \times 2^3$</li>
</ul>

<p>Case 5: $S = 4$</p>

<ul>
  <li>This can happen in two ways:</li>
  <li>Subcase 5a: Four pixels change by $\pm 1$ each</li>
  <li>Calculation: $\binom{64}{4} \times 2^4$</li>
  <li>Subcase 5b: One pixel changes by $\pm 2$.</li>
  <li>Ways to choose the pixel: $\binom{64}{1}$</li>
  <li>Values for $\delta_i$: $\pm 2$ ($2$ choices)</li>
  <li>Calculation: $\binom{64}{1} \times 2$</li>
</ul>

<ol>
  <li>Final Calculation Setup</li>
</ol>

<p>Summing all the cases above, the maximum number of images $N$ is:</p>

\[N = 1 + \left[ \binom{64}{1} \cdot 2 \right] + \left[ \binom{64}{2} \cdot 2^2 \right] + \left[ \binom{64}{3} \cdot 2^3 \right] + \left[ \binom{64}{4} \cdot 2^4 \right] + \left[ \binom{64}{1} \cdot 2 \right]\]

<p>Combining the $\binom{64}{1}$ terms, the simplified setup is:</p>

\[N = 1 + 4\binom{64}{1} + 4\binom{64}{2} + 8\binom{64}{3} + 16\binom{64}{4}\]

<p>(ii) A classifier $f(x)$ is trained using a modified loss function:</p>

\[\mathcal{L}'(x, y) = \mathcal{L}(f(x), y) + \lambda {\vert\vert x \vert\vert_x}^2\]

<p>where $\lambda &gt; 0$ is a regularization parameter applied directly to the input. An adversary constructs adversarial examples using the Fast Gradient Sign Method (FGSM):</p>

\[x_{adv} = x + \epsilon \cdot sign(\nabla_x \mathcal{L}'(x, y))\]

<p>Derive an expression for $\nabla_x \mathcal{L}' (x, y)$. Explain how the additional term $\lambda {\vert\vert x \vert\vert}_2^2$ changes the direction of the FGSM perturbation. Does this modification weaken FGSM attacks.</p>

<p>Solution:</p>

<ol>
  <li>Derivation of the Gradient</li>
</ol>

<p>We start with the modified loss function:</p>

\[\mathcal{L}'(x, y) = \mathcal{L}(f(x), y) + \lambda {\vert\vert x \vert\vert}_2^2\]

<p>To find the gradient with respect to the input $x$, we apply the linearity of the gradient operator:</p>

\[\nabla_x \mathcal{L}'(x, y) = \nabla_x \mathcal{L}(f(x), y) + \nabla_x (\lambda {\vert\vert x \vert\vert}_2^2)\]

<p>Recall that for the squared $L_2$ norm, ${\vert\vert x \vert\vert}_2^2 = \sum x_i^2$. The derivative of each component is $2x_i$, which gives us $\nabla_x {\vert\vert x \vert\vert}_2^2 = 2x$.</p>

<p>Thus, the final expression for the gradient is:</p>

\[\nabla_x \mathcal{L}'(x, y) = \nabla_x \mathcal{L}(f(x), y) + 2\lambda x\]

<ol>
  <li>Impact on the FGSM Perturbation</li>
</ol>

<p>The standard FGSM attack moves the input in the direction of the sign of the loss gradient to maximize error. With the modification, the perturbation vector becomes:</p>

\[x_{adv} = x + \epsilon \cdot sign\left( \underbrace{\nabla_x \mathcal{L}(f(x), y)}_{\text{Classification Gradient}} + \underbrace{2\lambda x}_{\text{Input Penalty}} \right)\]

<p>The additional term $2\lambda x$ acts as a <strong>centripetal force</strong> pulling the gradient toward the direction of the input vector itself. Here is how it changes the direction:</p>

<ul>
  <li><strong>Bias toward the Origin:</strong> Since $2\lambda x$ points directly away from the origin, the total gradient is now biased. The adversary is no longer purely maximizing the classification error; they are also being "tricked" into moving the sample further away from the origin.</li>
  <li><strong>Sign Interference:</strong> The $sign$ function is sensitive. If a specific component of the classification gradient is small, the $2\lambda x$ term can flip the sign of that component, forcing the FGSM attack to move in a direction that might actually <em>increase</em> the stability of the input relative to its original magnitude.</li>
</ul>

<ol>
  <li>Does this weaken FGSM attacks?</li>
</ol>

<p>Yes, theoretically, but with significant caveats.</p>

<ul>
  <li><strong>Gradient Masking/Confusion:</strong> By adding a term that depends only on the input value $x$ and not the relationship between $x$ and the label $y$, you are effectively "polluting" the adversarial direction. The perturbation is no longer optimal for changing the model's prediction.</li>
  <li><strong>The "Weight Decay" Analogy:</strong> This is essentially <strong>Weight Decay applied to inputs</strong>. It encourages the model to be less sensitive to large input values, potentially smoothening the decision boundary.</li>
</ul>

<p><strong>However, it is generally considered a weak defense for two reasons:</strong></p>

<ul>
  <li><strong>Gradient Transparency:</strong> An intelligent adversary knows the loss function. They can simply subtract $2\lambda x$ from their calculation to recover the "true" adversarial direction $\nabla_x \mathcal{L}(f(x), y)$. This is a form of <strong>Obfuscated Gradients</strong>, which usually fails against a motivated attacker.</li>
  <li><strong>Semantic Irrelevance:</strong> The term $2\lambda x$ doesn't actually make the model's features more robust; it just changes the geometry of the loss surface in a way that is easily bypassed by adjusting the attack algorithm.</li>
</ul>

<h2 id="heading-robustness-verification">Robustness Verification</h2>

<h3 id="heading-verification-methods">Verification Methods</h3>

<p>Interval Arithmetic</p>

<ul>
  <li>Affine Transformaton</li>
  <li>ReLU Transformation</li>
</ul>

<p>What is Affine Transformation?</p>

<p>What is ReLU Transformation?</p>

<p>ReluVal</p>

<ul>
  <li>Symbolic Representation</li>
  <li>Concretization</li>
  <li>Relaxation for Symbolic Bounds</li>
</ul>

<p>What are these concepts?</p>

<h4 id="heading-sample-question-3-nn-verification">Sample Question 3: NN Verification</h4>

<p>Consider a ReLU-activated neural network $f(x)$:</p>

\[f(x) = W_2 \cdot \text{ReLU}(W_1 \cdot x + b_1) + b_2\]

<p>where $W_1 \in \mathbb{R}^{3 \times 3}$, $b_1 \in \mathbb{R}^3$, $W_2 \in \mathbb{R}^{1 \times 3}$, $b_2 \in \mathbb{R}$.</p>

<p>Assume the first layer of $f(x)$ as</p>

\[W_1 =
\begin{bmatrix}
  2 &amp; 1 &amp; 0 \\
  1 &amp; -2 &amp; 2 \\
  2 &amp; -1 &amp; 2
\end{bmatrix}
\text{, }
b_1 =
\begin{bmatrix}
  0, -0.5, -0.3
\end{bmatrix}^{T}\]

<p>Given an input $x \in \mathbb{R}^3$ with $0.3 \leq x_1 \leq 0.5$, and $0.3 \leq x_3 \leq 0.7$, find the bounds of the output of $z = W_1 \cdot x + b_1$ (linear layer only) using interval arithmetic.</p>

<p>Solution:</p>

<p>We have</p>

\[z = W_1 x + b_1\]

<p>where</p>

\[W_1 = \begin{bmatrix} 2 &amp; 1 &amp; 0 \\ 1 &amp; -2 &amp; 2 \\ 2 &amp; -1 &amp; 2 \end{bmatrix},
\quad
b_1 = \begin{bmatrix} 0 \ -0.5 \ -0.3 \end{bmatrix}.\]

<p>Input bounds are given only for $x_1$ and $x_3$:</p>

<p>$x_1 \in [0.3, 0.5]$</p>

<p>$x_3 \in [0.3, 0.7]$</p>

<p>No bounds are given for $x_2$. Since only partial bounds are provided and no further assumptions are stated, we must assume $x_2$ is unbounded (can be any real number).</p>

<p>Let us express each component of $z$:</p>

<p>First component:</p>

\[z_1 = 2x_1 + 1x_2 + 0x_3 + 0 = 2x_1 + x_2.\]

<p>Since $x_1$ is bounded but $x_2$ is unbounded,</p>

\[z_1 \in (-\infty, \infty).\]

<p>Second component:</p>

\[z_2 = 1x_1 - 2x_2 + 2x_3 - 0.5 = x_1 - 2x_2 + 2x_3 - 0.5.\]

<p>Again, because $x_2$ is unbounded, $z_2$ is also unbounded:</p>

\[z_2 \in (-\infty, \infty).\]

<p>Third component:</p>

\[z_3 = 2x_1 - 1x_2 + 2x_3 - 0.3 = 2x_1 - x_2 + 2x_3 - 0.3.\]

<p>Same situation: the presence of $x_2$ with coefficient $-1$ makes this unbounded.</p>

<p>Thus, without bounds on $x_2$, interval arithmetic gives:</p>

\[\boxed{(-\infty, \infty)}\]

<p>for each component of $z$.</p>

<p>If we assume $x_2$ is also bounded but not specified, then one cannot compute finite intervals.</p>

<h2 id="heading-poisoning-attacks-and-backdoor-attacks">Poisoning Attacks and Backdoor Attacks</h2>

<h3 id="heading-threat-model-of-data-poisoning-attacks">Threat Model of Data Poisoning Attacks</h3>

<p>Attacker's knowledge:</p>

<ul>
  <li>Training data, Test data, Model architecture</li>
</ul>

<p>Attacker's capabilities:</p>

<ul>
  <li>Inject new samples into training data</li>
  <li>Delete samples</li>
  <li>Modify existing samples (features or labels)</li>
</ul>

<p>Attacker's goals:</p>

<ul>
  <li>Availability Attacks (Indiscriminated or Untargeted)</li>
  <li>Integrity Attacks (Targeted)</li>
</ul>

<h4 id="heading-attack-methods">Attack Methods</h4>

<p>Label poisoning.</p>

<ul>
  <li>The attacker controls a fraction of the training data and modify their labels.</li>
  <li>Dirty-Label: mismatched content/label is easily detectable.</li>
</ul>

<p>Feature poisoning.</p>

<ul>
  <li>Mechanism: feature collision</li>
</ul>

\[x_p = \arg \min {\vert\vert f(x_p) - f(x_t) \vert\vert}_2^2 + \beta {\vert\vert x_p - x_b \vert\vert}_2^2\]

<ul>
  <li>
    <ul>
      <li>$x_p$ is poisoning sample</li>
    </ul>
  </li>
  <li>
    <ul>
      <li>$x_t$ is target sample</li>
    </ul>
  </li>
  <li>
    <ul>
      <li>$x_b$ is base sample</li>
    </ul>
  </li>
  <li>Clean-Label</li>
</ul>

<p>Gradient-based poisoning.</p>

<ul>
  <li>Estimate how the optimal parameters $\mathbf{W}$ change when training data changes.</li>
  <li>Update poison data $x_p$ in the direction that maximizes test error.</li>
</ul>

<h4 id="heading-poisoning-as-a-min-max-optimization-problem">Poisoning as a min-max Optimization Problem</h4>

<p><strong>Inner Problem</strong>: Learner's loss (Minimization)</p>

<ul>
  <li>$\mathbf{W}^{*} = \arg \min_W \mathcal{L}(\mathbf{W}, \mathcal{D}_c \cup \mathcal{D}_p)$ (note: we use an $\mathcal{L}$ slightly different from previous lectures for simplicity, which stands for summation of loss of all samples.)</li>
  <li>This is the standard training loss.</li>
</ul>

<p><strong>Outer Problem</strong>: Attacker's objective (Maximization)</p>

<ul>
  <li>$\max_{\mathcal{D}<em>p} \mathcal{L}</em>{attack}(\mathbf{W}^{*})$</li>
  <li>This is to maximize the harm: maximize test error (untargeted attack) or force misclassification of specific inputs (targeted attack).</li>
</ul>

<h4 id="heading-backdoor-attack">Backdoor Attack</h4>

<p><strong>Definition</strong>: The attacker injects a hidden behavior into the model.</p>

<p><strong>behavior</strong>:</p>

<ul>
  <li>Input $x$ (Clean) $\rightarrow$ Output $y$ (Correct).</li>
  <li>Input $x + \text{Trigger}$ $\rightarrow$ Output $y_t$ (Attacker Chosen).</li>
</ul>

<p><strong>Key Components</strong>:</p>

<ul>
  <li>Trigger: A pattern (pixel patch, object, sound) added to the input.</li>
  <li>Payload: The incorrect prediction (target label).</li>
</ul>

<h3 id="heading-attack-methods-1">Attack Methods</h3>

<h4 id="heading-defense-taxonomy">Defense Taxonomy</h4>

<p>By Defender's Goal</p>

<ul>
  <li>Backdoor Removal/Mitigation: climinate the backdoor effect</li>
  <li>Backdoor Detection: deteermin if a model is poisoned or screen specific inputs</li>
</ul>

<p>By DNN Lifecycle</p>

<ul>
  <li>Pre-training: Data sanitization</li>
  <li>In-training: Robust training</li>
  <li>Post-trainig: Model inspection and input filtering after deployment</li>
</ul>

<h4 id="heading-typical-techniques">Typical Techniques</h4>

<p>In-training</p>

<ul>
  <li>Model orthogonalization (removal/mitigation)</li>
</ul>

<p>Post-training</p>

<ul>
  <li>Fine-pruning (removal/mitigation)</li>
  <li>Neural cleanse (detection) (reverse engineering)</li>
  <li>Meta Neural Trojan Detection (detection)</li>
  <li>STRIP (detection)</li>
  <li>Topological Evolution Dynamics (detection)</li>
</ul>

<h4 id="heading-sample-question-5-data-poisoning">Sample Question 5: Data Poisoning</h4>

<p>Consider a supervised learning model $f(x; \theta)$ trained using loss function $\mathcal{L}$. An attacker aims to perform a <em>untargeted data poisoning attack</em> by injecting a small set of poisoned samples $D_{poison} = {(x_p, y_p)}$ into the training dataset.</p>

<p>The attacker's goal is to cause a specific target input $x_t$ to be misclassified to any label $y_{adv} \neq y_t$.</p>

<p>Answer the following questions:</p>

<p>(i) Formulate the poisoning attack as a bilevel optimization problem. Clearly define the outer and inner problems.</p>

<p>(ii) Revise the optimization problem in (i) to ensure stealthiness by llimiting the deviation of poisoned samples from clean data.</p>

<p>Solutions:</p>

<p>(i) Bilevel optimization formulation for untargeted data poisoning</p>

<p>The attacker wants to craft a small set of poisoned samples $D_{\text{poison}} = {(x_p, y_p)}$ such that, after training on the combined dataset $D_{\text{clean}} \cup D_{\text{poison}}$, the resulting model misclassifies a specific target input $x_t$ to any label $y_{\text{adv}} \neq y_t$.</p>

<p>Inner problem (training): The defender trains the model by minimizing the loss on the poisoned training set:</p>

\[\theta^*(D_{\text{poison}}) = \arg\min_{\theta} \sum_{(x,y) \in D_{\text{clean}} \cup D_{\text{poison}}} \mathcal{L}(f(x; \theta), y)\]

<p>Outer problem (attacker): The attacker chooses $D_{\text{poison}}$ to maximize the loss on the target input $x_t$ with incorrect label $y_{\text{adv}}$, or equivalently to cause misclassification:</p>

\[\max_{D_{\text{poison}}} \mathcal{L}(f(x_t; \theta^*(D_{\text{poison}})), y_{\text{adv}})\]

<table>
  <tbody>
    <tr>
      <td>subject to constraints such as $</td>
      <td>D_{\text{poison}}</td>
      <td>\leq \epsilon$ (small fraction of total data).</td>
    </tr>
  </tbody>
</table>

<p>Thus the full bilevel problem is:</p>

\[\begin{aligned}
&amp;\max_{D_{\text{poison}}} \mathcal{L}(f(x_t; \theta^{*}), y_{\text{adv}}) \\
&amp;\text{s.t. } \theta^{*} = \arg\min_{\theta} \sum_{(x,y) \in D_{\text{clean}} \cup D_{\text{poison}}} \mathcal{L}(f(x; \theta), y), \\
&amp;\quad |D_{\text{poison}}| \leq \epsilon, \quad y_{\text{adv}} \neq y_t
\end{aligned}\]

<p>(ii) Revised formulation with stealthiness constraint</p>

<p>To ensure stealthiness, we want the poisoned samples to look similar to clean data so they are hard to detect by data inspection or outlier detection. This is typically enforced by limiting the deviation of each poisoned sample $(x_p, y_p)$ from some clean base sample or from the clean data distribution.</p>

<p>One common approach constrains the $\ell_p$-norm difference between the poisoned features and their clean counterparts:</p>

\[\begin{aligned}
&amp;\max_{D_{\text{poison}}} \mathcal{L}(f(x_t; \theta^{*}), y_{\text{adv}}) \\
&amp;\text{s.t. } \theta^{*} = \arg\min_{\theta} \sum_{(x,y) \in D_{\text{clean}} \cup D_{\text{poison}}} \mathcal{L}(f(x; \theta), y), \\
&amp;\quad |D_{\text{poison}}| \leq \epsilon, \quad y_{\text{adv}} \neq y_t, \\
&amp;\quad {\vert x_p - x_{\text{base}}\vert}_p \leq \delta \quad \text{for each poisoned sample}, \\
&amp;\quad y_p \text{ is plausible (e.g., correctly labeled for the base image)}.
\end{aligned}\]

<p>The constraint ${\vert x_p - x_{\text{base}}\vert}_p \leq \delta$ ensures that poisoned samples are close to some clean data point $x{\text{base}}$ (e.g., the original clean version of that sample), making them visually or statistically similar to legitimate training data. The bound $\delta$ controls the stealthiness level—smaller $\delta$ means more stealthy but possibly less effective attack.</p>

<h2 id="heading-privacy-attacks-and-defencse">Privacy Attacks and Defencse</h2>

<h3 id="heading-privacy-attacks">Privacy Attacks</h3>

<p>Membership inference:</p>

<p>Attribute inference:</p>

<p>Model inversion:</p>

<p>Training example extraction:</p>]]></content><author><name>Rain</name></author><category term="AI Security" /><category term="Review" /><category term="Lectures" /><summary type="html"><![CDATA[AI Security Topic Review]]></summary></entry><entry><title type="html">The &quot;Night-Up&quot; Level MySQL Documentation for Database Deploy, Upgrade, Backup and Restore</title><link href="https://congjyu.github.io/blog/2026/02/24/MySQL/" rel="alternate" type="text/html" title="The &quot;Night-Up&quot; Level MySQL Documentation for Database Deploy, Upgrade, Backup and Restore" /><published>2026-02-24T00:00:00+00:00</published><updated>2026-02-24T00:00:00+00:00</updated><id>https://congjyu.github.io/blog/2026/02/24/MySQL</id><content type="html" xml:base="https://congjyu.github.io/blog/2026/02/24/MySQL/"><![CDATA[<h1 id="heading-the-night-up-level-mysql-documentation-for-database-deploy-upgrade-backup-and-restore--起夜級-mysql-數據庫部署升級備份及恢復文檔">The "Night-Up" Level MySQL Documentation for Database Deploy, Upgrade, Backup and Restore | 「起夜」級 MySQL 數據庫部署、升級、備份及恢復文檔</h1>

<blockquote>
  <p>⚠️ WARNING: THE COMMANDS/SCRIPTS IN THIS PASSAGE ARE ALMOST RUN UNDER <code class="language-plaintext highlighter-rouge">root</code> PRIVILEDGES. PLEASE BE CAREFUL AND MAKE SURE WHAT YOU ARE DOING BEFORE RUNNING THOSE COMMANDS/SCRIPTS IN REAL ENVIRONMENTS.</p>

  <p>⚠️ 警告：本文所包含之命令/腳本幾乎全部都使用 <code class="language-plaintext highlighter-rouge">root</code> 權限執行。請務必小心並且確保你知道自己喺度做緊乜嘢之後再於真實環境之中執行命令。</p>
</blockquote>

<blockquote>
  <p>ℹ Tips: The terms "master" and "slave" used in this article are only used to indicate the architectural relationship between database servers and do not refer to any group or have any derogatory connotation.</p>

  <p>ℹ 提示：本文中所出現之「主」「從」等字眼只係用於表示數據庫服務器之架構關係，並無指代任何群體且無任何貶意。</p>
</blockquote>

<h2 id="heading-1-self-introduction-of-documentation--文檔自述">1. Self Introduction of Documentation | 文檔自述</h2>

<p>This documentation is for the installation, deployment, upgrading, main/duplicated database, backup and restoration.</p>

<p>本文檔適用於 MySQL 喺 Linux 服務器環境之下嘅安裝部署、系統升級、主備數據庫搭建以及備份恢復之操作。</p>

<p>Under the Linux system environment, there are mainly 3 ways to install MySQL: install via <code class="language-plaintext highlighter-rouge">rpm</code>, install via binary packages and build from source code. Installing via <code class="language-plaintext highlighter-rouge">rpm</code> is simple, but it is not flexible enough, and one machine can only install one copy of MySQL database. The way building from source code is complex and may take a long time, but it can be configured by hand, so we do not take this way in general. This documentation recommends the second method to complete the installation.</p>

<p>喺 Linux 系統環境下， MySQL 嘅安裝主要有三種方式：通過 <code class="language-plaintext highlighter-rouge">rpm</code> 安裝、使用二進制軟件包安裝以及使用源碼編譯安裝。採用 <code class="language-plaintext highlighter-rouge">rpm</code> 包安裝嘅方式操作簡單，但其缺點係唔夠靈活，而且一台機器只可以安裝一個 MySQL 數據庫；採用二進制軟件包安裝嘅方式操作較為繁瑣且耗時較長，但可以根據實際情況手動優化編譯選項，一般情況下唔需要使用呢種方法進行安裝部署。本文檔描述採用 MySQL 二進制軟件包進行安裝部署之操作步驟。</p>

<p>For version upgrade operations in MySQL, this document stipulates that the number before the first decimal point from left to right in the post-MySQL version number is the large version number, and the number after the decimal point is the small version number. For example, in MySQL 8.0.36, 8 is the major version and 0.36 is the minor version; Small version upgrades adopt direct upgrade scheme, large version upgrade adopts logical data migration upgrade scheme. It should be noted that in the event of a MySQL version upgrade involving changes to configuration items in the MySQL configuration file and scrapping, the operation should be treated as a major version upgrade regardless of the version number size.</p>

<p>對於 MySQL 嘅版本升級操作，本文檔規定 MySQL 後版本數字中由左至右第一個小數點前嘅數字係大版本，小數點後嘅數字係小版本。例如 MySQL 8.0.36 中 8 係大版本，0.36 係小版本； MySQL 8.1 中 8 係大版本，1 係小版本。小版本升級採用直接升級方案，大版本升級採用邏輯數據遷移升級方案。需要注意嘅係，若涉及到 MySQL 配置文件中嘅配置項嘅更改及廢棄嘅 MySQL 版本升級，無論版本數字大小，都應該當做大版本升級嚟進行操作。</p>

<p>The MySQL main and duplicated database construction described in this document contains two architectures, a main-duplicated architecture (data is synchronized from the main to the duplicate) and a two-host architecture (two mains synchronize data with each other and are each other's duplicates). Building a main-duplicated library generally adopts a main-duplicated architecture, in which there must be a main node, the main library, and one or more duplicated nodes, the duplicated library. All data is written to the main node first, and then other nodes copy the incremental data on the main node, thus guaranteeing the consistency of the data. Building a main-duplicated database using a main-duplicated replication scheme will improve the availability and performance of the database. In a one-main-duplicated architecture, all data from the duplicated node (duplicated library) comes from the main node, and in general, the use of read-write separation model on this architecture is able to adequately resource, sending the client's read data request to the duplicated, the client's write request to the host, and then the duplicated synchronizes the newly written data from the main. In this case the throughput of the database can be greatly improved since the read-write requests are distributed to the duplicated and main processing respectively.</p>

<p>本文檔描述之 MySQL 主備數據庫搭建包含兩種架構，分別係一個主一從架構（數據從主機同步到從機）同兩個主機架構（兩台主機互相同步數據，互相係對方嘅從機）。搭建主備庫一般採用主從架構，其中必須有一個主節點，即主庫，以及一個或者多個嘅從節點，即備庫。所有數據都會先寫入到主節點，然後其他節點會複製主節點上嘅增量數據，從而保證數據嘅一致性。使用主從複製方案搭建主備庫將提升數據庫嘅可用性及性能。喺一主一從嘅架構中，從節點（備庫）嘅所有數據都係來自主節點，一般而言，喺呢個架構上使用讀寫分離嘅模式能夠充分資源，將客戶端嘅讀數據請求發送畀備機，將客戶端嘅寫數據請求發送畀主機，然後備機從主機處同步新寫入嘅數據。呢種情況下由於讀寫請求分別分攤到咗備機同主機上處理，所以可以極大提升數據庫嘅吞吐量。</p>

<p>Database backup derives and stores the data and structure of the database in an external location so that the database can be recovered in the event of data loss or corruption. There are two types of MySQL database backups described in this document: physical backup (raw backup) and logical backup. Among them, physical backups (logical backups) directly copy database files, including but not limited to data table files and log files, and logical backups backup SQL statement files, deriving all SQL statements that record data and structure operations.</p>

<p>數據庫備份將數據庫嘅數據同結構導出並儲存喺外部位置，令數據庫喺數據丟失或者損壞之時可以恢復。本文件描述嘅 MySQL 數據庫備份方式有物理備份（原始備份）同邏輯備份兩種。其中，物理備份（邏輯備份）直接複製數據庫文件，包括但唔限於數據表文件同日誌文件；邏輯備份則備份 SQL 語句文件，將記錄數據同結構操作嘅所有 SQL 語句導出。</p>

<p>Finally, this document contains some of the problems encountered during the testing process during MySQL installation, deployment, upgrade, backup, etc., and provides a problem analysis and solution for reference when actually deploying MySQL.</p>

<p>最後，本文件收錄咗一啲喺測試過程中喺 MySQL 安裝、部署、升級、備份等過程中遇到嘅問題，並提供咗問題分析及解決方法，以供實際部署 MySQL 時參考。</p>

<h2 id="heading-2-command-line-prompt-examples--命令提示示例">2. Command Line Prompt Examples | 命令提示示例</h2>

<p>The text in this document using isowidth font with dark underprint is the command line raw text in the terminal. where the command that needs to be entered is preceded by a one-character permission prompt. The <code class="language-plaintext highlighter-rouge">#</code> symbol indicates that the next command was entered under the test machine's root privileges, the <code class="language-plaintext highlighter-rouge">$</code> symbol indicates that the next command was entered under the test machine's ordinary user privileges, and the <code class="language-plaintext highlighter-rouge">&gt;</code> symbol indicates that the next command requires a login to the MySQL command line console for operation. The permission prompt is automatically displayed by the computer terminal when the command is actually entered, eliminating the need for the user to manually enter this permission prompt as well as the space immediately following the symbol.</p>

<p>本文中使用等寬字體並帶有深色底紋嘅文本係終端中嘅命令行原始文本。其中需要輸入嘅命令前帶有一個字符嘅權限提示符。<code class="language-plaintext highlighter-rouge">#</code> 符號表示接下來嘅一條命令喺測試機嘅 root 權限下輸入；<code class="language-plaintext highlighter-rouge">$</code> 符號表示接下來嘅一條命令喺測試機嘅普通用戶權限下輸入；<code class="language-plaintext highlighter-rouge">&gt;</code> 符號表示接下來嘅命令需要登入 MySQL 命令行控制台進行操作。實際輸入命令時權限提示符號由電腦終端自動顯示，唔需要用戶手動輸入呢個權限提示符號以及符號後緊跟嘅空格。</p>

<h2 id="heading-3-installation-and-deployment--安裝部署">3. Installation and Deployment | 安裝部署</h2>

<h3 id="heading-31-check-the-current-environment-of-the-machine--檢查機器當前嘅環境">3.1. Check the current environment of the machine | 檢查機器當前嘅環境</h3>

<p>Check the current system information about the computer, and make sure they are matched with the MySQL for installation. Use <code class="language-plaintext highlighter-rouge">uname</code> tool can check the current system and architecture information easily.</p>

<p>檢查當前機器嘅系統信息，確保呢啲信息同需要安裝嘅 MySQL 數據庫軟件相匹配。使用 <code class="language-plaintext highlighter-rouge">uname</code> 工具可以方便地查看當前嘅系統和架構信息。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># uname -a
Linux clz-db-t2 3.10.0-693.el7.x86_64 #1 SMP Thu Jul 6 19:56:57 EDT 2017 x86_64 x86_64 x86_64 GNU/Linux
</code></pre></div></div>

<p>Use <code class="language-plaintext highlighter-rouge">ldd</code> tool to check the version of system's GNU libc (Glibc).</p>

<p>使用 <code class="language-plaintext highlighter-rouge">ldd</code> 工具來檢查系統嘅 GNU libc (Glibc) 版本。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># ldd --version
ldd (GNU libc) 2.17
Copyright (C) 2012 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Written by Roland McGrath and Ulrich Drepper.
</code></pre></div></div>

<p>The compatibility of the current machine with the required MySQL version is determined based on the system version, architecture, and glibc version, thus deciding which MySQL version to use. For example, the test machine in this case runs Red Hat Enterprise Linux 7, an x86_64 architecture, and uses GNU libc 2.17. Therefore, when selecting the MySQL binary package, a version compatible with el7, an x86_64 architecture, and based on glibc 2.17 should be chosen.</p>

<p>兼容性要求當前呢台機器需要對應嘅 glibc 版本，因此亦都會決定邊啲 MySQL 版本可以使用。例如，依家情況下有一台測試機運行著 Red Hat Enterprise Linux，並且使用 GNU libc 2.17。因此，當選擇 MySQL 二進制包版本嘅時候就應該選擇一個兼容 el7，x86_64 架構，並且基於 glibc 2.17 嘅 MySQL 版本。</p>

<p>If the system architecture is displayed as aarch64, then an aarch64 or arm64 package should be selected; if the system architecture is displayed as x86_64, then an x86_64, x64, or amd64 package should be selected. The specific naming of the architecture used by the package depends on the software distributor.</p>

<p>如果系統架構顯示為 aarch64，咁應該選擇 aarch64 或者 arm64 包；如果系統架構顯示為 x86_64，咁應該選擇 x86_64、 x64 或者 amd64 包。包所用嘅架構嘅具體命名取決於軟件分銷商。</p>

<h3 id="heading-32-download-mysql-package--下載-mysql-軟件包">3.2. Download MySQL package | 下載 MySQL 軟件包</h3>

<p>This document describes how to deploy MySQL using a binary package installation. From the official MySQL archive download page (<a href="https://downloads.mysql.com/archives/community/">https://downloads.mysql.com/archives/community/</a>), select the required MySQL package and copy the link to that binary archive. On the machine where you want to deploy MySQL, use the wget tool to pull the MySQL binary package from that link and place it in the <code class="language-plaintext highlighter-rouge">/usr/local</code> directory. On the machine without an internet connection, use the ssh tool to upload the MySQL binary package from your local machine to the server.</p>

<p>呢份文檔會講解點樣用二進制包安裝部署 MySQL。喺官方嘅 MySQL 封存檔案下載頁面（<a href="https://downloads.mysql.com/archives/community/">https://downloads.mysql.com/archives/community/</a>），選擇所需嘅 MySQL 包，然後將連結複製去嗰個二進制封存檔案。喺你想部署 MySQL 嘅機器上面，用 wget 工具喺嗰個連結度拉出 MySQL 二進制包，然後將佢放入 <code class="language-plaintext highlighter-rouge">/usr/local</code> 目錄。喺冇互聯網連線嘅機器上面，用 ssh 工具將 MySQL 二進制包由你嘅本地機器上載去伺服器。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># cd /usr/local
# wget "https://cdn.mysql.com/archives/mysql-8.0/mysql-8.1.0-linux-glibc2.17-x86_64.tar.xz"
</code></pre></div></div>

<p>You can use the <code class="language-plaintext highlighter-rouge">ls</code> command in the <code class="language-plaintext highlighter-rouge">/usr/local</code> directory to view the files and directories in the current directory, ensuring that the MySQL binary package has been downloaded and placed in the specified location.</p>

<p>你可以用 <code class="language-plaintext highlighter-rouge">/usr/local</code> 目錄入面嘅 <code class="language-plaintext highlighter-rouge">ls</code> 命令嚟睇返當前目錄入面嘅檔案同目錄，確保 MySQL 二進制包已經下載好，然後放喺指定位置。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># ls -lah
total 428M
drwxr-xr-x. 16 root root 4.0K Jul 31 17:27 .
drwxr-xr-x. 14 root root  166 Feb 27  2019 ..
drwxr-xr-x.  2 root root   37 Aug 10  2020 bin
drwxr-xr-x.  2 root root    6 Mar 10  2016 etc
drwxr-xr-x.  2 root root    6 Mar 10  2016 games
drwxr-xr-x.  3 root root   21 Aug 10  2020 include
drwxr-xr-x.  2 root root    6 Mar 10  2016 lib
drwxr-xr-x.  4 root root  159 Aug 10  2020 lib64
drwxr-xr-x.  2 root root    6 Mar 10  2016 libexec
-rw-r--r--   1 root root 428M Jun 28  2023 mysql-8.1.0-linux-glibc2.17-x86_64.tar.xz
drwxr-xr-x   5 root root   44 Mar 26  2018 openssh.bak
drwxr-xr-x.  2 root root    6 Mar 10  2016 sbin
drwxr-xr-x.  6 root root   60 Aug 10  2020 share
drwxr-xr-x.  4 root root  106 Feb 27  2019 src
drwxr-xr-x   9 root root  188 Aug 10  2020 ssl
</code></pre></div></div>

<p>Use the <code class="language-plaintext highlighter-rouge">tar</code> tool to extract the package (in this example, "<code class="language-plaintext highlighter-rouge">mysql-8.1.0-linux-glibc2.17-x86_64.tar.xz</code>") and place it in the same directory.</p>

<p>用「<code class="language-plaintext highlighter-rouge">tar</code>」工具去解壓縮個包（喺呢個例子入面，係「<code class="language-plaintext highlighter-rouge">mysql-8.1.0-linux-glibc2.17-x86_64.tar.xz</code>」），然後將佢放入同一個目錄。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># tar -xvf mysql-8.1.0-linux-glibc2.17-x86_64.tar.xz
</code></pre></div></div>

<p>The directory <code class="language-plaintext highlighter-rouge">/usr/local/mysql-8.1.0-linux-glibc2.17-x86_64</code> is obtained.</p>

<p>得到咗個目錄「<code class="language-plaintext highlighter-rouge">/usr/local/mysql-8.1.0-linux-glibc2.17-x86_64</code>」。</p>

<h3 id="heading-33-setup-users-and-groups--建立用戶和組">3.3. Setup users and groups | 建立用戶和組</h3>

<p>Use the <code class="language-plaintext highlighter-rouge">useradd</code> command to create the <code class="language-plaintext highlighter-rouge">mysql</code> user group, and then create a new <code class="language-plaintext highlighter-rouge">mysql</code> user within that group.</p>

<p>用 <code class="language-plaintext highlighter-rouge">useradd</code> 命令去建立 <code class="language-plaintext highlighter-rouge">mysql</code> 用戶群組，然後喺嗰個群組入面建立一個新嘅 <code class="language-plaintext highlighter-rouge">mysql</code> 用戶。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># groupadd mysql
# useradd -r -g mysql -s /bin/false mysql
</code></pre></div></div>

<h3 id="heading-34-setup-configuration-files-and-directories--建立配置文件和相關目錄">3.4. Setup configuration files and directories | 建立配置文件和相關目錄</h3>

<p>Configure the <code class="language-plaintext highlighter-rouge">my.cnf</code> configuration file as follows and save it in the <code class="language-plaintext highlighter-rouge">/etc</code> directory. You can open and paste it using the Vim editor.</p>

<p>配置 <code class="language-plaintext highlighter-rouge">my.cnf</code> 配置文件，然後將佢放入 <code class="language-plaintext highlighter-rouge">/etc</code> 目錄之中。你可以喺 Vim 編輯器之中打開並且貼上內容。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># vim /etc/my.cnf
</code></pre></div></div>

<p>Paste the following contents.</p>

<p>貼上下面呢啲內容。</p>

<div class="language-conf highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[<span class="n">client</span>]
<span class="c">#ssl-mode=REQUIRED
</span><span class="n">port</span> = <span class="m">3366</span>
<span class="n">socket</span> = /<span class="n">tmp</span>/<span class="n">mysql</span>.<span class="n">sock</span>

[<span class="n">mysql</span>]
<span class="n">prompt</span>=<span class="s2">"\\u@\\h:\p \\R:\\m:\\s [\\d]&gt;"</span>
<span class="c">#tee=/data/mysql/mysql_3366/data/query.log
</span><span class="n">no</span>-<span class="n">auto</span>-<span class="n">rehash</span>
<span class="n">default</span>-<span class="n">character</span>-<span class="n">set</span>=<span class="n">utf8</span>
<span class="n">socket</span> = /<span class="n">tmp</span>/<span class="n">mysql</span>.<span class="n">sock</span>

[<span class="n">mysqld</span>]
<span class="c">#time_zone='+8:00'
</span><span class="n">require_secure_transport</span>=<span class="n">off</span>
<span class="n">basedir</span> = /<span class="n">usr</span>/<span class="n">local</span>/<span class="n">mysql</span>

<span class="c">#misc
</span><span class="n">user</span> = <span class="n">mysql</span>
<span class="n">datadir</span> =/<span class="n">data</span>/<span class="n">mysql</span>/<span class="n">data</span>/
<span class="n">port</span> = <span class="m">3366</span>
<span class="n">socket</span> = /<span class="n">tmp</span>/<span class="n">mysql</span>.<span class="n">sock</span>
<span class="n">event_scheduler</span> = <span class="n">off</span>
<span class="c">#tmp
</span><span class="n">tmpdir</span>=/<span class="n">tmp</span>

<span class="c">#timeout
</span><span class="n">interactive_timeout</span> = <span class="m">1800</span>
<span class="n">wait_timeout</span> = <span class="m">1800</span>

<span class="c">#character set
</span><span class="n">character</span>-<span class="n">set</span>-<span class="n">server</span> = <span class="n">utf8</span>
<span class="n">lower_case_table_names</span>=<span class="m">1</span>
<span class="c">#character-set-server = utf8mb4
#sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'
</span>
<span class="n">open_files_limit</span> = <span class="m">10240</span>
<span class="n">max_connections</span> =<span class="m">1000</span>
<span class="n">max_connect_errors</span> = <span class="m">100</span>

<span class="c">#monitor
#userstat=1
</span>
<span class="n">skip</span>-<span class="n">name</span>-<span class="n">resolve</span> = <span class="n">on</span>
<span class="c">#logs
</span><span class="n">log</span>-<span class="n">output</span>=<span class="n">file</span>
<span class="n">slow_query_log</span> = <span class="m">1</span>
<span class="n">slow_query_log_file</span> = /<span class="n">data</span>/<span class="n">mysql</span>/<span class="n">logs</span>/<span class="n">mysqlslow</span>.<span class="n">log</span>
<span class="n">log</span>-<span class="n">error</span> = /<span class="n">data</span>/<span class="n">mysql</span>/<span class="n">logs</span>/<span class="n">mysqlerror</span>.<span class="n">log</span>
<span class="c">#log_error_verbosity =
</span><span class="n">pid</span>-<span class="n">file</span> = /<span class="n">tmp</span>/<span class="n">mysql</span>.<span class="n">pid</span>
<span class="n">long_query_time</span> = <span class="m">0</span>.<span class="m">1</span>
<span class="c">#log-slow-admin-statements =
#log-queries-not-using-indexes =
</span><span class="n">log</span>-<span class="n">slow</span>-<span class="n">slave</span>-<span class="n">statements</span> = <span class="n">off</span>

<span class="c">#binlog
</span><span class="n">binlog_checksum</span> = <span class="n">CRC32</span>
<span class="n">binlog_format</span> = <span class="n">row</span>
<span class="n">server</span>-<span class="n">id</span> = <span class="m">22567</span>
<span class="n">log</span>-<span class="n">bin</span> = /<span class="n">data</span>/<span class="n">mysql</span>/<span class="n">binlog</span>/<span class="n">mysql</span>-<span class="n">bin</span>
<span class="n">log</span>-<span class="n">bin</span>-<span class="n">index</span> = /<span class="n">data</span>/<span class="n">mysql</span>/<span class="n">binlog</span>/<span class="n">mysql</span>-<span class="n">bin</span>.<span class="n">index</span>
<span class="n">binlog_cache_size</span> = <span class="m">32</span><span class="n">M</span>
<span class="n">max_binlog_size</span> = <span class="m">1</span><span class="n">G</span>
<span class="n">max_binlog_cache_size</span> = <span class="m">1</span><span class="n">G</span>
<span class="n">sync_binlog</span> = <span class="m">1</span>
<span class="n">expire_logs_days</span> = <span class="m">7</span>
<span class="n">sync_master_info</span>=<span class="m">10000</span>
<span class="c">#skip-log-bin
</span>
<span class="c">#relay log
</span><span class="n">relay</span>-<span class="n">log</span> = /<span class="n">data</span>/<span class="n">mysql</span>/<span class="n">binlog</span>/<span class="n">mysql</span>-<span class="n">relay</span>-<span class="n">bin</span>
<span class="n">skip_slave_start</span> =<span class="n">on</span>
<span class="n">max_relay_log_size</span> = <span class="m">1</span><span class="n">G</span>
<span class="n">relay_log_purge</span> =<span class="n">on</span>
<span class="n">relay_log_recovery</span> =<span class="n">on</span>
<span class="n">log_slave_updates</span> = <span class="n">on</span>
<span class="c">#slave-skip-errors=,,
</span><span class="n">sync_relay_log_info</span>=<span class="m">10000</span>

<span class="n">slave_rows_search_algorithms</span>=<span class="n">INDEX_SCAN</span>,<span class="n">HASH_SCAN</span>

<span class="n">explicit_defaults_for_timestamp</span>=<span class="n">off</span>
<span class="c">#buffers &amp; cache
</span><span class="n">table_open_cache</span> = <span class="m">10000</span>
<span class="n">table_definition_cache</span> = <span class="m">10000</span>
<span class="n">max_allowed_packet</span> = <span class="m">1073741824</span> 
<span class="n">max_heap_table_size</span> = <span class="m">256</span><span class="n">M</span>
<span class="n">sort_buffer_size</span> = <span class="m">16</span><span class="n">M</span>
<span class="n">join_buffer_size</span> = <span class="m">16</span><span class="n">M</span>
<span class="c">#thread_cache_size = 0
#query_cache_size = 0
#query_cache_type = 0
#query_cache_limit = 256K
</span><span class="n">thread_stack</span> = <span class="m">192</span><span class="n">K</span>
<span class="n">tmp_table_size</span> = <span class="m">96</span><span class="n">M</span>
<span class="n">key_buffer_size</span> = <span class="m">8</span><span class="n">M</span>
<span class="n">read_buffer_size</span> = <span class="m">16</span><span class="n">M</span>
<span class="n">read_rnd_buffer_size</span> = <span class="m">16</span><span class="n">M</span>
<span class="n">bulk_insert_buffer_size</span> = <span class="m">32</span><span class="n">M</span>

<span class="c">#myisam
#myisam_sort_buffer_size = 128M
#myisam_max_sort_file_size = 10G
#myisam_repair_threads = 
</span>
<span class="c">#innodb
</span><span class="n">innodb_adaptive_hash_index</span>=<span class="n">off</span>
<span class="n">innodb_buffer_pool_size</span> = <span class="m">4</span><span class="n">G</span>
<span class="n">innodb_buffer_pool_instances</span> = <span class="m">43</span>
<span class="n">innodb_data_file_path</span> = <span class="n">ibdata1</span>:<span class="m">1</span><span class="n">G</span>:<span class="n">autoextend</span>
<span class="n">innodb_flush_log_at_trx_commit</span> = <span class="m">1</span>
<span class="n">innodb_log_buffer_size</span> = <span class="m">1</span><span class="n">G</span>
<span class="n">innodb_log_file_size</span> = <span class="m">2</span><span class="n">G</span>
<span class="n">innodb_log_files_in_group</span> = <span class="m">3</span>
<span class="c">#innodb_max_dirty_pages_pct =
</span><span class="n">innodb_file_per_table</span> = <span class="n">on</span>
<span class="c">#innodb_rollback_on_timeout
#innodb_status_file =
#innodb_io_capacity = 50000
</span><span class="n">innodb_io_capacity</span>=<span class="m">200</span>
<span class="n">innodb_io_capacity_max</span>=<span class="m">400</span>
<span class="n">transaction_isolation</span> = <span class="n">REPEATABLE</span>-<span class="n">READ</span>

<span class="c">#innodb_flush_method = O_DIRECT
</span>
<span class="n">gtid_mode</span> = <span class="n">ON</span>
<span class="n">enforce_gtid_consistency</span> = <span class="n">ON</span>
<span class="n">master_info_repository</span> = <span class="n">TABLE</span>
<span class="n">relay</span>-<span class="n">log</span>-<span class="n">info</span>-<span class="n">repository</span> = <span class="n">TABLE</span>
<span class="n">innodb_undo_tablespaces</span>=<span class="m">0</span>
<span class="n">innodb_undo_log_truncate</span> = <span class="n">on</span>
<span class="n">innodb_lock_wait_timeout</span>=<span class="m">10</span>

<span class="c"># replication
#rpl_semi_sync_master_enabled = 0;
#rpl_semi_sync_master_timeout = 3600;
</span>
<span class="c"># Two-Master configure
#server-
#auto-increment-offset =
#auto-increment-increment = 
</span>
<span class="c">#server-
#auto-increment-offset =
#auto-increment-increment = 
</span>
<span class="n">slave_preserve_commit_order</span> = <span class="n">on</span>
<span class="n">slave_transaction_retries</span> = <span class="m">10</span>
<span class="n">log_timestamps</span> = <span class="n">system</span>
<span class="c">#show_compatibility_56 = on
</span><span class="n">slave_parallel_workers</span> = <span class="m">4</span>
<span class="n">slave_parallel_type</span> = <span class="n">LOGICAL_CLOCK</span>

<span class="n">interactive_timeout</span>=<span class="m">3600</span>
<span class="n">wait_timeout</span>=<span class="m">3600</span>
<span class="n">lock_wait_timeout</span>=<span class="m">60</span>
<span class="n">max_user_connections</span>=<span class="m">800</span>
<span class="c">#log_warnings=0
#log_error_verbosity=0
#optimizer_switch='index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=off,engine_condition_pushdown=on,index_condition_pushdown=on,mrr=on,mrr_cost_based=on,block_nested_loop=on,batched_key_access=off,materialization=on,semijoin=on,loosescan=on,firstmatch=on,duplicateweedout=on,subquery_materialization_cost_based=on,use_index_extensions=on,condition_fanout_filter=on,derived_merge=on,favor_range_scan=off'
</span>
[<span class="n">mysqld_safe</span>]
<span class="n">nice</span>=-<span class="m">19</span>
<span class="n">open</span>-<span class="n">files</span>-<span class="n">limit</span>=<span class="m">10240</span>
</code></pre></div></div>

<p>Save this configuration file after pasting. Create the MySQL-related directories to store the database's data files and logs.</p>

<p>貼上之後儲存呢個設定檔案。建立 MySQL 相關目錄嚟儲存資料庫嘅數據檔案同記錄。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># mkdir -p /data/mysql/data
# mkdir -p /data/mysql/binlog
# mkdir -p /data/mysql/logs
</code></pre></div></div>

<p>After creating the directory, switch the current directory to <code class="language-plaintext highlighter-rouge">/data</code> and grant ownership of the directory to the <code class="language-plaintext highlighter-rouge">mysql</code> user and group.</p>

<p>建立目錄之後，將當前目錄切換為 <code class="language-plaintext highlighter-rouge">/data</code>，並將目錄嘅擁有權授予 <code class="language-plaintext highlighter-rouge">mysql</code> 用戶同群組。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># cd /data
# chown -R mysql:mysql /data/mysql/
</code></pre></div></div>

<h3 id="heading-35-install--安裝">3.5. Install | 安裝</h3>

<p>Switch to the directory where the MySQL package is located, <code class="language-plaintext highlighter-rouge">/usr/local</code>.</p>

<p>切入去 MySQL 包嘅目錄，即係 <code class="language-plaintext highlighter-rouge">/usr/local</code>。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># cd /usr/local
</code></pre></div></div>

<p>To facilitate subsequent deployment and use, create a symbolic link to the MySQL package: <code class="language-plaintext highlighter-rouge">/usr/local/mysl</code>.</p>

<p>為咗方便部署及使用，創建一個軟鏈接，連接到 MySQL 嘅軟件包 <code class="language-plaintext highlighter-rouge">/usr/local/mysl</code>。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># ln -s /usr/local/mysql-8.1.0-linux-glibc2.17-x86_64 /usr/local/mysql
</code></pre></div></div>

<p>Check if the symbolic link was created successfully.</p>

<p>檢查此軟鏈接係咪已經成功創建。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># ls -l | grep mysql
lrwxrwxrwx  1 root root        45 Jul 24 13:50 mysql -&gt; /usr/local/mysql-8.1.0-linux-glibc2.17-x86_64
drwxr-xr-x  9 root root       129 Jul 24 13:40 mysql-8.1.0-linux-glibc2.17-x86_64
-rw-r--r--  1 root root 448377800 Jun 28  2023 mysql-8.1.0-linux-glibc2.17-x86_64.tar.xz
</code></pre></div></div>

<h3 id="heading-36-initialization--初始化">3.6. Initialization | 初始化</h3>

<p>Use the <code class="language-plaintext highlighter-rouge">mysqld</code> program to initialize the MySQL server.</p>

<p>使用 <code class="language-plaintext highlighter-rouge">mysqld</code> 程式來初始化 MySQL 伺服器。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># /usr/local/mysql/bin/mysqld --initialize --user=mysql
</code></pre></div></div>

<p>Copy the startup script that comes with the MySQL support files and place it in the <code class="language-plaintext highlighter-rouge">/etc/init.d</code> directory.</p>

<p>複製 MySQL 支援檔案隨附嘅啟動指令腳本，然後將佢放入 <code class="language-plaintext highlighter-rouge">/etc/init.d</code> 目錄。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># cp /usr/local/mysql/support-files/mysql.server /etc/init.d/mysql.server
</code></pre></div></div>

<p>Use <code class="language-plaintext highlighter-rouge">service</code> tool to start the MySQL services.</p>

<p>使用 <code class="language-plaintext highlighter-rouge">service</code> 工具來啟動 MySQL 服務。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># service mysql.server start
</code></pre></div></div>

<p>When you see the message "<code class="language-plaintext highlighter-rouge">Starting MySQL... SUCCESS!</code>", it means the MySQL database has started successfully. The temporary password generated upon initial startup can be found in the MySQL logs. This password will be required for the first login, and you should change your account password immediately after logging into MySQL. According to the configuration file <code class="language-plaintext highlighter-rouge">my.cnf</code>, the MySQL error log is stored in <code class="language-plaintext highlighter-rouge">/data/mysql/logs/mysqlerror.log</code>. You can directly use the <code class="language-plaintext highlighter-rouge">cat</code> and <code class="language-plaintext highlighter-rouge">grep</code> tools to search for the keyword "<code class="language-plaintext highlighter-rouge">password</code>" to find the temporary password generated by MySQL.</p>

<p>當你見到「<code class="language-plaintext highlighter-rouge">Starting MySQL... SUCCESS!</code>」嘅訊息，即係 MySQL 資料庫已經成功啟動。初次啟動時產生嘅臨時密碼可以喺 MySQL 記錄入面搵到。第一次登入需要呢個密碼，而你應該喺登入 MySQL 之後即刻更改你嘅帳戶密碼。根據配置檔案 <code class="language-plaintext highlighter-rouge">my.cnf</code>， MySQL 錯誤記錄儲存喺 <code class="language-plaintext highlighter-rouge">/data/mysql/logs/mysqlerror.log</code>。你可以直接用 <code class="language-plaintext highlighter-rouge">cat</code> 同 <code class="language-plaintext highlighter-rouge">grep</code> 工具去搜尋關鍵字「<code class="language-plaintext highlighter-rouge">password</code>」嚟搵 MySQL 生成嘅臨時密碼。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># cat /data/mysql/logs/mysqlerror.log | grep password
2025-07-21T17:10:07.293097+08:00 6 [Note] [MY-010454] [Server] A temporary password is generated for root@localhost: jnPW*+Lse52Z
</code></pre></div></div>

<p>"<code class="language-plaintext highlighter-rouge">jnPW*+Lse52Z</code>" is the temporary password generated in this example. It's important to note that the temporary password initialized after each MySQL deployment is different; therefore, you must check the logs for this password after each deployment and initialization. Use this password to log in to the MySQL server.</p>

<p>「<code class="language-plaintext highlighter-rouge">jnPW*+Lse52Z</code>」係呢個例子入面生成嘅臨時密碼。重要嘅係要留意，每次 MySQL 部署之後初始化嘅臨時密碼都唔同；所以，你必須喺每次部署同初始化之後檢查呢個密碼嘅記錄。用呢個密碼登入 MySQL 伺服器。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># /usr/local/mysql/bin/mysql -uroot -p
</code></pre></div></div>

<p>Follow the prompts to enter your password. The appearance of the "<code class="language-plaintext highlighter-rouge">&gt;</code>" sign indicates successful login to the MySQL command-line interface. Change the password for the root account.</p>

<p>跟住啲提示輸入你嘅密碼。出現「<code class="language-plaintext highlighter-rouge">&gt;</code>」號表示成功登入 MySQL 命令行介面。更改 root 帳戶嘅密碼。</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> <span class="k">ALTER</span> <span class="k">USER</span> <span class="s1">'root'</span><span class="o">@</span><span class="s1">'localhost'</span> <span class="n">IDENTIFIED</span> <span class="k">BY</span> <span class="s1">'newpassword'</span><span class="p">;</span>
</code></pre></div></div>

<h3 id="heading-37-create-application-account--創建應用帳戶">3.7. Create application account | 創建應用帳戶</h3>

<p>Using the MySQL root user for application connections is insecure; therefore, a dedicated account should be created for the application. Following the principle of least privilege, the application-specific account should be granted the necessary permissions. The application account name should be related to the current database ID or the application itself to avoid confusion. For example, on a server with server-id 13, create an account for the application, name it "apn_13", and grant it read and write permissions. The CREATE, DELETE, and DROP permissions should be granted cautiously based on the specific application requirements.</p>

<p>用 MySQL 根用戶嚟連接應用程式係唔安全嘅；所以，應該為個應用程式建立一個專用嘅帳戶。根據最低特權原則，應用程式專用帳戶應該獲得必要嘅權限。應用程式帳戶名稱應該同當前嘅資料庫 ID 或者應用程式本身有關，以免混淆。例如，喺伺服器 ID 為 13 嘅伺服器上面，為應用程式建立一個帳戶，將佢命名為「apn_13」，然後授予佢讀寫權限。應該根據特定嘅應用程式要求，謹慎噉授予 CREATE、DELETE 同 DROP 權限。</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> <span class="k">CREATE</span> <span class="k">USER</span> <span class="s1">'apn_13'</span><span class="o">@</span><span class="s1">'localhost'</span> <span class="n">IDENTIFIED</span> <span class="k">BY</span> <span class="s1">'newpassword'</span><span class="p">;</span>
<span class="o">&gt;</span> <span class="k">GRANT</span> <span class="k">SELECT</span><span class="p">,</span> <span class="k">INSERT</span><span class="p">,</span> <span class="k">UPDATE</span> <span class="k">ON</span> <span class="n">database_name</span><span class="p">.</span><span class="n">database_table</span> <span class="k">TO</span> <span class="s1">'apn_13'</span><span class="o">@</span><span class="s1">'localhost'</span><span class="p">;</span>
</code></pre></div></div>

<p>In the command to create a user, the "<code class="language-plaintext highlighter-rouge">newpassword</code>" field needs to be set to a new password that meets the security requirements and is different from the root user's password. In the command to grant permissions, the "<code class="language-plaintext highlighter-rouge">database_name</code>" and "<code class="language-plaintext highlighter-rouge">database_table</code>" fields should be filled in according to the application's usage. If the application uses all databases and tables in the database, you can fill in "<code class="language-plaintext highlighter-rouge">*.*</code>" to represent all databases and tables, for example:</p>

<p>喺建立用戶嘅命令入面，「<code class="language-plaintext highlighter-rouge">newpassword</code>」欄位需要設定為一個符合安全要求嘅新密碼，同埋同 root 用戶嘅密碼唔同。喺授予權限嘅命令入面，應該根據應用程式嘅用途填寫「<code class="language-plaintext highlighter-rouge">database_name</code>」同「<code class="language-plaintext highlighter-rouge">database_table</code>」欄位。如果應用程式用咗資料庫入面嘅所有資料庫同表，你可以填「<code class="language-plaintext highlighter-rouge">*.*</code>」嚟表示所有資料庫同表，例如：</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> <span class="k">GRANT</span> <span class="k">SELECT</span><span class="p">,</span> <span class="k">INSERT</span><span class="p">,</span> <span class="k">UPDATE</span> <span class="k">ON</span> <span class="o">*</span><span class="p">.</span><span class="o">*</span> <span class="k">TO</span> <span class="s1">'apn_13'</span><span class="o">@</span><span class="s1">'localhost'</span><span class="p">;</span>
</code></pre></div></div>

<h2 id="heading-4-upgrade--版本升級">4. Upgrade | 版本升級</h2>

<h3 id="heading-41-small-version-numbers-upgrade-directly">4.1. Small Version Numbers: Upgrade Directly</h3>

<h4 id="heading-411-check-environment">4.1.1. Check environment</h4>

<p>Check the current system environment. We can query for the system information and its MySQL version information from terminal commands. For example the test machine T2:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>- IP: 10.91.134.xxx
- OS: Red Hat Enterprise Linux 7
- CPU Architecture: x86_64
- MySQL: 8.0.36 MySQL Community Server
- GLIBC: GNU libc 2.17
</code></pre></div></div>

<p>From the information above, we can confirmed that the running MySQL version is MySQL 8.0.36.</p>

<p>Then we confirm the service status.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># service mysql.service status
</code></pre></div></div>

<p>Log into MySQL command line interface and query for current MySQL information in service.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># /usr/local/mysql/bin/mysql -u root -p
</code></pre></div></div>

<p>Check the current version information.</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> <span class="k">SELECT</span> <span class="k">Version</span><span class="p">();</span>
</code></pre></div></div>

<p>And then get the version.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>+-----------+
| version() |
+-----------+
| 8.0.36    |
+-----------+
1 row in set (0.00 sec)
</code></pre></div></div>

<h4 id="heading-412-download-new-version-of-mysql-binary-package">4.1.2. Download new version of MySQL binary package</h4>

<p>Copy download link from official <a href="https://downloads.mysql.com/archives/community/">MySQL website</a> and pull down to machine via <code class="language-plaintext highlighter-rouge">wget</code> tool. Put the newly downloaded package under the directory <code class="language-plaintext highlighter-rouge">/usr/local</code>. In this example we need to upgrade MySQL 8.0.36 to MySQL 8.0.42 so we just <code class="language-plaintext highlighter-rouge">wget</code> the package of MySQL 8.0.42.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># cd /usr/local
# wget "https://downloads.mysql.com/archives/get/p/23/file/mysql-8.0.42-linux-glibc2.28-x86_64.tar.xz"
</code></pre></div></div>

<p>Wait until the progress bar finished, then check the <code class="language-plaintext highlighter-rouge">/usr/local</code> directory to see whether the package <code class="language-plaintext highlighter-rouge">mysql-8.0.42-linux-glibc2.28-x86_64.tar.xz</code> exists. Then use <code class="language-plaintext highlighter-rouge">tar</code> to extract the package.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># tar -xvf mysql-8.0.42-linux-glibc2.17-x86_64.tar.xz
</code></pre></div></div>

<p>Get the directory for new version.</p>

<h4 id="heading-413-stop-the-applications">4.1.3. Stop the applications</h4>

<p>Release the connection to MySQL database to prevent the read/write problem. This step should be done according to the actual situation.</p>

<h4 id="heading-414-stop-the-service-of-database">4.1.4. Stop the service of database</h4>

<p>Log into MySQL and run dirty-data writing operations.</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> <span class="k">SET</span> <span class="k">GLOBAL</span> <span class="n">innodb_fast_shutdown</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</code></pre></div></div>

<p>Log out MySQL and shut down MySQL services.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># /usr/local/mysql/bin/mysqladmin -u root -p shutdown
</code></pre></div></div>

<p>The services will be closed after the password is entered. Check the launch daemons to make sure the services are stopped.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># service mysqld status
</code></pre></div></div>

<p>If "<code class="language-plaintext highlighter-rouge">ERROR! MySQL is not running, but lock file (/var/lock/subsys/mysql) exists</code>" shows up, the MySQL services are shutted down correctly.</p>

<h4 id="heading-415-remove-old-version">4.1.5. Remove old version</h4>

<p>Remove the soft link of old version MySQL, and create a new soft link for new version.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># rm -f /usr/local/usr
# ln -s /usr/local/mysql-8.0.42-linux-glibc2.28-x86_64 /usr/local/mysql
</code></pre></div></div>

<h4 id="heading-416-compatibility-checking">4.1.6. Compatibility checking</h4>

<p>Use <code class="language-plaintext highlighter-rouge">mysqld_safe</code> launch the new version MySQL temporary, then use <code class="language-plaintext highlighter-rouge">mysql_upgrade</code> tool attached to MySQL to check the compatibility.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># /usr/local/mysql/bin/mysql_safe --user=mysql
# /usr/local/mysql/bin/mysql_upgrade -u root -p
</code></pre></div></div>

<p>Then we will get the hints.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>The mysql_upgrade client is now deprecated. The actions executed by the upgrade client are now done by the server.
To upgrade, please start the new MySQL binary with the older data directory. Repairing user tables is done automatically. Restart is not required after upgrade.
The upgrade process automatically starts on running a new MySQL binary with an older data directory. To avoid accidental upgrades, please use the --upgrade=NONE option with the MySQL binary. The option --upgrade=FORCE is also provided to run the server upgrade sequence on demand.
It may be possible that the server upgrade fails due to a number of reasons. In that case, the upgrade sequence will run again during the next MySQL server start. If the server upgrade fails repeatedly, the server can be started with the --upgrade=MINIMAL option to start the server without executing the upgrade sequence, thus allowing users to manually rectify the problem.
</code></pre></div></div>

<p>If there is no compatibility problem we can restart the database. Copy one new version launch daemon from <code class="language-plaintext highlighter-rouge">support-files</code> directory to <code class="language-plaintext highlighter-rouge">/etc/init.d</code>.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># cp /usr/local/mysql/support-files/mysql.server /etc/init.d/mysqld
# service mysqld start
</code></pre></div></div>

<p>Then log into the MySQL and verify the current version.</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> <span class="k">SELECT</span> <span class="k">Version</span><span class="p">();</span>
</code></pre></div></div>

<h4 id="heading-417-roll-back">4.1.7. Roll back</h4>

<p>If we run into any compatibility related problems in previous steps, we need to roll back. Restore the MySQL soft link to previous status and restart. Stop MySQL service first if there is MySQL service running.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># service mysqld stop
</code></pre></div></div>

<p>or</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># /usr/local/mysql/support-files/mysql.server stop
</code></pre></div></div>

<p>remove the current version soft link.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># rm -f /usr/local/mysql
</code></pre></div></div>

<p>and create soft link of previous version.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># ln -s /usr/local/mysql-8.0.36-linux-glibc2.28-x86_64 /usr/local/mysql
</code></pre></div></div>

<p>start previous version's services.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># /usr/local/mysql/support-files/mysql.server start
</code></pre></div></div>

<p>Log into MySQL to check the version is rolled back to the previous status. If everything is fine, remove the launch daemon in <code class="language-plaintext highlighter-rouge">/etc/init.d</code>, and copy one prevous launch daemon to the <code class="language-plaintext highlighter-rouge">/etc/init.d</code> directory.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># rm -f /etc/init.d/mysqld
# cp /usr/local/mysql/support-files/mysql.server /etc/init.d/mysqld
</code></pre></div></div>

<h3 id="heading-42-large-version-numbers-logical-data-migration">4.2. Large Version Numbers: Logical Data Migration</h3>

<h4 id="heading-421-check-the-environment">4.2.1. Check the environment</h4>

<p>Confirm the current environment of the machine. From the terminal we can get the current system and MySQL information. In this example, they are:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>- IP: 10.91.134.136
- OS: Red Hat Enterprise Linux 7
- CPU Architecture: x86_64
- MySQL: 8.0.36 MySQL Community Server
- GLIBC: GNU libc 2.17
</code></pre></div></div>

<p>The currently running MySQL is version 8.0.36, and we can easily check the status of MySQL services.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># service mysqld status
</code></pre></div></div>

<p>Log into MySQL and query for the current MySQL version which is serving.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># /usr/local/mysql/bin/mysql -u root -p
</code></pre></div></div>

<p>Confirm the current version.</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> <span class="k">SELECT</span> <span class="k">Version</span><span class="p">();</span>
</code></pre></div></div>

<p>We can get the version is 8.0.36.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>+-----------+
| version() |
+-----------+
| 8.0.36    |
+-----------+
1 row in set (0.00 sec)
</code></pre></div></div>

<h4 id="heading-422-download-new-version-of-mysql-binary-package">4.2.2. Download new version of MySQL binary package</h4>

<p>Download MySQL from the official <a href="https://downloads.mysql.com/archives/community/">MySQL archive website</a>. Copy the target link and use <code class="language-plaintext highlighter-rouge">wget</code> tool to download package to machine. Put the newly downloaded MySQL package in <code class="language-plaintext highlighter-rouge">/usr/local</code> directory. In this example case, we need to upgrade MySQL from 8.0.42 to MySQL 9.3.0, so we <code class="language-plaintext highlighter-rouge">wget</code> the MySQL 9.3.0 package directly.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># cd /usr/local
# wget "https://downloads.mysql.com/archives/get/p/23/file/mysql-9.3.0-linux-glibc2.17-x86_64.tar.xz"
</code></pre></div></div>

<p>Wait for the progress bar and check the <code class="language-plaintext highlighter-rouge">/usr/local</code> directory to see whether there is a <code class="language-plaintext highlighter-rouge">mysql-9.3.0-linux-glibc2.17-x86_64.tar.xz</code> package. Then extract the package via <code class="language-plaintext highlighter-rouge">tar</code> tool.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># tar -xvf mysql-9.3.0-linux-glibc2.17-x86_64.tar.xz
</code></pre></div></div>

<p>Now we get the new version MySQL directory. In this case it is <code class="language-plaintext highlighter-rouge">mysql-9.3.0-linux-glibc2.17-x86_64</code>.</p>

<h4 id="heading-423-export-all-the-data-from-whole-database">4.2.3. Export all the data from whole database</h4>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># /usr/local/mysql/bin/mysqldump -u root -p --add-drop-table --routines --events --all-databases --force &gt; backup_file.sql
</code></pre></div></div>

<p>The exported data is contained in the <code class="language-plaintext highlighter-rouge">sql</code> file.</p>

<h4 id="heading-424-stop-applications">4.2.4. Stop applications</h4>

<p>Release the connection to database.</p>

<h4 id="heading-425-stop-database-services">4.2.5. Stop database services</h4>

<p>Run the write dirty data operation. This operation may need a longer time, and it depends on the amount of dirty data and the speed of the disks.</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> <span class="k">SET</span> <span class="k">GLOBAL</span> <span class="n">innodb_fast_shutdown</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</code></pre></div></div>

<h4 id="heading-426-soft-link-to-new-mysql">4.2.6. Soft link to new MySQL</h4>

<p>Remove old MySQL soft link first, and then create a new MySQL soft link for new version.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># rm -f /usr/local/mysql
# ln -s /usr/local/mysql-9.3.0-linux-glibc2.28-x86_64 /usr/local/mysql
</code></pre></div></div>

<h4 id="heading-427-prepare-a-configuration-file">4.2.7. Prepare a configuration file</h4>

<p>Backup old MySQL configurations <code class="language-plaintext highlighter-rouge">my.cnf</code> and move the file to a backup directory.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># mv /etc/my.cnf /data/backup/backup_8.0.42.cnf
</code></pre></div></div>

<p>Check the configuration file for new MySQL to make sure it is matched with the update descriptions of newer MySQL versions. For those changed configurations items and deprecated items, new alternatives should be applied to the configuration file. For issues made by changes of the configurations, see details in Chapter Diagnose. Put the new version's configuration file in <code class="language-plaintext highlighter-rouge">/etc</code> directory and name it as <code class="language-plaintext highlighter-rouge">my.cnf</code>.</p>

<h4 id="heading-428-initialize-the-new-database">4.2.8. Initialize the new database</h4>

<p>Change to <code class="language-plaintext highlighter-rouge">/data</code> directory. Backup old MySQL related directories as <code class="language-plaintext highlighter-rouge">mysql_bak</code>.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># cd /data
# mv mysql mysql_bak
</code></pre></div></div>

<p>Create new MySQL directory under <code class="language-plaintext highlighter-rouge">/data</code> directory.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># mkdir -p /data/mysql/data
# mkdir -p /data/mysql/binlog
# mkdir -p /data/mysql/logs
</code></pre></div></div>

<p>And its structure should be like this.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mysql
├── binlog
├── data
└── logs
</code></pre></div></div>

<p>Change the owner and privileges of the directory and subdirectories to <code class="language-plaintext highlighter-rouge">mysql:mysql</code>.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># chown -R mysql:mysql /data/mysql
</code></pre></div></div>

<p>Use <code class="language-plaintext highlighter-rouge">ls -lah</code> command to see detailed privilege information and make sure the change is successful.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># ls -lah | grep mysql
drwxr-xr-x   5 mysql mysql   44 Jul 22 15:33 mysql
</code></pre></div></div>

<p>Initialize the MySQL database.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># /usr/local/mysql/bin/mysql --initialize --user=mysql
</code></pre></div></div>

<h4 id="heading-429-start-the-database">4.2.9. Start the database</h4>

<p>If the initialization is successful, we can start the database. Copy one launch daemon to <code class="language-plaintext highlighter-rouge">/etc/init.d</code> first.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># cp /usr/local/mysql/support-files/mysql.server /etc/init.d/mysqld
# service mysqld start
</code></pre></div></div>

<h2 id="heading-5-data-importing--數據導入">5. Data Importing | 數據導入</h2>

<p>When we finish the construction of the database and start services, we may need to migrate the original data from the previous database to new database. In this part we describe the operations that imports previous data to new database.</p>

<p>Create new database. According to the data scripts (usually <code class="language-plaintext highlighter-rouge">sql</code> files), setup databases with same names as previous databases. Generally, the names of the databases should be same of names contained in exported <code class="language-plaintext highlighter-rouge">sql</code> scripts. Additionally, exported backup scripts also contain exporting time and other information.</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> <span class="k">CREATE</span> <span class="k">DATABASE</span> <span class="n">original_data</span><span class="p">;</span>
</code></pre></div></div>

<p>Change into the newly created database <code class="language-plaintext highlighter-rouge">original_data</code> after the creation, and the <code class="language-plaintext highlighter-rouge">original_data</code> can be any name according to the actual situation.</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> <span class="n">USE</span> <span class="n">original_data</span><span class="p">;</span>
</code></pre></div></div>

<p>Then upload the <code class="language-plaintext highlighter-rouge">sql</code> import script to the server. Log into the MySQL command line interface, import the data in <code class="language-plaintext highlighter-rouge">original_data</code> database. The position where the script is located should be an absolute path.</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> <span class="k">SOURCE</span> <span class="o">/</span><span class="n">path</span><span class="o">/</span><span class="k">to</span><span class="o">/</span><span class="n">original_data_backup_time</span><span class="p">.</span><span class="k">sql</span><span class="p">;</span>
</code></pre></div></div>

<p>Wait for the importing process to finish. If there are several <code class="language-plaintext highlighter-rouge">sql</code> backup scripts need to import, we should import them by time order, from early to the latest.</p>

<h2 id="heading-6-main-and-duplicate-database-preparing--主備數據庫搭建">6. Main and Duplicate Database Preparing | 主備數據庫搭建</h2>

<h3 id="heading-61-prepare-and-check">6.1. Prepare and Check</h3>

<p>To set up a MySQL master-slave database setup, at least one device is needed for the master database and one device for the slave database. Both devices should have the same MySQL database software version installed. In this example, both test machines are running MySQL version 8.1.0. The system environments of test machines t1 and t2 are as follows. For simplicity, the two machines used to set up the master-slave database setup will be referred to as t1 and t2.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Test Machine t1:
Linux clz-db-t1 3.10.0-693.el7.x86_64 #1 SMP Thu Jul 6 19:56:57 EDT 2017 x86_64 x86_64 x86_64 GNU/Linux

Test Machine t2:
Linux clz-db-t2 3.10.0-693.el7.x86_64 #1 SMP Thu Jul 6 19:56:57 EDT 2017 x86_64 x86_64 x86_64 GNU/Linux
</code></pre></div></div>

<p>The two test machine have the same configuration and system environments, and they both have MySQL 8.1.0 database server installed. Start these two test machine, login and verify their MySQL version.</p>

<h3 id="heading-62-master-slave-architecture">6.2. Master-slave Architecture</h3>

<h4 id="heading-621-setup-mysqlserver-services">6.2.1. Setup <code class="language-plaintext highlighter-rouge">mysql.server</code> services</h4>

<p><strong>Managing the MySQL service directly through <code class="language-plaintext highlighter-rouge">mysql.server</code>:</strong> When not using the <code class="language-plaintext highlighter-rouge">service</code> utility to manage <code class="language-plaintext highlighter-rouge">mysqld</code>, starting the MySQL service requires using <code class="language-plaintext highlighter-rouge">/mysqld/support-files/mysql.server</code>. This method is suitable for MySQL software that was not installed using RPM.</p>

<p>Start MySQL services:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># /usr/local/mysql/support-files/mysql.server start
</code></pre></div></div>

<p>Stop MySQL services:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># /usr/local/mysql/support-files/mysql.server stop
</code></pre></div></div>

<p>Check MySQL status:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># /usr/local/mysql/support-files/mysql.server status
</code></pre></div></div>

<p><strong>Managing MySQL services using <code class="language-plaintext highlighter-rouge">systemctl</code>:</strong> If the MySQL database software was installed via RPM, you can use <code class="language-plaintext highlighter-rouge">systemctl</code> to manage the MySQL service.</p>

<p>Enable MySQL services:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># systemctl enable mysqld.service
</code></pre></div></div>

<p>Start MySQL services:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># systemctl start mysqld.service
</code></pre></div></div>

<p>Check MySQL status:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># systemctl status mysqld.service
</code></pre></div></div>

<p>Stop MySQL services:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># systemctl stop mysqld.service
</code></pre></div></div>

<p>Disable MySQL services:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># systemctl disable mysqld.service
</code></pre></div></div>

<p>Restart MySQL services:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># systemctl restart mysqld.service
</code></pre></div></div>

<p><strong>Managing the MySQL service using a service:</strong> For easier testing and management, you can use the <code class="language-plaintext highlighter-rouge">mysqld</code> service to control the MySQL service. Copy the <code class="language-plaintext highlighter-rouge">mysql.server</code> script from the <code class="language-plaintext highlighter-rouge">support-files</code> directory under your MySQL software installation directory to the <code class="language-plaintext highlighter-rouge">/etc/init.d</code> directory and rename it to <code class="language-plaintext highlighter-rouge">mysqld</code>. This method is suitable for MySQL server software installed using binary packages.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># cp /usr/local/mysql/support-files/mysql.server /etc/init.d/mysql.server
</code></pre></div></div>

<p>Perform this process on both machines, and then you can use the service command to manage the MySQL service status.</p>

<p>Stop MySQL services:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># service mysql.server stop
</code></pre></div></div>

<p>Check MySQL status:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># service mysql.server status
</code></pre></div></div>

<p>Start MySQL services:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># service mysql.server start
</code></pre></div></div>

<h4 id="heading-622-mysql-database-host-settings">6.2.2. MySQL database host settings</h4>

<p>Check the machine <code class="language-plaintext highlighter-rouge">t1</code> that hosts the MySQL master database, log in and open the MySQL configuration file <code class="language-plaintext highlighter-rouge">my.cnf</code>. This configuration file should be located in the <code class="language-plaintext highlighter-rouge">/etc</code> directory.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># vim /etc/my.cnf
</code></pre></div></div>

<p>Check that the configuration items <code class="language-plaintext highlighter-rouge">bin-log</code> and <code class="language-plaintext highlighter-rouge">server-id</code> in the configuration file are set and not empty. The <code class="language-plaintext highlighter-rouge">server-id</code> can be set according to the actual situation; the test machine's <code class="language-plaintext highlighter-rouge">server-id</code> is set to 22567. Note that the <code class="language-plaintext highlighter-rouge">server-id</code> of the MySQL database master server and the database standby (slave) server must not be the same; otherwise, the database backup machine will be unable to connect to the master.</p>

<p>Create a backup account for master-slave replication on the MySQL master database server. First, list all accounts currently in MySQL for verification and comparison.</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> <span class="k">SELECT</span> <span class="k">user</span> <span class="k">FROM</span> <span class="n">mysql</span><span class="p">.</span><span class="k">user</span><span class="p">;</span>
</code></pre></div></div>

<p>Then create a new backup user dedicated to master-slave replication, named <code class="language-plaintext highlighter-rouge">replica_user</code>.</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> <span class="k">CREATE</span> <span class="k">USER</span> <span class="s1">'replica_user'</span><span class="o">@</span><span class="s1">'%'</span> <span class="n">IDENTIFIED</span> <span class="k">BY</span> <span class="s1">'newpassword'</span><span class="p">;</span>
</code></pre></div></div>

<p>Listing all users in MySQL again reveals that the newly created user, <code class="language-plaintext highlighter-rouge">replica_user</code>, has been added to the MySQL database.</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> <span class="k">SELECT</span> <span class="k">user</span> <span class="k">FROM</span> <span class="n">mysql</span><span class="p">.</span><span class="k">user</span><span class="p">;</span>
<span class="o">+</span><span class="c1">------------------+</span>
<span class="o">|</span> <span class="k">user</span>             <span class="o">|</span>
<span class="o">+</span><span class="c1">------------------+</span>
<span class="o">|</span> <span class="n">replica_user</span>     <span class="o">|</span>
<span class="o">|</span> <span class="n">mysql</span><span class="p">.</span><span class="n">infoschema</span> <span class="o">|</span>
<span class="o">|</span> <span class="n">mysql</span><span class="p">.</span><span class="k">session</span>    <span class="o">|</span>
<span class="o">|</span> <span class="n">mysql</span><span class="p">.</span><span class="n">sys</span>        <span class="o">|</span>
<span class="o">|</span> <span class="n">root</span>             <span class="o">|</span>
<span class="o">+</span><span class="c1">------------------+</span>
<span class="mi">5</span> <span class="k">rows</span> <span class="k">in</span> <span class="k">set</span> <span class="p">(</span><span class="mi">0</span><span class="p">.</span><span class="mi">00</span> <span class="n">sec</span><span class="p">)</span>
</code></pre></div></div>

<p>Check the host settings to ensure that the <code class="language-plaintext highlighter-rouge">log_bin</code> binary log is enabled, and check the current host's master status. Find the status of the <code class="language-plaintext highlighter-rouge">log_bin</code> variable in the configuration; this status should be ON. If this status is OFF, modify this value in the configuration file <code class="language-plaintext highlighter-rouge">my.cnf</code> and restart the MySQL service.</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> <span class="k">SHOW</span> <span class="n">VARIABLES</span> <span class="k">LIKE</span> <span class="s1">'log_bin'</span><span class="p">;</span>
<span class="o">+</span><span class="c1">---------------+-------+</span>
<span class="o">|</span> <span class="n">Variable_name</span> <span class="o">|</span> <span class="n">Value</span> <span class="o">|</span>
<span class="o">+</span><span class="c1">---------------+-------+</span>
<span class="o">|</span> <span class="n">log_bin</span>       <span class="o">|</span> <span class="k">ON</span>    <span class="o">|</span>
<span class="o">+</span><span class="c1">---------------+-------+</span>
<span class="mi">1</span> <span class="k">row</span> <span class="k">in</span> <span class="k">set</span> <span class="p">(</span><span class="mi">0</span><span class="p">.</span><span class="mi">01</span> <span class="n">sec</span><span class="p">)</span>
</code></pre></div></div>

<p>Check the current MySQL host status. Use the following command in MySQL 8.0.</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> <span class="k">SHOW</span> <span class="n">MASTER</span> <span class="n">STATUS</span> <span class="err">\</span><span class="k">G</span>
<span class="o">***************************</span> <span class="mi">1</span><span class="p">.</span> <span class="k">row</span> <span class="o">***************************</span>
<span class="n">File</span><span class="p">:</span> <span class="n">mysql</span><span class="o">-</span><span class="n">bin</span><span class="p">.</span><span class="mi">000001</span>
<span class="k">Position</span><span class="p">:</span> <span class="mi">157</span>
<span class="n">Binlog_Do_DB</span><span class="p">:</span>
<span class="n">Binlog_Ignore_DB</span><span class="p">:</span>
<span class="n">Executed_Gtid_Set</span><span class="p">:</span>
<span class="mi">1</span> <span class="k">row</span> <span class="k">in</span> <span class="k">set</span> <span class="p">(</span><span class="mi">0</span><span class="p">.</span><span class="mi">00</span> <span class="n">sec</span><span class="p">)</span>
</code></pre></div></div>

<p>Use the following command in MySQL version 8.4.</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> <span class="k">SHOW</span> <span class="nb">BINARY</span> <span class="n">LOG</span> <span class="n">STATUS</span> <span class="err">\</span><span class="k">G</span>
<span class="o">***************************</span> <span class="mi">1</span><span class="p">.</span> <span class="k">row</span> <span class="o">***************************</span>
<span class="n">File</span><span class="p">:</span> <span class="n">mysql</span><span class="o">-</span><span class="n">bin</span><span class="p">.</span><span class="mi">000002</span>
<span class="k">Position</span><span class="p">:</span> <span class="mi">798</span>
<span class="n">Binlog_Do_DB</span><span class="p">:</span>
<span class="n">Binlog_Ignore_DB</span><span class="p">:</span>
<span class="n">Executed_Gtid_Set</span><span class="p">:</span> <span class="n">b6a6daa5</span><span class="o">-</span><span class="mi">71</span><span class="n">a5</span><span class="o">-</span><span class="mi">11</span><span class="n">f0</span><span class="o">-</span><span class="mi">91</span><span class="n">cb</span><span class="o">-</span><span class="mi">005056994</span><span class="n">dcc</span><span class="p">:</span><span class="mi">1</span><span class="o">-</span><span class="mi">2</span>
<span class="mi">1</span> <span class="k">row</span> <span class="k">in</span> <span class="k">set</span> <span class="p">(</span><span class="mi">0</span><span class="p">.</span><span class="mi">00</span> <span class="n">sec</span><span class="p">)</span>
</code></pre></div></div>

<p>Grant <code class="language-plaintext highlighter-rouge">replica_user</code> master-slave replication permissions.</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> <span class="k">GRANT</span> <span class="n">REPLICATION</span> <span class="n">SLAVE</span><span class="p">,</span> <span class="n">REPLICATION</span> <span class="n">CLIENT</span> <span class="k">ON</span> <span class="o">*</span><span class="p">.</span><span class="o">*</span> <span class="k">TO</span> <span class="s1">'replica_user'</span><span class="o">@</span><span class="s1">'%'</span><span class="p">;</span>
</code></pre></div></div>

<h4 id="heading-623-check-the-mysql-database-standby-slave-settings">6.2.3. Check the MySQL database standby (slave) settings.</h4>

<p>Check that the configuration items <code class="language-plaintext highlighter-rouge">bin-log</code> and <code class="language-plaintext highlighter-rouge">server-id</code> in <code class="language-plaintext highlighter-rouge">my.cnf</code> are enabled and their values are not empty. The <code class="language-plaintext highlighter-rouge">server-id</code> of the MySQL backup machine in this test is set to <code class="language-plaintext highlighter-rouge">25566</code>. In actual deployment, the setting can be adjusted according to the situation; generally, <code class="language-plaintext highlighter-rouge">server-id</code> is set to the last part of the server IP address. It is important to note that the <code class="language-plaintext highlighter-rouge">server-id</code> of the MySQL database host and the database backup machine (slave) must not be the same; otherwise, the database backup machine will be unable to connect to the host. If the slave node is only used as a database backup and there is no intention to switch it to the master, then bin-log logging is not required; otherwise, bin-log logging should be enabled for both the master and slave. Log in to the MySQL server on the slave machine. First, stop all master-slave replication operations. For MySQL 8.0 and above, use the following statement.</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> <span class="n">STOP</span> <span class="n">REPLICA</span><span class="p">;</span>
</code></pre></div></div>

<p>For versions of MySQL below 8.0, use the following statement.</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> <span class="n">STOP</span> <span class="n">SLAVE</span><span class="p">;</span>
</code></pre></div></div>

<p>Then, configure the MySQL master database address and account settings on the standby MySQL database. Use the following command in MySQL 8.1. The arrow "<code class="language-plaintext highlighter-rouge">-&gt;</code>" indicates a newline prompt; you don't need to copy it while typing, as it will appear automatically when you press Enter.</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> <span class="n">CHANGE</span> <span class="n">MASTER</span> <span class="k">TO</span> <span class="n">master_host</span><span class="o">=</span><span class="s1">'10.91.134.135'</span><span class="p">,</span>
<span class="o">-&gt;</span> <span class="n">master_port</span><span class="o">=</span><span class="mi">3366</span><span class="p">,</span>
<span class="o">-&gt;</span> <span class="n">master_user</span><span class="o">=</span><span class="s1">'replica_user'</span><span class="p">,</span>
<span class="o">-&gt;</span> <span class="n">master_password</span><span class="o">=</span><span class="s1">'newpassword'</span><span class="p">,</span>
<span class="o">-&gt;</span> <span class="n">master_log_file</span><span class="o">=</span><span class="s1">'mysql-bin.000001'</span><span class="p">,</span>
<span class="o">-&gt;</span> <span class="n">master_log_pos</span><span class="o">=</span><span class="mi">157</span><span class="p">,</span>
<span class="o">-&gt;</span> <span class="n">get_master_public_key</span><span class="o">=</span><span class="mi">1</span><span class="p">;</span>
</code></pre></div></div>

<p>In this configuration, <code class="language-plaintext highlighter-rouge">master_host</code> should be set to the IP address of the machine hosting the primary database, and the secondary database should be able to access this address. <code class="language-plaintext highlighter-rouge">master_port</code> should be set to the port used by the primary MySQL database. <code class="language-plaintext highlighter-rouge">master_user</code> should be set to the username created in the previous section for master-slave replication; in this example, it's <code class="language-plaintext highlighter-rouge">replica_user</code>. <code class="language-plaintext highlighter-rouge">master_password</code> should be set to the password for the master-slave replication account. <code class="language-plaintext highlighter-rouge">master_log_file</code> and <code class="language-plaintext highlighter-rouge">master_log_pos</code> should be filled in according to the host's status output. In MySQL 8.0 and later versions, the <code class="language-plaintext highlighter-rouge">get_master_public_key</code> option needs to be enabled; see the Troubleshooting section for details. After completion, restart master-slave replication. For MySQL 8.4 and later versions, the following command should be used.</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> <span class="n">CHANGE</span> <span class="n">REPLICATION</span> <span class="k">SOURCE</span> <span class="k">TO</span>
<span class="o">-&gt;</span> <span class="n">source_host</span><span class="o">=</span><span class="s1">'10.91.134.135'</span><span class="p">,</span>
<span class="o">-&gt;</span> <span class="n">source_port</span><span class="o">=</span><span class="mi">3366</span><span class="p">,</span>
<span class="o">-&gt;</span> <span class="n">source_user</span><span class="o">=</span><span class="s1">'replica_user'</span><span class="p">,</span>
<span class="o">-&gt;</span> <span class="n">source_password</span><span class="o">=</span><span class="s1">'newpassword'</span><span class="p">,</span>
<span class="o">-&gt;</span> <span class="n">source_log_file</span><span class="o">=</span><span class="s1">'mysql-bin.000001'</span><span class="p">,</span>
<span class="o">-&gt;</span> <span class="n">source_log_pos</span><span class="o">=</span><span class="mi">1046</span><span class="p">,</span>
<span class="o">-&gt;</span> <span class="n">get_source_public_key</span><span class="o">=</span><span class="mi">1</span><span class="p">;</span>
</code></pre></div></div>

<p>In MySQL 8.0 and above:</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> <span class="k">START</span> <span class="n">REPLICA</span><span class="p">;</span>
</code></pre></div></div>

<p>Or in versions of MySQL below 8.0:</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> <span class="k">START</span> <span class="n">SLAVE</span><span class="p">;</span>
</code></pre></div></div>

<p>Check the primary/standby connection status by entering the following information on the standby machine.</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> <span class="k">SHOW</span> <span class="n">REPLICA</span> <span class="n">STATUS</span> <span class="err">\</span><span class="k">G</span>
<span class="o">***************************</span> <span class="mi">1</span><span class="p">.</span> <span class="k">row</span> <span class="o">***************************</span>
<span class="n">Replica_IO_State</span><span class="p">:</span> <span class="n">Waiting</span> <span class="k">for</span> <span class="k">source</span> <span class="k">to</span> <span class="n">send</span> <span class="n">event</span>
<span class="n">Source_Host</span><span class="p">:</span> <span class="mi">10</span><span class="p">.</span><span class="mi">91</span><span class="p">.</span><span class="mi">134</span><span class="p">.</span><span class="mi">135</span>
<span class="n">Source_User</span><span class="p">:</span> <span class="n">replica_user</span>
<span class="n">Source_Port</span><span class="p">:</span> <span class="mi">3366</span>
<span class="n">Connect_Retry</span><span class="p">:</span> <span class="mi">60</span>
<span class="n">Source_Log_File</span><span class="p">:</span> <span class="n">mysql</span><span class="o">-</span><span class="n">bin</span><span class="p">.</span><span class="mi">000001</span>
<span class="n">Read_Source_Log_Pos</span><span class="p">:</span> <span class="mi">351</span>
<span class="n">Relay_Log_File</span><span class="p">:</span> <span class="n">mysql</span><span class="o">-</span><span class="n">relay</span><span class="o">-</span><span class="n">bin</span><span class="p">.</span><span class="mi">000002</span>
<span class="n">Relay_Log_Pos</span><span class="p">:</span> <span class="mi">520</span>
<span class="n">Relay_Source_Log_File</span><span class="p">:</span> <span class="n">mysql</span><span class="o">-</span><span class="n">bin</span><span class="p">.</span><span class="mi">000001</span>
<span class="n">Replica_IO_Running</span><span class="p">:</span> <span class="n">Yes</span>
<span class="n">Replica_SQL_Running</span><span class="p">:</span> <span class="n">Yes</span>
<span class="n">Replicate_Do_DB</span><span class="p">:</span>
<span class="n">Replicate_Ignore_DB</span><span class="p">:</span>
<span class="n">Replicate_Do_Table</span><span class="p">:</span>
<span class="n">Replicate_Ignore_Table</span><span class="p">:</span>
<span class="n">Replicate_Wild_Do_Table</span><span class="p">:</span>
<span class="n">Replicate_Wild_Ignore_Table</span><span class="p">:</span>
<span class="n">Last_Errno</span><span class="p">:</span> <span class="mi">0</span>
<span class="n">Last_Error</span><span class="p">:</span>
<span class="n">Skip_Counter</span><span class="p">:</span> <span class="mi">0</span>
<span class="n">Exec_Source_Log_Pos</span><span class="p">:</span> <span class="mi">351</span>
<span class="n">Relay_Log_Space</span><span class="p">:</span> <span class="mi">730</span>
<span class="n">Until_Condition</span><span class="p">:</span> <span class="k">None</span>
<span class="n">Until_Log_File</span><span class="p">:</span>
<span class="n">Until_Log_Pos</span><span class="p">:</span> <span class="mi">0</span>
<span class="n">Source_SSL_Allowed</span><span class="p">:</span> <span class="k">No</span>
<span class="n">Source_SSL_CA_File</span><span class="p">:</span>
<span class="n">Source_SSL_CA_Path</span><span class="p">:</span>
<span class="n">Source_SSL_Cert</span><span class="p">:</span>
<span class="n">Source_SSL_Cipher</span><span class="p">:</span>
<span class="n">Source_SSL_Key</span><span class="p">:</span>
<span class="n">Seconds_Behind_Source</span><span class="p">:</span> <span class="mi">0</span>
<span class="n">Source_SSL_Verify_Server_Cert</span><span class="p">:</span> <span class="k">No</span>
<span class="n">Last_IO_Errno</span><span class="p">:</span> <span class="mi">0</span>
<span class="n">Last_IO_Error</span><span class="p">:</span>
<span class="n">Last_SQL_Errno</span><span class="p">:</span> <span class="mi">0</span>
<span class="n">Last_SQL_Error</span><span class="p">:</span>
<span class="n">Replicate_Ignore_Server_Ids</span><span class="p">:</span>
<span class="n">Source_Server_Id</span><span class="p">:</span> <span class="mi">22567</span>
<span class="n">Source_UUID</span><span class="p">:</span> <span class="mi">4</span><span class="n">f6e1086</span><span class="o">-</span><span class="mi">6852</span><span class="o">-</span><span class="mi">11</span><span class="n">f0</span><span class="o">-</span><span class="n">aa5f</span><span class="o">-</span><span class="mi">005056994</span><span class="n">dcc</span>
<span class="n">Source_Info_File</span><span class="p">:</span> <span class="n">mysql</span><span class="p">.</span><span class="n">slave_master_info</span>
<span class="n">SQL_Delay</span><span class="p">:</span> <span class="mi">0</span>
<span class="n">SQL_Remaining_Delay</span><span class="p">:</span> <span class="k">NULL</span>
<span class="n">Replica_SQL_Running_State</span><span class="p">:</span> <span class="n">Replica</span> <span class="n">has</span> <span class="k">read</span> <span class="k">all</span> <span class="n">relay</span> <span class="n">log</span><span class="p">;</span> <span class="n">waiting</span> <span class="k">for</span> <span class="k">more</span> <span class="n">updates</span>
<span class="n">Source_Retry_Count</span><span class="p">:</span> <span class="mi">10</span>
<span class="n">Source_Bind</span><span class="p">:</span>
<span class="n">Last_IO_Error_Timestamp</span><span class="p">:</span>
<span class="n">Last_SQL_Error_Timestamp</span><span class="p">:</span>
<span class="n">Source_SSL_Crl</span><span class="p">:</span>
<span class="n">Source_SSL_Crlpath</span><span class="p">:</span>
<span class="n">Retrieved_Gtid_Set</span><span class="p">:</span> <span class="mi">4</span><span class="n">f6e1086</span><span class="o">-</span><span class="mi">6852</span><span class="o">-</span><span class="mi">11</span><span class="n">f0</span><span class="o">-</span><span class="n">aa5f</span><span class="o">-</span><span class="mi">005056994</span><span class="n">dcc</span><span class="p">:</span><span class="mi">1</span>
<span class="n">Executed_Gtid_Set</span><span class="p">:</span> <span class="mi">4</span><span class="n">f6e1086</span><span class="o">-</span><span class="mi">6852</span><span class="o">-</span><span class="mi">11</span><span class="n">f0</span><span class="o">-</span><span class="n">aa5f</span><span class="o">-</span><span class="mi">005056994</span><span class="n">dcc</span><span class="p">:</span><span class="mi">1</span><span class="p">,</span>
<span class="mi">87</span><span class="n">ce2877</span><span class="o">-</span><span class="mi">692</span><span class="n">d</span><span class="o">-</span><span class="mi">11</span><span class="n">f0</span><span class="o">-</span><span class="mi">8354</span><span class="o">-</span><span class="mi">0050569982</span><span class="n">d7</span><span class="p">:</span><span class="mi">1</span><span class="o">-</span><span class="mi">3</span>
<span class="n">Auto_Position</span><span class="p">:</span> <span class="mi">0</span>
<span class="n">Replicate_Rewrite_DB</span><span class="p">:</span>
<span class="n">Channel_Name</span><span class="p">:</span>
<span class="n">Source_TLS_Version</span><span class="p">:</span>
<span class="n">Source_public_key_path</span><span class="p">:</span>
<span class="n">Get_Source_public_key</span><span class="p">:</span> <span class="mi">1</span>
<span class="n">Network_Namespace</span><span class="p">:</span>
<span class="mi">1</span> <span class="k">row</span> <span class="k">in</span> <span class="k">set</span> <span class="p">(</span><span class="mi">0</span><span class="p">.</span><span class="mi">00</span> <span class="n">sec</span><span class="p">)</span>
</code></pre></div></div>

<p>If both <code class="language-plaintext highlighter-rouge">Replica_IO_Running</code> and <code class="language-plaintext highlighter-rouge">Replica_SQL_Running</code> show as <code class="language-plaintext highlighter-rouge">Yes</code>, it means that MySQL master-slave replication is running normally.</p>

<h4 id="heading-624-testing-master-slave-replication-function">6.2.4. Testing master-slave replication function</h4>

<p>Check if the databases in the current primary and standby databases are consistent, and view the current database.</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> <span class="k">SHOW</span> <span class="n">DATABASES</span><span class="p">;</span>
<span class="o">+</span><span class="c1">--------------------+</span>
<span class="o">|</span> <span class="k">Database</span>           <span class="o">|</span>
<span class="o">+</span><span class="c1">--------------------+</span>
<span class="o">|</span> <span class="n">information_schema</span> <span class="o">|</span>
<span class="o">|</span> <span class="n">mysql</span>              <span class="o">|</span>
<span class="o">|</span> <span class="n">performance_schema</span> <span class="o">|</span>
<span class="o">|</span> <span class="n">sys</span>                <span class="o">|</span>
<span class="o">+</span><span class="c1">--------------------+</span>
<span class="mi">4</span> <span class="k">rows</span> <span class="k">in</span> <span class="k">set</span> <span class="p">(</span><span class="mi">0</span><span class="p">.</span><span class="mi">01</span> <span class="n">sec</span><span class="p">)</span>
</code></pre></div></div>

<p>Create a new test database in the main database, named <code class="language-plaintext highlighter-rouge">dbtest1</code> in this example.</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> <span class="k">CREATE</span> <span class="k">DATABASE</span> <span class="n">dbtest1</span><span class="p">;</span>
</code></pre></div></div>

<p>Check if the database is included in the primary database.</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> <span class="k">SHOW</span> <span class="n">DATABASES</span><span class="p">;</span>
<span class="o">+</span><span class="c1">--------------------+</span>
<span class="o">|</span> <span class="k">Database</span>           <span class="o">|</span>
<span class="o">+</span><span class="c1">--------------------+</span>
<span class="o">|</span> <span class="n">dbtest1</span>            <span class="o">|</span>
<span class="o">|</span> <span class="n">information_schema</span> <span class="o">|</span>
<span class="o">|</span> <span class="n">mysql</span>              <span class="o">|</span>
<span class="o">|</span> <span class="n">performance_schema</span> <span class="o">|</span>
<span class="o">|</span> <span class="n">sys</span>                <span class="o">|</span>
<span class="o">+</span><span class="c1">--------------------+</span>
<span class="mi">5</span> <span class="k">rows</span> <span class="k">in</span> <span class="k">set</span> <span class="p">(</span><span class="mi">0</span><span class="p">.</span><span class="mi">00</span> <span class="n">sec</span><span class="p">)</span>
</code></pre></div></div>

<p>Check if the backup database is synchronized.</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> <span class="k">SHOW</span> <span class="n">DATABASES</span><span class="p">;</span>
<span class="o">+</span><span class="c1">--------------------+</span>
<span class="o">|</span> <span class="k">Database</span>           <span class="o">|</span>
<span class="o">+</span><span class="c1">--------------------+</span>
<span class="o">|</span> <span class="n">dbtest1</span>            <span class="o">|</span>
<span class="o">|</span> <span class="n">information_schema</span> <span class="o">|</span>
<span class="o">|</span> <span class="n">mysql</span>              <span class="o">|</span>
<span class="o">|</span> <span class="n">performance_schema</span> <span class="o">|</span>
<span class="o">|</span> <span class="n">sys</span>                <span class="o">|</span>
<span class="o">+</span><span class="c1">--------------------+</span>
<span class="mi">5</span> <span class="k">rows</span> <span class="k">in</span> <span class="k">set</span> <span class="p">(</span><span class="mi">0</span><span class="p">.</span><span class="mi">00</span> <span class="n">sec</span><span class="p">)</span>
</code></pre></div></div>

<p>It can be confirmed that the newly created database has been replicated from the primary database to the standby database. After confirming that the primary-standby synchronization and replication are normal, remove the test database and tables.</p>

<h3 id="heading-63-two-host-architecture">6.3. Two-host Architecture</h3>

<p>To achieve a master-slave architecture where the two MySQL servers act as each other's master and backup, and operations on either MySQL server are synchronized across both servers, master-slave replication needs to be configured on both MySQL servers.</p>

<p>For use in MySQL 8.4 and above:</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> <span class="k">SHOW</span> <span class="nb">BINARY</span> <span class="n">LOG</span> <span class="n">STATUS</span> <span class="err">\</span><span class="k">G</span>
</code></pre></div></div>

<p>Used in MySQL versions 8.1 and below:</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> <span class="k">SHOW</span> <span class="n">MASTER</span> <span class="n">STATUS</span> <span class="err">\</span><span class="k">G</span>
</code></pre></div></div>

<p>The following information can be obtained.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>*************************** 1. row ***************************
File: mysql-bin.000003
Position: 4006
Binlog_Do_DB:
Binlog_Ignore_DB:
Executed_Gtid_Set: b6a6daa5-71a5-11f0-91cb-005056994dcc:8-18,
e3cbf8a0-71a6-11f0-8381-0050569982d7:1-4
1 row in set (0.00 sec)
</code></pre></div></div>

<p>Create a new account on <code class="language-plaintext highlighter-rouge">t2</code> specifically for master-slave replication on <code class="language-plaintext highlighter-rouge">t1</code>, following the same procedure as creating a replication-dedicated account on <code class="language-plaintext highlighter-rouge">t1</code>. Log in to the MySQL console on <code class="language-plaintext highlighter-rouge">t2</code>.</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> <span class="k">CREATE</span> <span class="k">USER</span> <span class="s1">'replica_user_2'</span><span class="o">@</span><span class="s1">'%'</span> <span class="n">IDENTIFIED</span> <span class="k">BY</span> <span class="s1">'newpassword'</span><span class="p">;</span>
</code></pre></div></div>

<p>Then grant the new user <code class="language-plaintext highlighter-rouge">replica_user_2</code> the permissions related to master-slave replication.</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> <span class="k">GRANT</span> <span class="n">REPLICATION</span> <span class="n">SLAVE</span><span class="p">,</span> <span class="n">REPLICATION</span> <span class="n">CLIENT</span> <span class="k">ON</span> <span class="o">*</span><span class="p">.</span><span class="o">*</span> <span class="k">TO</span> <span class="s1">'replica_user_2'</span><span class="o">@</span><span class="s1">'%'</span><span class="p">;</span>
</code></pre></div></div>

<p>Then log in to MySQL on <code class="language-plaintext highlighter-rouge">t1</code> and first stop master-slave replication on <code class="language-plaintext highlighter-rouge">t1</code>.</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> <span class="n">STOP</span> <span class="n">REPLICA</span><span class="p">;</span>
</code></pre></div></div>

<p>Then, configure the information for <code class="language-plaintext highlighter-rouge">t2</code> in <code class="language-plaintext highlighter-rouge">t1</code>. Enter the IP address of <code class="language-plaintext highlighter-rouge">t2</code> in <code class="language-plaintext highlighter-rouge">source_host</code>; enter the MySQL service port of <code class="language-plaintext highlighter-rouge">t2</code> in <code class="language-plaintext highlighter-rouge">source_port</code>; enter the username created on <code class="language-plaintext highlighter-rouge">t2</code> for master-slave replication (in this example, <code class="language-plaintext highlighter-rouge">replica_user_2</code>); enter the password corresponding to <code class="language-plaintext highlighter-rouge">source_user</code>; enter the name of the corresponding binary log file on <code class="language-plaintext highlighter-rouge">t2</code> (in this example, <code class="language-plaintext highlighter-rouge">mysql-bin.000003</code>); enter the position of the binary log (e.g., <code class="language-plaintext highlighter-rouge">3012</code> in this example); finally, set <code class="language-plaintext highlighter-rouge">get_source_public_key</code> to <code class="language-plaintext highlighter-rouge">1</code> to retrieve the public key from the host.</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> <span class="n">CHANGE</span> <span class="n">REPLICATION</span> <span class="k">SOURCE</span> <span class="k">TO</span>
<span class="o">-&gt;</span> <span class="n">source_host</span><span class="o">=</span><span class="s1">'10.91.134.136'</span><span class="p">,</span>
<span class="o">-&gt;</span> <span class="n">source_port</span><span class="o">=</span><span class="mi">3366</span><span class="p">,</span>
<span class="o">-&gt;</span> <span class="n">source_user</span><span class="o">=</span><span class="s1">'replica_user_2'</span><span class="p">,</span>
<span class="o">-&gt;</span> <span class="n">source_password</span><span class="o">=</span><span class="s1">'newpassword'</span><span class="p">,</span>
<span class="o">-&gt;</span> <span class="n">source_log_file</span><span class="o">=</span><span class="s1">'mysql-bin.000003'</span><span class="p">,</span>
<span class="o">-&gt;</span> <span class="n">source_log_pos</span><span class="o">=</span><span class="mi">3012</span><span class="p">,</span>
<span class="o">-&gt;</span> <span class="n">get_source_public_key</span><span class="o">=</span><span class="mi">1</span><span class="p">;</span>
</code></pre></div></div>

<p>Once completed, start master-slave replication on <code class="language-plaintext highlighter-rouge">t1</code>.</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> <span class="k">START</span> <span class="n">REPLICA</span><span class="p">;</span>
</code></pre></div></div>

<p>Check the master-slave replication status of <code class="language-plaintext highlighter-rouge">t1</code>.</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> <span class="k">SHOW</span> <span class="n">REPLICA</span> <span class="n">STATUS</span> <span class="err">\</span><span class="k">G</span>
<span class="p">...</span>
<span class="n">Replica_IO_Running</span><span class="p">:</span> <span class="n">Yes</span>
<span class="n">Replica_SQL_Running</span><span class="p">:</span> <span class="n">Yes</span>
<span class="p">...</span>
</code></pre></div></div>

<p>Master-slave replication is working normally. When a write operation is performed on machine <code class="language-plaintext highlighter-rouge">t2</code>, machine <code class="language-plaintext highlighter-rouge">t1</code> can synchronize and modify the data normally.</p>

<h2 id="heading-7-backup-and-restore--備份及恢復">7. Backup and Restore | 備份及恢復</h2>

<h3 id="heading-71-physical-backup-original-backup">7.1. Physical Backup (Original Backup)</h3>

<p>Physical backups consist of original copies of the directories and files storing the database content. This type of backup is suitable for large or critical databases that require rapid recovery in the event of problems. Physical backups are faster than logical backups because they involve only file copying operations and do not require database transformation. In addition to the database itself, physical backups can include any related files such as logs or configuration files. Physical backup files can only be migrated to other machines with the same or similar hardware characteristics. The granularity of physical backups ranges from the entire data directory level to the individual file level, but may not necessarily offer table-level granularity, depending on the storage engine used.</p>

<h4 id="heading-711-cold-backup">7.1.1. Cold backup</h4>

<p>Perform backups while the database is completely shut down. The advantages of this method are high data consistency and fast backup and recovery speeds. However, the disadvantage is that it requires system downtime, which can significantly impact business operations. Alternatively, if conditions allow for a cold backup, disconnect the application from the database, stop reading and writing data, and shut down the MySQL database.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># service mysqld stop
</code></pre></div></div>

<p>A properly functioning MySQL database stores data in the <code class="language-plaintext highlighter-rouge">/data/mysql</code> directory and its configuration file in <code class="language-plaintext highlighter-rouge">/etc/my.cnf</code>. Therefore, a cold backup only needs to back up these two directories. Create a new directory <code class="language-plaintext highlighter-rouge">/backup/offline</code> under <code class="language-plaintext highlighter-rouge">/data</code> to store the backup files for the cold backup.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># mkdir -p /data/backup/offline
</code></pre></div></div>

<p>Copy the entire <code class="language-plaintext highlighter-rouge">/data/mysql</code> directory to the <code class="language-plaintext highlighter-rouge">/data/backup/offline</code> directory, and rename it, adding the backup time. For example:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># cp -r /data/mysql /data/backup/offline/backup_data_2025_08_04
</code></pre></div></div>

<p>Next, copy the entire my.cnf configuration file to the <code class="language-plaintext highlighter-rouge">mysql_backup_offline</code> directory, rename it, and add the backup time. For example:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># cp /etc/my.cnf /data/backup/offline/backup_config_2025_08_04.cnf
</code></pre></div></div>

<p>For specific, recurring database backup operations, a script named <code class="language-plaintext highlighter-rouge">mysql_backup_offline.sh</code> can be created to perform a cold backup of the entire database. The shell script is as follows.</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">backup_date</span><span class="o">=</span><span class="si">$(</span><span class="nb">date</span> <span class="s2">"+%Y_%m_%d"</span><span class="si">)</span>
<span class="nv">backup_folder</span><span class="o">=</span>/data/backup/offline
<span class="nv">data_dir_name</span><span class="o">=</span><span class="s2">"backup_data_</span><span class="k">${</span><span class="nv">backup_date</span><span class="k">}</span><span class="s2">"</span>
<span class="nv">config_name</span><span class="o">=</span><span class="s2">"backup_config_</span><span class="k">${</span><span class="nv">backup_date</span><span class="k">}</span><span class="s2">"</span>
<span class="nb">echo</span> <span class="s2">"[ INFO ] Starting offline backup process..."</span>
<span class="nb">echo</span> <span class="s2">"[ INFO ] Stoping MySQL service..."</span>
service mysqld stop
service mysqld status
<span class="nb">echo</span> <span class="s2">"[ INFO ] MySQL service stopped."</span>
<span class="nb">echo</span> <span class="s2">"[ INFO ] Backup databases..."</span>
<span class="nb">cp</span> <span class="nt">-r</span> /data/mysql <span class="k">${</span><span class="nv">backup_folder</span><span class="k">}</span>/backup_data_<span class="k">${</span><span class="nv">backup_date</span><span class="k">}</span>
<span class="nb">tar</span> <span class="nt">-zcvPf</span> <span class="k">${</span><span class="nv">backup_folder</span><span class="k">}</span>/backup_data_<span class="k">${</span><span class="nv">backup_date</span><span class="k">}</span>.tar.gz <span class="k">${</span><span class="nv">backup_folder</span><span class="k">}</span>/backup_data_<span class="k">${</span><span class="nv">backup_date</span><span class="k">}</span>
<span class="nb">echo</span> <span class="s2">"[ INFO ] Backup configuration..."</span>
<span class="nb">cp</span> /etc/my.cnf <span class="k">${</span><span class="nv">backup_folder</span><span class="k">}</span>/backup_data_<span class="k">${</span><span class="nv">backup_date</span><span class="k">}</span>
<span class="nb">echo</span> <span class="s2">"[ INFO ] Finished."</span>
</code></pre></div></div>

<p>When a cold backup of the database is required, the script will automatically perform a cold backup of the database and name the backup file.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># cd /usr/local
# sh mysql_backup_offline.sh
</code></pre></div></div>

<p>Restart the MySQL service after the cold backup is complete.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># service mysqld start
</code></pre></div></div>

<h4 id="heading-712-hot-backup">7.1.2. Hot backup</h4>

<p>Performing backups while the database is running will not affect its normal operation. However, using a hot backup strategy on a MySQL database requires additional backup tools, such as the open-source database backup tool Percona XtraBackup. Use wget to pull the corresponding version of the XtraBackup binary package (rpm format) from the XtraBackup website and place it in the <code class="language-plaintext highlighter-rouge">/usr/local</code> directory.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># cd /usr/local
# wget "https://downloads.percona.com/downloads/Percona-XtraBackup-8.4/Percona-XtraBackup-8.4.0-1/binary/redhat/7/x86_64/percona-xtrabackup-84-8.4.0-1.1.el7.x86_64.rpm"
</code></pre></div></div>

<p>Install the XtraBackup package.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># yum localinstall percona-xtrabackup-80-8.0.32-26.1.el7.x86_64.rpm
</code></pre></div></div>

<p>If you are prompted that <code class="language-plaintext highlighter-rouge">libev.so.4</code> is missing, you need to install <code class="language-plaintext highlighter-rouge">libev</code>, a dependency required by XtraBackup.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># yum localinstall libev-4.04-2.el6.x86_64.rpm
</code></pre></div></div>

<p>If you are prompted that <code class="language-plaintext highlighter-rouge">openssl</code> is missing, you need to install the OpenSSL dependency required by XtraBackup.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># yum install openssl
</code></pre></div></div>

<p>If you are prompted that <code class="language-plaintext highlighter-rouge">zstd</code> is missing, you need to install Zstandard (usually abbreviated as zstd), a dependency required by XtraBackup. If <code class="language-plaintext highlighter-rouge">zstd</code> is available in the package manager's repository, you can install it directly through the package manager.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># yum install zstd
</code></pre></div></div>

<p>If the <code class="language-plaintext highlighter-rouge">zstd</code> package is not included in the <code class="language-plaintext highlighter-rouge">yum</code> repository, you can pull the source package from the <code class="language-plaintext highlighter-rouge">zstd</code> project website and compile it for installation, or pull the RPM package from another reliable mirror server to your local machine for installation. Place the <code class="language-plaintext highlighter-rouge">zstd</code> RPM package in the <code class="language-plaintext highlighter-rouge">/usr/local</code> directory.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># rpm -ivh zstd-1.4.0-1.el6.x86_64.rpm
</code></pre></div></div>

<p>Alternatively, you can use <code class="language-plaintext highlighter-rouge">yum</code> to install locally.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># yum localinstall zstd-1.4.0-1.el6.x86_64.rpm
</code></pre></div></div>

<p>After installing XtraBackup, use the command to verify that the version of the current backup tool matches the version of the running MySQL service.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># xtrabackup --version
xtrabackup version 8.4.0-1 based on MySQL server 8.4.0 Linux (x86_64) (revision id: da6e1abd)
</code></pre></div></div>

<p>For databases that require regular hot backups, the following script can be used to simplify the commands entered each time, automatically name the backup files according to a predetermined method, and avoid backup file name conflicts.</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">echo</span> <span class="s2">"[ INFO ] Starting XtraBackup..."</span>
<span class="nv">current_date</span><span class="o">=</span><span class="si">$(</span><span class="nb">date</span> <span class="s2">"+%Y_%m_%d"</span><span class="si">)</span>
<span class="nv">backup_dir</span><span class="o">=</span>/data/backup/xtrabackup
xtrabackup <span class="nt">--backup</span> <span class="nt">--slave-info</span> <span class="nt">-u</span> root <span class="nt">-H</span> localhost <span class="nt">-P3366</span> <span class="nt">-p</span> <span class="nt">--target-dir</span><span class="o">=</span><span class="k">${</span><span class="nv">backup_dir</span><span class="k">}</span>/backup_<span class="k">${</span><span class="nv">current_date</span><span class="k">}</span> 2&gt; <span class="k">${</span><span class="nv">backup_dir</span><span class="k">}</span>/backup_<span class="k">${</span><span class="nv">current_date</span><span class="k">}</span>.log
<span class="nb">echo</span> <span class="s2">"[ INFO ] Compressing..."</span>
<span class="nb">tar</span> <span class="nt">-zcvPf</span> <span class="k">${</span><span class="nv">backup_dir</span><span class="k">}</span>/backup_<span class="k">${</span><span class="nv">current_date</span><span class="k">}</span>.tar.gz <span class="k">${</span><span class="nv">backup_dir</span><span class="k">}</span>/backup_<span class="k">${</span><span class="nv">current_date</span><span class="k">}</span>/
<span class="nb">echo</span> <span class="s2">"[ INFO ] XtraBackup done."</span>
</code></pre></div></div>

<h3 id="heading-72-logical-backup">7.2. Logical Backup</h3>

<p>The <code class="language-plaintext highlighter-rouge">mysqldump</code> tool can be used to back up an entire database or a single table. For a MySQL server installed in the <code class="language-plaintext highlighter-rouge">/usr/local/mysql</code> directory, use the following command to start the <code class="language-plaintext highlighter-rouge">mysqldump</code> backup.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># /usr/local/mysql/bin/mysqldump -u root -p --lock-all-tables --flush-logs --set-gtid-purged=off database_name &gt; backup_name.sql
</code></pre></div></div>

<p>Set the backup location for the MySQL database to the <code class="language-plaintext highlighter-rouge">dump</code> folder under the <code class="language-plaintext highlighter-rouge">/data/backup</code> directory. Name the backup file as follows: <code class="language-plaintext highlighter-rouge">dump_database_name_yyyymmddHHMM.sql</code>, for example, <code class="language-plaintext highlighter-rouge">dump_dbtest1_202507311541.sql</code> indicates that this backup is for the database <code class="language-plaintext highlighter-rouge">dbtest1</code>, created on July 31, 2025. Include the backup creation date in the filename to prevent duplicate backup file names. For regular logical backups of a specific database, the following script can simplify the commands entered each time.</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">current_date</span><span class="o">=</span><span class="si">$(</span><span class="nb">date</span> <span class="s2">"+%Y%m%d%H%M"</span><span class="si">)</span>
<span class="nv">backup_folder</span><span class="o">=</span>/data/backup/dump
<span class="nv">username</span><span class="o">=</span><span class="s2">"root"</span>
<span class="nv">db_name</span><span class="o">=</span><span class="s2">"db1"</span>
<span class="nv">file_name</span><span class="o">=</span><span class="s2">"dump_</span><span class="k">${</span><span class="nv">db_name</span><span class="k">}</span><span class="s2">_</span><span class="k">${</span><span class="nv">current_date</span><span class="k">}</span><span class="s2">.sql"</span>
<span class="nv">backup_dir_file_name</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">backup_folder</span><span class="k">}</span><span class="s2">/</span><span class="k">${</span><span class="nv">file_name</span><span class="k">}</span><span class="s2">"</span>
<span class="nb">echo</span> <span class="s2">"[ INFO ] Starting backup MySQL database </span><span class="k">${</span><span class="nv">db_name</span><span class="k">}</span><span class="s2"> at </span><span class="k">${</span><span class="nv">current_date</span><span class="k">}</span><span class="s2">..."</span>
/usr/local/mysql/bin/mysqldump <span class="nt">-u</span><span class="k">${</span><span class="nv">username</span><span class="k">}</span> <span class="nt">-p</span> <span class="nt">--lock-all-tables</span> <span class="nt">--flush-logs</span> <span class="nt">--set-gtid-purged</span><span class="o">=</span>off <span class="k">${</span><span class="nv">db_name</span><span class="k">}</span> <span class="o">&gt;</span> <span class="k">${</span><span class="nv">backup_dir_file_name</span><span class="k">}</span>
<span class="nb">cd</span> <span class="k">${</span><span class="nv">backup_folder</span><span class="k">}</span>
<span class="nb">tar</span> <span class="nt">-zcvPf</span> <span class="k">${</span><span class="nv">file_name</span><span class="k">}</span>.tar.gz <span class="k">${</span><span class="nv">file_name</span><span class="k">}</span>
<span class="nv">finish_date</span><span class="o">=</span><span class="si">$(</span><span class="nb">date</span> <span class="s2">"+%Y%m%d%H%M"</span><span class="si">)</span>
<span class="nb">echo</span> <span class="s2">"[ INFO ] Finish backup MySQL database </span><span class="k">${</span><span class="nv">db_name</span><span class="k">}</span><span class="s2"> at </span><span class="k">${</span><span class="nv">finish_date</span><span class="k">}</span><span class="s2">."</span>
</code></pre></div></div>

<h2 id="heading-8-diagnose--問題排查及解決方法">8. Diagnose | 問題排查及解決方法</h2>

<p>つづく…</p>]]></content><author><name>Rain</name></author><category term="Database" /><category term="SQL" /><category term="Documentation" /><summary type="html"><![CDATA[The "Night-Up" Level MySQL Documentation for Database Deploy, Upgrade, Backup and Restore | 「起夜」級 MySQL 數據庫部署、升級、備份及恢復文檔]]></summary></entry><entry><title type="html">Make Mac an RDP Server</title><link href="https://congjyu.github.io/blog/2026/01/24/xrdp/" rel="alternate" type="text/html" title="Make Mac an RDP Server" /><published>2026-01-24T00:00:00+00:00</published><updated>2026-01-24T00:00:00+00:00</updated><id>https://congjyu.github.io/blog/2026/01/24/xrdp</id><content type="html" xml:base="https://congjyu.github.io/blog/2026/01/24/xrdp/"><![CDATA[<h1 id="heading-make-mac-an-rdp-server--使用-rdp-遠程連接-mac-mini">Make Mac an RDP Server | 使用 RDP 遠程連接 Mac mini</h1>

<blockquote>
  <p>⚠️ THIS POST HAS A "靈車漂移" TAG, AND DOES NOT GUARANTEE STABILITY.</p>

  <p>⚠️ 呢篇文章帶有「靈車漂移」之標籤，並且不保證穩定性。</p>
</blockquote>

<h2 id="heading-0-background--motivation--背景及動機">0. Background &amp; Motivation | 背景及動機</h2>

<p>Recently Rain is using a combination of "separated" NAS and remote desktop, that is, an Intel NUC with a mounted external hard disk. It is worth attention that I want to have a user-friendly command line environment as well as a beautiful graphic user interface (GUI), so I installed a macOS Big Sur 11.9 on NUC, also called "Hacintosh". I will called it "Nucintosh" because it is installed on a NUC hardware.</p>

<p>呢排 Rain 用緊一個「散裝」NAS 及遠程桌面結合物，即係一台 Intel NUC，並掛載有一個外置硬碟。值得注意嘅係 Rain 想要一個用戶友好嘅命令行終端環境，又同時想要一個優雅美麗嘅圖形用戶介面，所以選擇喺 NUC 之上安裝 macOS Big Sur 11.9 系統，即係話安裝一個「黑蘋果」。因為佢運行於 NUC 硬件之上，所以喺下文中將稱呼佢為「Nucintosh」。</p>

<p>It is generally acknowledge that macOS use ARD to connect from remote, and it does not support the RDP protocol form Microsoft. Rain wants to make Nucintosh be connected via RDP, so that maybe I can adjust the resolution settings from remote client. (not sure)</p>

<p>一般來講，macOS 係用 ARD 喺遠端連線，而且佢唔支援微軟嘅 RDP 協定。 Rain 想令到 Nucintosh 透過 RDP 連接，咁我可能可以喺遠端客戶端調整解像度嘅設定。（唔確定）</p>

<p>The method is to build an RDP server on macOS and make it run rdp services, and there is an open source solution called <code class="language-plaintext highlighter-rouge">xrdp</code> (<a href="https://github.com/neutrinolabs/xrdp">GitHub repo</a>) but however it does not support macOS officially.</p>

<p>此方法係專門構建一個 RDP 伺服器於 macOS 系統之上，令到佢運行 RDP 服務。而且有一個叫做「<code class="language-plaintext highlighter-rouge">xrdp</code>」嘅開源嘅解決方案，但係佢冇官方嘅 macOS 支援。</p>

<p>According to <a href="https://github.com/neutrinolabs/xrdp/issues/2194">some discussions</a>, there is a way to make Macs run <code class="language-plaintext highlighter-rouge">xrdp</code> and get remote connected.</p>

<p>根據<a href="https://github.com/neutrinolabs/xrdp/issues/2194">一啲討論</a>，有一種方法可以令到 Mac 電腦運行 <code class="language-plaintext highlighter-rouge">xrdp</code> 並使得可以被遠程連接控制。</p>

<h2 id="heading-1-compile-xrdp--編譯-xrdp">1. Compile XRDP | 編譯 XRDP</h2>

<p>I choose <code class="language-plaintext highlighter-rouge">xrdp 0.9.19</code> version for this task. Someone said this version runs well on macOS Monterey. My Nucintosh runs a macOS Big Sur system, which is earlier than macOS Monterey, so it has a large probability to work with <code class="language-plaintext highlighter-rouge">xrdp 0.9.19</code> as well. Let us have a try.</p>

<p>我選擇 <code class="language-plaintext highlighter-rouge">xrdp 0.9.19</code> 呢個版本。有啲人話呢個版本喺 macOS Monterey 上面運行良好。我嘅 Nucintosh 搭載一個 macOS Big Sur 系統，相比於 Monterey 早一個大版本，因此有相當大可能性令到 <code class="language-plaintext highlighter-rouge">xrdp 0.9.19</code> 運行良好。讓我哋試一試。</p>

<h3 id="heading-11-prepare-dependencies--準備依賴">1.1. Prepare dependencies | 準備依賴</h3>

<p>We need these following dependencies:</p>

<p>我哋需要下面呢啲依賴庫：</p>

<ul>
  <li>Xcode commandline tools</li>
  <li>Homebrew</li>
  <li>OpenSSL</li>
  <li>Automake and Autoconfig</li>
  <li>Libtool</li>
  <li>pkgconfig</li>
  <li>nasm</li>
  <li>XQuartz and its libs</li>
</ul>

<p>If the machine do not have Homebrew installed, it is required to install one.</p>

<p>如果機器上面冇安裝 Homebrew，咁應該安裝一個先。</p>

<p>First, get Xcode command line tools ready.</p>

<p>首先，準備好 Xcode 命令行工具（Xcode Commandline Tools）。</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>xcode-select <span class="nt">--install</span>
</code></pre></div></div>

<p>Then install Homebrew.</p>

<p>然後安裝 Homebrew。</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/bin/bash <span class="nt">-c</span> <span class="s2">"</span><span class="si">$(</span>curl <span class="nt">-fsSL</span> https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh<span class="si">)</span><span class="s2">"</span>
</code></pre></div></div>

<p>It is also okay to have the macOS has MacPorts installed, like my Nucintosh, but it is also highly recommend to install Homebrew in this case. Some dependencies seem can only be installed from Homebrew, like XQuartz components.</p>

<p>如果 macOS 已經安裝咗 MacPorts 都冇問題。好似 Nucintosh 一樣既有 MacPorts 又有 Homebrew。但係仍然非常推薦安裝 Homebrew，因為我哋後面需要用到。一啲依賴庫只能夠從 Homebrew 嗰度安裝，例如 XQuartz 嘅組件。</p>

<p>Install <code class="language-plaintext highlighter-rouge">openssh</code>.</p>

<p>安裝 <code class="language-plaintext highlighter-rouge">openssh</code>。</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>brew <span class="nb">install </span>openssh
</code></pre></div></div>

<p>Install <code class="language-plaintext highlighter-rouge">automake</code> and <code class="language-plaintext highlighter-rouge">autoconfig</code>.</p>

<p>安裝 <code class="language-plaintext highlighter-rouge">automake</code> 同埋 <code class="language-plaintext highlighter-rouge">autoconfig</code>。</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>brew <span class="nb">install </span>automake
</code></pre></div></div>

<p>Install <code class="language-plaintext highlighter-rouge">libtool</code>.</p>

<p>安裝 <code class="language-plaintext highlighter-rouge">libtool</code>。</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>brew <span class="nb">install </span>libtool
</code></pre></div></div>

<p>Install <code class="language-plaintext highlighter-rouge">pkgconfig</code>.</p>

<p>安裝 <code class="language-plaintext highlighter-rouge">pkgconfig</code>。</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>brew <span class="nb">install </span>pkgconfig
</code></pre></div></div>

<p>Install <code class="language-plaintext highlighter-rouge">nasm</code>.</p>

<p>安裝 <code class="language-plaintext highlighter-rouge">nasm</code>。</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>brew <span class="nb">install </span>nasm
</code></pre></div></div>

<p>Install XQuartz and its related libraries.</p>

<p>安裝 XQuartz 及其相關嘅庫。</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>brew <span class="nb">install </span>xquartz
brew <span class="nb">install </span>libx11
brew <span class="nb">install </span>libxfixes
brew <span class="nb">install </span>libxrandr
</code></pre></div></div>

<p>Make sure all the dependencies above are installed.</p>

<p>確保所有嘅依賴都已經安裝好。</p>

<h3 id="heading-12-get-xrdp-source-code--獲取-xrdp-嘅源碼">1.2. Get XRDP source code | 獲取 XRDP 嘅源碼</h3>

<p>All the operations are completed via terminal and CLI, so the only thing now I need to do is to download <code class="language-plaintext highlighter-rouge">xrdp 0.9.19</code> source code archive via command line. I use <code class="language-plaintext highlighter-rouge">wget</code> to do this.</p>

<p>所有嘅操作都係喺終端嘅命令行介面完成嘅，因此依家需要通過命令行工具來下載 <code class="language-plaintext highlighter-rouge">xrdp 0.9.19</code> 源代碼包。使用 <code class="language-plaintext highlighter-rouge">wget</code> 即可做到。</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>wget <span class="s2">"https://github.com/neutrinolabs/xrdp/releases/download/v0.9.19/xrdp-0.9.19.tar.gz"</span>
</code></pre></div></div>

<p>If the system do not have a <code class="language-plaintext highlighter-rouge">wget</code> tool installed, we can install from Homebrew easily. (if neccessary)</p>

<p>如果系統上面冇安裝 <code class="language-plaintext highlighter-rouge">wget</code> 工具，可以使用 Homebrew 來安裝。（如果必要嘅話）</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>brew <span class="nb">install </span>wget
</code></pre></div></div>

<p>Move this <code class="language-plaintext highlighter-rouge">tar</code> file to a coding place, for example, I put it in <code class="language-plaintext highlighter-rouge">~/Coding</code> directory.</p>

<p>將呢個 <code class="language-plaintext highlighter-rouge">tar</code> 歸檔文件移動去一個編寫代碼嘅常用位置，例如我將佢放喺 <code class="language-plaintext highlighter-rouge">~/Coding</code> 嘅目錄下面。</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">mv </span>xrdp-0.9.19.tar.gz ~/Coding
</code></pre></div></div>

<p>Use <code class="language-plaintext highlighter-rouge">tar</code> tool to extract the package.</p>

<p>使用 <code class="language-plaintext highlighter-rouge">tar</code> 工具解壓源碼包。</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">tar</span> <span class="nt">-xvf</span> xrdp-0.9.19.tar.gz
</code></pre></div></div>

<p>Then <code class="language-plaintext highlighter-rouge">cd</code> into the directory.</p>

<p>然後 <code class="language-plaintext highlighter-rouge">cd</code> 入嗰個目錄。</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cd </span>xrdp-0.9.19
</code></pre></div></div>

<h3 id="heading-13-compile-and-build--編譯並構建">1.3. Compile and build | 編譯並構建</h3>

<p>These steps are for x86_64 Macs.</p>

<p>呢啲步驟適用於 x86_64 架構嘅 Mac 電腦。</p>

<p>Start.</p>

<p>開始配置。</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./bootstrap
</code></pre></div></div>

<p>Configure the project. If the system does not have <code class="language-plaintext highlighter-rouge">pkgconfig</code> installed, it will throw an error out here.</p>

<p>配置項目。如果個系統冇安裝有 <code class="language-plaintext highlighter-rouge">pkgconfig</code>，佢會丟出一個報錯喺度。</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./configure <span class="nv">PKG_CONFIG_PATH</span><span class="o">=</span>/opt/X11/lib/pkgconfig
</code></pre></div></div>

<p>Make and build.</p>

<p>構建。</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>make
</code></pre></div></div>

<p>Install <code class="language-plaintext highlighter-rouge">xrdp</code>.</p>

<p>安裝 <code class="language-plaintext highlighter-rouge">xrdp</code>。</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>make <span class="nb">install</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">xrdp</code> will be installed at <code class="language-plaintext highlighter-rouge">/usr/local/sbin/xrdp</code> by default.</p>

<p><code class="language-plaintext highlighter-rouge">xrdp</code> 呢個程序將會默認被安裝喺 <code class="language-plaintext highlighter-rouge">/usr/local/sbin/xrdp</code> 嘅位置。</p>

<h2 id="heading-2-settings-for-xrdp--xrdp-設定">2. Settings for XRDP | XRDP 設定</h2>

<p>Make sure <code class="language-plaintext highlighter-rouge">xrdp</code> is installed on macOS.</p>

<p>確保 <code class="language-plaintext highlighter-rouge">xrdp</code> 已經安裝喺 macOS 之中。</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>which xrdp
</code></pre></div></div>

<p>If the output is <code class="language-plaintext highlighter-rouge">/usr/local/sbin/xrdp</code>, then <code class="language-plaintext highlighter-rouge">xrdp</code> is installed correctly. Generally, if no changes were made in source code before installation, the configuration file <code class="language-plaintext highlighter-rouge">xrdp.ini</code> will be stored at <code class="language-plaintext highlighter-rouge">/etc/rxdp</code>. Use <code class="language-plaintext highlighter-rouge">vim</code> or other text editor to open it, with root privilige.</p>

<p>如果輸出係 <code class="language-plaintext highlighter-rouge">/usr/本地/sbin/xrdp</code>，咁 <code class="language-plaintext highlighter-rouge">xrdp</code> 就安裝得正確。一般嚟講，如果喺安裝之前冇對源碼作出任何變更，配置檔案 <code class="language-plaintext highlighter-rouge">xrdp.ini</code> 就會儲存喺 <code class="language-plaintext highlighter-rouge">/etc/rxdp</code>。使用 root 權限，用 <code class="language-plaintext highlighter-rouge">vim</code> 或者其他編輯器打開佢。</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>vim /etc/xrdp/xrdp.ini
</code></pre></div></div>

<p>We need to change some of the configurations to make <code class="language-plaintext highlighter-rouge">xrdp</code> run correctly on macOS.</p>

<p>我哋需要修改一啲配置項來令到 <code class="language-plaintext highlighter-rouge">xrdp</code> 喺 macOS 上面正確運行。</p>

<p>Find <code class="language-plaintext highlighter-rouge">[vnc-any]</code> line. The following lines are below:</p>

<p>搵到 <code class="language-plaintext highlighter-rouge">[vnc-any]</code> 呢行。呢行下面幾行嘅內容：</p>

<div class="language-conf highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[<span class="n">vnc</span>-<span class="n">any</span>]
<span class="n">name</span>=<span class="n">vnc</span>-<span class="n">any</span>
<span class="n">lib</span>=<span class="n">libvnc</span>.<span class="n">so</span>
<span class="n">ip</span>=<span class="n">ask</span>
<span class="n">port</span>=<span class="m">5900</span>
<span class="n">username</span>=<span class="n">ask</span>
<span class="n">password</span>=<span class="n">ask</span>
<span class="c">#pamusername=asksame
#pampassword=asksame
#pamsessionmng=127.0.0.1
#delay_ms=2000
</span></code></pre></div></div>

<p>We need to make some changes, like below.</p>

<p>修改一啲部分，如下。</p>

<div class="language-conf highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[<span class="n">vnc</span>-<span class="n">any</span>]
<span class="n">name</span>=<span class="n">vnc</span>-<span class="n">any</span>
<span class="n">lib</span>=<span class="n">libvnc</span>.<span class="n">dylib</span>
<span class="n">ip</span>=<span class="m">127</span>.<span class="m">0</span>.<span class="m">0</span>.<span class="m">1</span>
<span class="n">port</span>=<span class="m">5900</span>
<span class="n">username</span>=<span class="n">ask</span>
<span class="n">password</span>=<span class="n">ask</span>
<span class="c">#pamusername=asksame
#pampassword=asksame
#pamsessionmng=127.0.0.1
#delay_ms=2000
</span></code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">lib</code> file should be <code class="language-plaintext highlighter-rouge">libvnc.dylib</code> on macOS instead of <code class="language-plaintext highlighter-rouge">libvnc.so</code> on Linux. <code class="language-plaintext highlighter-rouge">ip</code> should be set to <code class="language-plaintext highlighter-rouge">127.0.0.1</code> by default.</p>

<p>喺 macOS 上面，<code class="language-plaintext highlighter-rouge">lib</code> 文件應該為 <code class="language-plaintext highlighter-rouge">libvnc.dylib</code>，而唔係 Linux 上嘅 <code class="language-plaintext highlighter-rouge">libvnc.so</code>。設定默認嘅 <code class="language-plaintext highlighter-rouge">ip</code> 為 <code class="language-plaintext highlighter-rouge">127.0.0.1</code>。</p>

<p>There are other session types, just comment them if not used, or they may cause some remote desktop client cannot connect successfully. Some clients may not display the <code class="language-plaintext highlighter-rouge">xrdp</code> portal correctly and lead to a crash.</p>

<p>佢仲有其他嘅 session 種類，如果唔使用嘅話刪除或者註釋咗佢即可。不然嘅話佢哋可能導致喺某些遠程桌面客戶端上面嘅連接失敗。一啲客戶端可能顯示唔到 <code class="language-plaintext highlighter-rouge">xrdp</code> 嘅 portal 介面，並導致崩潰。</p>

<p>After editting the configuration file, save changes and exit <code class="language-plaintext highlighter-rouge">vim</code>.</p>

<p>編輯完配置文件之後，保存並且退出 <code class="language-plaintext highlighter-rouge">vim</code>。</p>

<h2 id="heading-3-start-xrdp-services">3. Start XRDP services</h2>

<p>Use root to run <code class="language-plaintext highlighter-rouge">xrdp</code>.</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo</span> /usr/local/sbin/xrdp
<span class="nb">sudo</span> /usr/local/sbin/xrdp-sesman
</code></pre></div></div>

<p>Or, if the directory is in the PATH,</p>

<p>或者，如果個目錄喺 PATH 之中嘅話，</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>xrdp
<span class="nb">sudo </span>xrdp-sesman
</code></pre></div></div>

<h2 id="heading-4-connect-from-another-machine--從另外機器上連接">4. Connect from another machine | 從另外機器上連接</h2>

<p>I use Remote Desktop Manager to connect Nucintosh remotely. After I added a RDP session, it connected successfully. <code class="language-plaintext highlighter-rouge">xrdp</code> works. However, I soon found that I still cannot adjust the resolution. It is a limitation from macOS, not from ARD.</p>

<p>使用 Remote Desktop Manager 遠程連接 NUC。添加一個 RDP session 之後，佢成功連接。<code class="language-plaintext highlighter-rouge">xrdp</code> 工作正常。但係我好快發現佢仍然唔可以係客戶端調整分辨率。呢個係系統嘅限制，而唔係 ARD 嘅限制。</p>

<h2 id="heading-5-conclusion--總結">5. Conclusion | 總結</h2>

<p><code class="language-plaintext highlighter-rouge">xrdp</code> can work on macOS, making it possible to use RDP (including the built-in Remote Desktop on Windows) connect to Mac. RDP cannot realize the "dynamic resolution" on macOS and some Linux distros. These limitations are from the operating systems, not from remote desktop protocols.</p>

<p><code class="language-plaintext highlighter-rouge">xrdp</code> 可以喺 macOS 上面運行，可以令到 macOS 使用 RDP 來被遠程連接。RDP 喺 macOS 及一啲 Linux 發行版本上面唔可以實現「動態分辨率」功能。呢啲限制係由於系統，並非遠程桌面協議控制。</p>]]></content><author><name>Rain</name></author><category term="靈車漂移" /><category term="Remote Desktop" /><category term="macOS" /><summary type="html"><![CDATA[Make Mac an RDP Server | 使用 RDP 遠程連接 Mac mini]]></summary></entry><entry><title type="html">Weird Python</title><link href="https://congjyu.github.io/blog/2025/10/19/WeirdPython/" rel="alternate" type="text/html" title="Weird Python" /><published>2025-10-19T00:00:00+00:00</published><updated>2025-10-19T00:00:00+00:00</updated><id>https://congjyu.github.io/blog/2025/10/19/WeirdPython</id><content type="html" xml:base="https://congjyu.github.io/blog/2025/10/19/WeirdPython/"><![CDATA[<h1 id="heading-weird-python">Weird Python</h1>

<h2 id="heading-introduction">Introduction</h2>

<p>I create this article because of the annoying confusing problems I met in the Python code. I have to admit that Python is a awesome programming language however… it DOSE exist some weird problems when coding not carefully. So this passage is used to record the problems and solutions for Python debugging.</p>

<h2 id="heading-stable-method-for-patching-functions-in-packages">Stable Method for Patching Functions in Packages</h2>

<h3 id="heading-problem">Problem</h3>

<p>Well, here's the problem. It happened when I was completing my tutorials. The tutorial imported a Python package called <code class="language-plaintext highlighter-rouge">lucent</code>, which is used to visualize the layers of CNNs. The code was like this:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">lucent.optvis</span> <span class="kn">import</span> <span class="n">render</span>
<span class="kn">import</span> <span class="nn">lucent.modelzoo.util</span> <span class="k">as</span> <span class="n">lu_zoo</span>
</code></pre></div></div>

<p>It seems normal, but it may run into trouble because I will run this code on Jupyter Notebook (local or online). Then I use the <code class="language-plaintext highlighter-rouge">lucent</code> package to visualize the layers.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">model1</span><span class="p">.</span><span class="n">to</span><span class="p">(</span><span class="n">ldevice</span><span class="p">).</span><span class="nb">eval</span><span class="p">()</span>
<span class="n">lu_zoo</span><span class="p">.</span><span class="n">get_model_layers</span><span class="p">(</span><span class="n">model1</span><span class="p">)</span>

<span class="n">model1_conv_images</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">model1_conv_names</span> <span class="o">=</span> <span class="p">[</span>
    <span class="s">"conv5:0"</span><span class="p">,</span> <span class="s">"conv5:1"</span><span class="p">,</span> <span class="s">"conv5:3"</span><span class="p">,</span> <span class="s">"conv5:7"</span><span class="p">,</span>
    <span class="s">"conv5:15"</span><span class="p">,</span> <span class="s">"conv5:31"</span><span class="p">,</span> <span class="s">"conv5:63"</span><span class="p">,</span> <span class="s">"conv5:127"</span>
<span class="p">]</span>

<span class="n">lu_zoo</span><span class="p">.</span><span class="n">get_model_layers</span><span class="p">(</span><span class="n">model1</span><span class="p">)</span>

<span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">model1_conv_names</span><span class="p">:</span>
    <span class="n">out</span> <span class="o">=</span> <span class="n">render</span><span class="p">.</span><span class="n">render_vis</span><span class="p">(</span><span class="n">model1</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">show_image</span><span class="o">=</span><span class="bp">False</span><span class="p">)</span>
    <span class="n">model1_conv_images</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">squeeze</span><span class="p">(</span><span class="n">out</span><span class="p">[</span><span class="mi">0</span><span class="p">]))</span>

<span class="n">plt</span><span class="p">.</span><span class="n">figure</span><span class="p">(</span><span class="n">figsize</span><span class="o">=</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="mi">4</span><span class="p">))</span>
<span class="n">show_imgs</span><span class="p">(</span><span class="n">model1_conv_images</span><span class="p">,</span> <span class="n">nc</span><span class="o">=</span><span class="mi">4</span><span class="p">)</span>
</code></pre></div></div>

<p>Then the output cell run into trouble. There should be a beautiful progress bar loading in the output cell showing the current progress of the analyzation, however, the progress bar flashed and disappeared suddenly, leaving some static time text in the output cell. Like the cell below.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>5it/s

second

second

second
</code></pre></div></div>

<p>The situation is not what we want.</p>

<h3 id="heading-attempts--solution">Attempts &amp; Solution</h3>

<p>The solution is simple too. Now the <code class="language-plaintext highlighter-rouge">tqdm</code> package provides a special module for Jupyter Notebook, so we can use that module to show progress bar in Jupyter Notebook environment. However, the <code class="language-plaintext highlighter-rouge">tqdm</code> package is not import directly and it is import by another package. How can we replace the original <code class="language-plaintext highlighter-rouge">tqdm</code> method to the new specified Jupyter Notebook <code class="language-plaintext highlighter-rouge">tqdm</code> method?</p>

<p>Change the code file in the <code class="language-plaintext highlighter-rouge">lucent</code> package and make it use <code class="language-plaintext highlighter-rouge">notebook.tqdm</code> directly DOES work… but it seems that it is not that "normal". (i.e. totally a strange method, does not robust and stable at all). So, any other methods?</p>

<p>Luckily, there is an elegant way to solve this problem. We import <code class="language-plaintext highlighter-rouge">sys</code> the system package first. Then we import the <code class="language-plaintext highlighter-rouge">tqdm.notebook</code> package. Finally we can "patch" the <code class="language-plaintext highlighter-rouge">tqdm</code> with <code class="language-plaintext highlighter-rouge">tqdm.notebook.tqdm</code>, and this will help us enable the Jupyter Notebook progress bar.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">sys</span>
<span class="kn">from</span> <span class="nn">tqdm</span> <span class="kn">import</span> <span class="n">notebook</span>

<span class="n">sys</span><span class="p">.</span><span class="n">modules</span><span class="p">[</span><span class="s">"tqdm"</span><span class="p">]</span> <span class="o">=</span> <span class="n">notebook</span>
</code></pre></div></div>

<p>Re-run the Jupyter Notebook, and we find that everything goes well.</p>

<p>Also, this method works well with other situations when some packages need to be change in certain imported packages.</p>

<h2 id="heading-つづく">つづく…</h2>]]></content><author><name>Rain</name></author><category term="Python" /><category term="Coding" /><category term="Debug" /><summary type="html"><![CDATA[Weird Python]]></summary></entry><entry><title type="html">網絡短詩記錄</title><link href="https://congjyu.github.io/blog/2023/01/01/Covid/" rel="alternate" type="text/html" title="網絡短詩記錄" /><published>2023-01-01T00:00:00+00:00</published><updated>2023-01-01T00:00:00+00:00</updated><id>https://congjyu.github.io/blog/2023/01/01/Covid</id><content type="html" xml:base="https://congjyu.github.io/blog/2023/01/01/Covid/"><![CDATA[<blockquote>
  <p>本文原作者未知，轉載自網絡，僅供參考。</p>
</blockquote>

<p>我要到伊犁採一朵大大的棉花，</p>

<p>我要到成都砍一根長長的竹子，</p>

<p>我要做一根世界上最大最長的棉簽，</p>

<p>我要在午夜之前給月亮做一個核酸，</p>

<p>明天晚上，</p>

<p>月亮的光，</p>

<p>將照亮我們門上的封條。</p>]]></content><author><name>Rain</name></author><category term="隨筆雜談" /><summary type="html"><![CDATA[本文原作者未知，轉載自網絡，僅供參考。]]></summary></entry></feed>