[{"data":1,"prerenderedAt":1909},["ShallowReactive",2],{"post:\u002Fposts\u002Fmysql\u002Fmysql-learning-07":3,"posts:ordered":1622},{"id":4,"title":5,"body":6,"category":1606,"cover":1607,"date":1608,"description":1609,"extension":1610,"featured":1611,"meta":1612,"navigation":694,"path":1613,"seo":1614,"series":1615,"seriesOrder":715,"stem":1616,"tags":1617,"__hash__":1621},"posts\u002Fposts\u002Fmysql\u002Fmysql-learning-07.md","MySQL 学习笔记 07：索引",{"type":7,"value":8,"toc":1585},"minimark",[9,16,21,26,31,142,145,148,152,165,210,213,238,241,249,252,274,277,283,285,289,301,303,317,320,326,328,351,354,398,400,404,408,445,447,488,490,494,517,519,542,545,551,553,564,566,570,591,593,617,620,626,628,639,641,726,728,732,759,761,776,778,786,788,792,857,859,870,872,876,880,888,890,896,898,986,988,999,1001,1005,1008,1022,1025,1102,1105,1179,1181,1185,1253,1255,1269,1271,1277,1279,1283,1499,1501,1513,1515,1544,1546,1550,1556,1559,1581],[10,11,12],"blockquote",{},[13,14,15],"p",{},"本文是《SQL 学习笔记》系列的一篇：示例数据和 SQL 都是我整理并在 MySQL 8 上跑过的，讲解部分按自己的理解写的。",[17,18,20],"h1",{"id":19},"_10-索引-index","10 索引 (Index)",[22,23,25],"h2",{"id":24},"_100-示例数据","10.0 示例数据",[27,28,30],"h3",{"id":29},"customers-表","Customers 表",[32,33,34,57],"table",{},[35,36,37],"thead",{},[38,39,40,45,48,51,54],"tr",{},[41,42,44],"th",{"align":43},"right","Id",[41,46,47],{},"Name",[41,49,50],{},"MemberCategory",[41,52,53],{},"PhoneNumber",[41,55,56],{},"Race",[58,59,60,78,95,111,127],"tbody",{},[38,61,62,66,69,72,75],{},[63,64,65],"td",{"align":43},"1",[63,67,68],{},"Chia",[63,70,71],{},"A",[63,73,74],{},"11111111",[63,76,77],{},"CN",[38,79,80,83,86,89,92],{},[63,81,82],{"align":43},"2",[63,84,85],{},"Tom",[63,87,88],{},"B",[63,90,91],{},"22222222",[63,93,94],{},"ML",[38,96,97,100,103,105,108],{},[63,98,99],{"align":43},"3",[63,101,102],{},"Alice",[63,104,71],{},[63,106,107],{},"33333333",[63,109,110],{},"IND",[38,112,113,116,119,122,125],{},[63,114,115],{"align":43},"4",[63,117,118],{},"Bob",[63,120,121],{},"C",[63,123,124],{},"44444444",[63,126,77],{},[38,128,129,132,135,137,140],{},[63,130,131],{"align":43},"5",[63,133,134],{},"Chen",[63,136,71],{},[63,138,139],{},"55555555",[63,141,77],{},[13,143,144],{},"假设表中有 100 万行，下面用来说明索引的作用。",[146,147],"hr",{},[22,149,151],{"id":150},"_101-索引概念与作用","10.1 索引概念与作用",[153,154,155,159,162],"ul",{},[156,157,158],"li",{},"索引加速基于搜索键（search key）的选择操作。",[156,160,161],{},"无索引时，查询需要线性扫描整个表。",[156,163,164],{},"示例：",[166,167,172],"pre",{"className":168,"code":169,"language":170,"meta":171,"style":171},"language-sql shiki shiki-themes github-light github-dark","SELECT * FROM Customers WHERE membercategory = 'A';\n","sql","",[173,174,175],"code",{"__ignoreMap":171},[176,177,180,184,187,190,194,197,200,203,207],"span",{"class":178,"line":179},"line",1,[176,181,183],{"class":182},"szBVR","SELECT",[176,185,186],{"class":182}," *",[176,188,189],{"class":182}," FROM",[176,191,193],{"class":192},"sVt8B"," Customers ",[176,195,196],{"class":182},"WHERE",[176,198,199],{"class":192}," membercategory ",[176,201,202],{"class":182},"=",[176,204,206],{"class":205},"sZZnC"," 'A'",[176,208,209],{"class":192},";\n",[13,211,212],{},"注释：",[153,214,215,229,232,235],{},[156,216,217,218,221,222,225,226,228],{},"没有索引时，数据库必须逐行读取 ",[173,219,220],{},"Customers"," 表，检查 ",[173,223,224],{},"membercategory"," 是否等于 ",[173,227,71],{},"。",[156,230,231],{},"这种扫描方式称为全表扫描（Full Table Scan）。",[156,233,234],{},"如果表有 100 万行，即使只匹配少量行，也要读取全部 100 万行。",[156,236,237],{},"有索引时，数据库可以先在索引中定位满足条件的行，再回表读取完整数据。",[13,239,240],{},"执行效果对比：",[166,242,247],{"className":243,"code":245,"language":246,"meta":171},[244],"language-text","无索引：\n读取 1,000,000 行 → 返回 3 行（Chia、Alice、Chen）\n\n有索引：\n在索引中找到 'A' 对应的位置 → 直接定位到 3 行 → 返回\n","text",[173,248,245],{"__ignoreMap":171},[13,250,251],{},"作用：",[153,253,254,257,271],{},[156,255,256],{},"减少需要扫描的数据量。",[156,258,259,260,262,263,266,267,270],{},"加快 ",[173,261,196],{}," 过滤、",[173,264,265],{},"JOIN"," 连接、",[173,268,269],{},"ORDER BY"," 排序。",[156,272,273],{},"降低查询的 I\u002FO 成本。",[13,275,276],{},"类比：",[166,278,281],{"className":279,"code":280,"language":246,"meta":171},[244],"索引就像书的目录。\n没有目录，找某一章要从第一页翻到最后一页。\n有目录，可以直接翻到对应页码。\n",[173,282,280],{"__ignoreMap":171},[146,284],{},[22,286,288],{"id":287},"_102-索引结构","10.2 索引结构",[153,290,291,294],{},[156,292,293],{},"索引通常存储在 B-trees（B 树）中。",[156,295,296,297,300],{},"创建索引键（如 ",[173,298,299],{},"memberCategory","）后，可快速定位行。",[13,302,212],{},[153,304,305,308,311,314],{},[156,306,307],{},"B 树是一种平衡的多路搜索树。",[156,309,310],{},"它保持数据有序，支持范围查询和等值查询。",[156,312,313],{},"查找、插入、删除的时间复杂度大致为 O(log n)。",[156,315,316],{},"叶子节点通常存储索引键和指向实际数据行的指针。",[13,318,319],{},"示意：",[166,321,324],{"className":322,"code":323,"language":246,"meta":171},[244],"假设在 MemberCategory 上建索引：\n\n          [B]\n        \u002F     \\\n      [A]     [C]\n     \u002F  \\       \\\n  Chia  Alice   Bob\n  Chen\n\n查找 'A'：\n从根节点 [B] 出发 → 进入左子树 [A] → 找到 Chia、Alice、Chen\n",[173,325,323],{"__ignoreMap":171},[13,327,251],{},[153,329,330,333,339,345],{},[156,331,332],{},"索引键保持有序。",[156,334,335,336],{},"支持等值查询：",[173,337,338],{},"WHERE membercategory = 'A'",[156,340,341,342],{},"支持范围查询：",[173,343,344],{},"WHERE membercategory BETWEEN 'A' AND 'C'",[156,346,347,348],{},"支持排序：",[173,349,350],{},"ORDER BY membercategory",[13,352,353],{},"不同数据库可能使用不同结构：",[32,355,356,366],{},[35,357,358],{},[38,359,360,363],{},[41,361,362],{},"数据库",[41,364,365],{},"常见索引结构",[58,367,368,376,384,391],{},[38,369,370,373],{},[63,371,372],{},"MySQL InnoDB",[63,374,375],{},"B+ 树",[38,377,378,381],{},[63,379,380],{},"PostgreSQL",[63,382,383],{},"B 树",[38,385,386,389],{},[63,387,388],{},"SQL Server",[63,390,383],{},[38,392,393,396],{},[63,394,395],{},"Oracle",[63,397,383],{},[146,399],{},[22,401,403],{"id":402},"_103-创建索引","10.3 创建索引",[27,405,407],{"id":406},"_1031-基本语法","10.3.1 基本语法",[166,409,411],{"className":168,"code":410,"language":170,"meta":171,"style":171},"CREATE [UNIQUE] INDEX index-name\nON table-name (column-name [, ...] [ASC|DESC]);\n",[173,412,413,427],{"__ignoreMap":171},[176,414,415,418,421,424],{"class":178,"line":179},[176,416,417],{"class":182},"CREATE",[176,419,420],{"class":192}," [UNIQUE] ",[176,422,423],{"class":182},"INDEX",[176,425,426],{"class":182}," index-name\n",[176,428,430,433,436,439,442],{"class":178,"line":429},2,[176,431,432],{"class":182},"ON",[176,434,435],{"class":182}," table-name",[176,437,438],{"class":192}," (column",[176,440,441],{"class":182},"-name",[176,443,444],{"class":192}," [, ...] [ASC|DESC]);\n",[13,446,212],{},[153,448,449,455,461,467,473,479],{},[156,450,451,454],{},[173,452,453],{},"CREATE INDEX","：创建普通索引。",[156,456,457,460],{},[173,458,459],{},"CREATE UNIQUE INDEX","：创建唯一索引，索引列的值不能重复。",[156,462,463,466],{},[173,464,465],{},"index-name","：索引名称，在表内通常唯一。",[156,468,469,472],{},[173,470,471],{},"table-name","：要建索引的表。",[156,474,475,478],{},[173,476,477],{},"column-name","：索引键列，可以多列。",[156,480,481,484,485,228],{},[173,482,483],{},"ASC | DESC","：索引中列的排序方向，默认 ",[173,486,487],{},"ASC",[146,489],{},[27,491,493],{"id":492},"_1032-单列唯一索引","10.3.2 单列唯一索引",[166,495,497],{"className":168,"code":496,"language":170,"meta":171,"style":171},"CREATE UNIQUE INDEX gdCust_idx\nON GoodCustomers(PhoneNumber);\n",[173,498,499,510],{"__ignoreMap":171},[176,500,501,503,506],{"class":178,"line":179},[176,502,417],{"class":182},[176,504,505],{"class":182}," UNIQUE INDEX",[176,507,509],{"class":508},"sScJk"," gdCust_idx\n",[176,511,512,514],{"class":178,"line":429},[176,513,432],{"class":182},[176,515,516],{"class":192}," GoodCustomers(PhoneNumber);\n",[13,518,212],{},[153,520,521,527,533,536],{},[156,522,523,524,526],{},"在 ",[173,525,53],{}," 列上创建唯一索引。",[156,528,529,530,532],{},"唯一索引要求 ",[173,531,53],{}," 的值不能重复。",[156,534,535],{},"如果已有重复值，创建会失败。",[156,537,538,539,541],{},"如果 ",[173,540,53],{}," 允许 NULL，多个 NULL 通常被允许（取决于数据库）。",[13,543,544],{},"执行效果：",[166,546,549],{"className":547,"code":548,"language":246,"meta":171},[244],"GoodCustomers 中 PhoneNumber 不允许重复。\n插入 ('C001', ..., '11111111') 成功。\n插入 ('C002', ..., '11111111') 失败：唯一约束冲突。\n",[173,550,548],{"__ignoreMap":171},[13,552,251],{},[153,554,555,561],{},[156,556,557,558,228],{},"加速按电话号码查询：",[173,559,560],{},"WHERE PhoneNumber = '11111111'",[156,562,563],{},"同时保证电话号码唯一。",[146,565],{},[27,567,569],{"id":568},"_1033-复合索引","10.3.3 复合索引",[166,571,573],{"className":168,"code":572,"language":170,"meta":171,"style":171},"CREATE INDEX gdCust_idx\nON GoodCustomers(CustomerID, CustomerName);\n",[173,574,575,584],{"__ignoreMap":171},[176,576,577,579,582],{"class":178,"line":179},[176,578,417],{"class":182},[176,580,581],{"class":182}," INDEX",[176,583,509],{"class":508},[176,585,586,588],{"class":178,"line":429},[176,587,432],{"class":182},[176,589,590],{"class":192}," GoodCustomers(CustomerID, CustomerName);\n",[13,592,212],{},[153,594,595,598,609],{},[156,596,597],{},"复合索引（composite index）包含两列。",[156,599,600,601,604,605,608],{},"索引键的顺序是 ",[173,602,603],{},"CustomerID"," 在前，",[173,606,607],{},"CustomerName"," 在后。",[156,610,611,612,614,615,270],{},"查询条件先按 ",[173,613,603],{}," 排序，再按 ",[173,616,607],{},[13,618,619],{},"复合索引的“最左前缀”原则：",[166,621,624],{"className":622,"code":623,"language":246,"meta":171},[244],"索引 (CustomerID, CustomerName) 可以支持：\n  WHERE CustomerID = 'C001'\n  WHERE CustomerID = 'C001' AND CustomerName = 'Alice'\n  ORDER BY CustomerID\n  ORDER BY CustomerID, CustomerName\n\n但通常不能单独支持：\n  WHERE CustomerName = 'Alice'\n  ORDER BY CustomerName\n",[173,625,623],{"__ignoreMap":171},[13,627,251],{},[153,629,630,633,636],{},[156,631,632],{},"加速多列联合查询。",[156,634,635],{},"比多个单列索引更节省空间。",[156,637,638],{},"索引列顺序很重要，应该把最常用于过滤的列放在前面。",[13,640,164],{},[166,642,644],{"className":168,"code":643,"language":170,"meta":171,"style":171},"-- 可以使用复合索引\nSELECT * FROM GoodCustomers\nWHERE CustomerID = 'C001' AND CustomerName = 'Alice';\n\n-- 通常不能有效使用复合索引\nSELECT * FROM GoodCustomers\nWHERE CustomerName = 'Alice';\n",[173,645,646,652,663,689,696,702,713],{"__ignoreMap":171},[176,647,648],{"class":178,"line":179},[176,649,651],{"class":650},"sJ8bj","-- 可以使用复合索引\n",[176,653,654,656,658,660],{"class":178,"line":429},[176,655,183],{"class":182},[176,657,186],{"class":182},[176,659,189],{"class":182},[176,661,662],{"class":192}," GoodCustomers\n",[176,664,666,668,671,673,676,679,682,684,687],{"class":178,"line":665},3,[176,667,196],{"class":182},[176,669,670],{"class":192}," CustomerID ",[176,672,202],{"class":182},[176,674,675],{"class":205}," 'C001'",[176,677,678],{"class":182}," AND",[176,680,681],{"class":192}," CustomerName ",[176,683,202],{"class":182},[176,685,686],{"class":205}," 'Alice'",[176,688,209],{"class":192},[176,690,692],{"class":178,"line":691},4,[176,693,695],{"emptyLinePlaceholder":694},true,"\n",[176,697,699],{"class":178,"line":698},5,[176,700,701],{"class":650},"-- 通常不能有效使用复合索引\n",[176,703,705,707,709,711],{"class":178,"line":704},6,[176,706,183],{"class":182},[176,708,186],{"class":182},[176,710,189],{"class":182},[176,712,662],{"class":192},[176,714,716,718,720,722,724],{"class":178,"line":715},7,[176,717,196],{"class":182},[176,719,681],{"class":192},[176,721,202],{"class":182},[176,723,686],{"class":205},[176,725,209],{"class":192},[146,727],{},[27,729,731],{"id":730},"_1034-降序索引","10.3.4 降序索引",[166,733,735],{"className":168,"code":734,"language":170,"meta":171,"style":171},"CREATE INDEX idx_age_desc\nON Customers (Age DESC);\n",[173,736,737,746],{"__ignoreMap":171},[176,738,739,741,743],{"class":178,"line":179},[176,740,417],{"class":182},[176,742,581],{"class":182},[176,744,745],{"class":508}," idx_age_desc\n",[176,747,748,750,753,756],{"class":178,"line":429},[176,749,432],{"class":182},[176,751,752],{"class":192}," Customers (Age ",[176,754,755],{"class":182},"DESC",[176,757,758],{"class":192},");\n",[13,760,212],{},[153,762,763,766,773],{},[156,764,765],{},"索引键按降序存储。",[156,767,768,769,772],{},"适合频繁执行 ",[173,770,771],{},"ORDER BY Age DESC"," 的查询。",[156,774,775],{},"不同数据库对降序索引的支持程度不同。",[13,777,251],{},[153,779,780,783],{},[156,781,782],{},"加速降序排序。",[156,784,785],{},"避免查询时额外排序。",[146,787],{},[27,789,791],{"id":790},"_1035-删除索引","10.3.5 删除索引",[166,793,795],{"className":168,"code":794,"language":170,"meta":171,"style":171},"-- MySQL\nDROP INDEX gdCust_idx ON GoodCustomers;\n\n-- PostgreSQL \u002F Oracle\nDROP INDEX gdCust_idx;\n\n-- SQL Server\nDROP INDEX gdCust_idx ON GoodCustomers;\n",[173,796,797,802,817,821,826,835,839,844],{"__ignoreMap":171},[176,798,799],{"class":178,"line":179},[176,800,801],{"class":650},"-- MySQL\n",[176,803,804,807,809,812,814],{"class":178,"line":429},[176,805,806],{"class":182},"DROP",[176,808,581],{"class":182},[176,810,811],{"class":192}," gdCust_idx ",[176,813,432],{"class":182},[176,815,816],{"class":192}," GoodCustomers;\n",[176,818,819],{"class":178,"line":665},[176,820,695],{"emptyLinePlaceholder":694},[176,822,823],{"class":178,"line":691},[176,824,825],{"class":650},"-- PostgreSQL \u002F Oracle\n",[176,827,828,830,832],{"class":178,"line":698},[176,829,806],{"class":182},[176,831,581],{"class":182},[176,833,834],{"class":192}," gdCust_idx;\n",[176,836,837],{"class":178,"line":704},[176,838,695],{"emptyLinePlaceholder":694},[176,840,841],{"class":178,"line":715},[176,842,843],{"class":650},"-- SQL Server\n",[176,845,847,849,851,853,855],{"class":178,"line":846},8,[176,848,806],{"class":182},[176,850,581],{"class":182},[176,852,811],{"class":192},[176,854,432],{"class":182},[176,856,816],{"class":192},[13,858,212],{},[153,860,861,864,867],{},[156,862,863],{},"不同数据库删除索引语法略有不同。",[156,865,866],{},"删除索引不会影响表数据，只影响查询性能。",[156,868,869],{},"删除主键索引通常需要先删除主键约束。",[146,871],{},[22,873,875],{"id":874},"_104-索引的代价与使用场景","10.4 索引的代价与使用场景",[27,877,879],{"id":878},"_1041-索引的代价","10.4.1 索引的代价",[153,881,882,885],{},[156,883,884],{},"每个索引增加存储空间。",[156,886,887],{},"数据插入、更新、删除时，索引必须更新：检索省时，但增删改操作成本增加。",[13,889,212],{},[166,891,894],{"className":892,"code":893,"language":246,"meta":171},[244],"读操作（SELECT）：\n  索引通常加快查询。\n\n写操作（INSERT \u002F UPDATE \u002F DELETE）：\n  索引需要同步维护，所以写入变慢。\n",[173,895,893],{"__ignoreMap":171},[13,897,164],{},[166,899,901],{"className":168,"code":900,"language":170,"meta":171,"style":171},"-- 假设 Customers 表在 MemberCategory、Race 上各有一个索引\n\nINSERT INTO Customers (Id, Name, MemberCategory, Race)\nVALUES (6, 'David', 'B', 'US');\n\n-- 数据库需要：\n-- 1. 插入数据行\n-- 2. 更新 MemberCategory 索引\n-- 3. 更新 Race 索引\n-- 所以比没有索引时慢\n",[173,902,903,908,912,925,955,959,964,969,974,980],{"__ignoreMap":171},[176,904,905],{"class":178,"line":179},[176,906,907],{"class":650},"-- 假设 Customers 表在 MemberCategory、Race 上各有一个索引\n",[176,909,910],{"class":178,"line":429},[176,911,695],{"emptyLinePlaceholder":694},[176,913,914,917,920,922],{"class":178,"line":665},[176,915,916],{"class":182},"INSERT INTO",[176,918,919],{"class":192}," Customers (Id, ",[176,921,47],{"class":182},[176,923,924],{"class":192},", MemberCategory, Race)\n",[176,926,927,930,933,937,940,943,945,948,950,953],{"class":178,"line":691},[176,928,929],{"class":182},"VALUES",[176,931,932],{"class":192}," (",[176,934,936],{"class":935},"sj4cs","6",[176,938,939],{"class":192},", ",[176,941,942],{"class":205},"'David'",[176,944,939],{"class":192},[176,946,947],{"class":205},"'B'",[176,949,939],{"class":192},[176,951,952],{"class":205},"'US'",[176,954,758],{"class":192},[176,956,957],{"class":178,"line":698},[176,958,695],{"emptyLinePlaceholder":694},[176,960,961],{"class":178,"line":704},[176,962,963],{"class":650},"-- 数据库需要：\n",[176,965,966],{"class":178,"line":715},[176,967,968],{"class":650},"-- 1. 插入数据行\n",[176,970,971],{"class":178,"line":846},[176,972,973],{"class":650},"-- 2. 更新 MemberCategory 索引\n",[176,975,977],{"class":178,"line":976},9,[176,978,979],{"class":650},"-- 3. 更新 Race 索引\n",[176,981,983],{"class":178,"line":982},10,[176,984,985],{"class":650},"-- 所以比没有索引时慢\n",[13,987,251],{},[153,989,990,993,996],{},[156,991,992],{},"索引不是越多越好。",[156,994,995],{},"读多写少的表适合建索引。",[156,997,998],{},"写多读少的表应控制索引数量。",[146,1000],{},[27,1002,1004],{"id":1003},"_1042-建议创建索引的情况","10.4.2 建议创建索引的情况",[13,1006,1007],{},"原笔记列出：",[153,1009,1010,1013,1019],{},[156,1011,1012],{},"预计表中行数快速增长。",[156,1014,1015,1016,1018],{},"频繁在列上过滤（如 ",[173,1017,338],{},"）。",[156,1020,1021],{},"单表创建过多索引会使更新和插入变慢。",[13,1023,1024],{},"补充注释：",[166,1026,1028],{"className":168,"code":1027,"language":170,"meta":171,"style":171},"-- 适合建索引：主键、外键、频繁过滤列、频繁排序列\nCREATE INDEX idx_customers_membercategory\nON Customers(MemberCategory);\n\n-- 适合建索引：JOIN 条件列\nCREATE INDEX idx_orders_custid\nON Orders(CustId);\n\n-- 适合建索引：ORDER BY 列\nCREATE INDEX idx_customers_age\nON Customers(Age);\n",[173,1029,1030,1035,1044,1051,1055,1060,1069,1076,1080,1085,1094],{"__ignoreMap":171},[176,1031,1032],{"class":178,"line":179},[176,1033,1034],{"class":650},"-- 适合建索引：主键、外键、频繁过滤列、频繁排序列\n",[176,1036,1037,1039,1041],{"class":178,"line":429},[176,1038,417],{"class":182},[176,1040,581],{"class":182},[176,1042,1043],{"class":508}," idx_customers_membercategory\n",[176,1045,1046,1048],{"class":178,"line":665},[176,1047,432],{"class":182},[176,1049,1050],{"class":192}," Customers(MemberCategory);\n",[176,1052,1053],{"class":178,"line":691},[176,1054,695],{"emptyLinePlaceholder":694},[176,1056,1057],{"class":178,"line":698},[176,1058,1059],{"class":650},"-- 适合建索引：JOIN 条件列\n",[176,1061,1062,1064,1066],{"class":178,"line":704},[176,1063,417],{"class":182},[176,1065,581],{"class":182},[176,1067,1068],{"class":508}," idx_orders_custid\n",[176,1070,1071,1073],{"class":178,"line":715},[176,1072,432],{"class":182},[176,1074,1075],{"class":192}," Orders(CustId);\n",[176,1077,1078],{"class":178,"line":846},[176,1079,695],{"emptyLinePlaceholder":694},[176,1081,1082],{"class":178,"line":976},[176,1083,1084],{"class":650},"-- 适合建索引：ORDER BY 列\n",[176,1086,1087,1089,1091],{"class":178,"line":982},[176,1088,417],{"class":182},[176,1090,581],{"class":182},[176,1092,1093],{"class":508}," idx_customers_age\n",[176,1095,1097,1099],{"class":178,"line":1096},11,[176,1098,432],{"class":182},[176,1100,1101],{"class":192}," Customers(Age);\n",[13,1103,1104],{},"常见适合建索引的场景：",[32,1106,1107,1117],{},[35,1108,1109],{},[38,1110,1111,1114],{},[41,1112,1113],{},"场景",[41,1115,1116],{},"示例",[58,1118,1119,1129,1139,1149,1159,1169],{},[38,1120,1121,1124],{},[63,1122,1123],{},"主键",[63,1125,1126],{},[173,1127,1128],{},"PRIMARY KEY (Id)",[38,1130,1131,1134],{},[63,1132,1133],{},"外键",[63,1135,1136],{},[173,1137,1138],{},"FOREIGN KEY (CustId)",[38,1140,1141,1144],{},[63,1142,1143],{},"高频等值过滤",[63,1145,1146],{},[173,1147,1148],{},"WHERE MemberCategory = 'A'",[38,1150,1151,1154],{},[63,1152,1153],{},"高频范围过滤",[63,1155,1156],{},[173,1157,1158],{},"WHERE Age BETWEEN 18 AND 30",[38,1160,1161,1164],{},[63,1162,1163],{},"高频 JOIN 列",[63,1165,1166],{},[173,1167,1168],{},"ON C.Id = O.CustId",[38,1170,1171,1174],{},[63,1172,1173],{},"高频 ORDER BY 列",[63,1175,1176],{},[173,1177,1178],{},"ORDER BY CreatedAt DESC",[146,1180],{},[27,1182,1184],{"id":1183},"_1043-不建议创建索引的情况","10.4.3 不建议创建索引的情况",[166,1186,1188],{"className":168,"code":1187,"language":170,"meta":171,"style":171},"-- 列值重复率很高，索引选择性差\n-- 例如 Gender 只有 'M'、'F' 两种值，索引效果有限\nCREATE INDEX idx_gender ON Customers(Gender);\n\n-- 表很小，全表扫描已经很快\n-- 例如只有几十行的配置表\nCREATE INDEX idx_config_key ON Config(`Key`);\n-- 注意：Key 是 MySQL 的保留字，这里必须用反引号包起来\n",[173,1189,1190,1195,1200,1215,1219,1224,1229,1248],{"__ignoreMap":171},[176,1191,1192],{"class":178,"line":179},[176,1193,1194],{"class":650},"-- 列值重复率很高，索引选择性差\n",[176,1196,1197],{"class":178,"line":429},[176,1198,1199],{"class":650},"-- 例如 Gender 只有 'M'、'F' 两种值，索引效果有限\n",[176,1201,1202,1204,1206,1209,1212],{"class":178,"line":665},[176,1203,417],{"class":182},[176,1205,581],{"class":182},[176,1207,1208],{"class":508}," idx_gender",[176,1210,1211],{"class":182}," ON",[176,1213,1214],{"class":192}," Customers(Gender);\n",[176,1216,1217],{"class":178,"line":691},[176,1218,695],{"emptyLinePlaceholder":694},[176,1220,1221],{"class":178,"line":698},[176,1222,1223],{"class":650},"-- 表很小，全表扫描已经很快\n",[176,1225,1226],{"class":178,"line":704},[176,1227,1228],{"class":650},"-- 例如只有几十行的配置表\n",[176,1230,1231,1233,1235,1238,1240,1243,1246],{"class":178,"line":715},[176,1232,417],{"class":182},[176,1234,581],{"class":182},[176,1236,1237],{"class":508}," idx_config_key",[176,1239,1211],{"class":182},[176,1241,1242],{"class":192}," Config(",[176,1244,1245],{"class":205},"`Key`",[176,1247,758],{"class":192},[176,1249,1250],{"class":178,"line":846},[176,1251,1252],{"class":650},"-- 注意：Key 是 MySQL 的保留字，这里必须用反引号包起来\n",[13,1254,212],{},[153,1256,1257,1260,1263,1266],{},[156,1258,1259],{},"选择性（selectivity）高的列更适合建索引。",[156,1261,1262],{},"选择性 = 不同值数量 \u002F 总行数。",[156,1264,1265],{},"选择性接近 1 表示值几乎都不同，适合索引。",[156,1267,1268],{},"选择性接近 0 表示值大量重复，索引效果差。",[13,1270,164],{},[166,1272,1275],{"className":1273,"code":1274,"language":246,"meta":171},[244],"Id        选择性接近 1    适合索引\nEmail     选择性接近 1    适合索引\nGender    选择性接近 0    通常不适合\nStatus    选择性较低      视情况而定\n",[173,1276,1274],{"__ignoreMap":171},[146,1278],{},[27,1280,1282],{"id":1281},"_1044-索引失效的常见情况","10.4.4 索引失效的常见情况",[166,1284,1286],{"className":168,"code":1285,"language":170,"meta":171,"style":171},"-- 在索引列上使用函数，可能导致索引失效\nSELECT * FROM Customers WHERE YEAR(CreatedAt) = 2026;\n\n-- 更推荐：\nSELECT * FROM Customers\nWHERE CreatedAt >= '2026-01-01' AND CreatedAt \u003C '2027-01-01';\n\n-- 使用 LIKE '%xxx' 前缀通配，通常无法使用索引\nSELECT * FROM Customers WHERE Name LIKE '%Alice';\n\n-- LIKE 'xxx%' 前缀匹配，通常可以使用索引\nSELECT * FROM Customers WHERE Name LIKE 'Alice%';\n\n-- 对索引列进行运算，可能导致索引失效\nSELECT * FROM Customers WHERE Age + 1 = 23;\n\n-- 更推荐：\nSELECT * FROM Customers WHERE Age = 22;\n",[173,1287,1288,1293,1318,1322,1327,1338,1363,1367,1372,1395,1399,1404,1426,1431,1437,1467,1472,1477],{"__ignoreMap":171},[176,1289,1290],{"class":178,"line":179},[176,1291,1292],{"class":650},"-- 在索引列上使用函数，可能导致索引失效\n",[176,1294,1295,1297,1299,1301,1303,1305,1308,1311,1313,1316],{"class":178,"line":429},[176,1296,183],{"class":182},[176,1298,186],{"class":182},[176,1300,189],{"class":182},[176,1302,193],{"class":192},[176,1304,196],{"class":182},[176,1306,1307],{"class":935}," YEAR",[176,1309,1310],{"class":192},"(CreatedAt) ",[176,1312,202],{"class":182},[176,1314,1315],{"class":935}," 2026",[176,1317,209],{"class":192},[176,1319,1320],{"class":178,"line":665},[176,1321,695],{"emptyLinePlaceholder":694},[176,1323,1324],{"class":178,"line":691},[176,1325,1326],{"class":650},"-- 更推荐：\n",[176,1328,1329,1331,1333,1335],{"class":178,"line":698},[176,1330,183],{"class":182},[176,1332,186],{"class":182},[176,1334,189],{"class":182},[176,1336,1337],{"class":192}," Customers\n",[176,1339,1340,1342,1345,1348,1351,1353,1355,1358,1361],{"class":178,"line":704},[176,1341,196],{"class":182},[176,1343,1344],{"class":192}," CreatedAt ",[176,1346,1347],{"class":182},">=",[176,1349,1350],{"class":205}," '2026-01-01'",[176,1352,678],{"class":182},[176,1354,1344],{"class":192},[176,1356,1357],{"class":182},"\u003C",[176,1359,1360],{"class":205}," '2027-01-01'",[176,1362,209],{"class":192},[176,1364,1365],{"class":178,"line":715},[176,1366,695],{"emptyLinePlaceholder":694},[176,1368,1369],{"class":178,"line":846},[176,1370,1371],{"class":650},"-- 使用 LIKE '%xxx' 前缀通配，通常无法使用索引\n",[176,1373,1374,1376,1378,1380,1382,1384,1387,1390,1393],{"class":178,"line":976},[176,1375,183],{"class":182},[176,1377,186],{"class":182},[176,1379,189],{"class":182},[176,1381,193],{"class":192},[176,1383,196],{"class":182},[176,1385,1386],{"class":182}," Name",[176,1388,1389],{"class":182}," LIKE",[176,1391,1392],{"class":205}," '%Alice'",[176,1394,209],{"class":192},[176,1396,1397],{"class":178,"line":982},[176,1398,695],{"emptyLinePlaceholder":694},[176,1400,1401],{"class":178,"line":1096},[176,1402,1403],{"class":650},"-- LIKE 'xxx%' 前缀匹配，通常可以使用索引\n",[176,1405,1407,1409,1411,1413,1415,1417,1419,1421,1424],{"class":178,"line":1406},12,[176,1408,183],{"class":182},[176,1410,186],{"class":182},[176,1412,189],{"class":182},[176,1414,193],{"class":192},[176,1416,196],{"class":182},[176,1418,1386],{"class":182},[176,1420,1389],{"class":182},[176,1422,1423],{"class":205}," 'Alice%'",[176,1425,209],{"class":192},[176,1427,1429],{"class":178,"line":1428},13,[176,1430,695],{"emptyLinePlaceholder":694},[176,1432,1434],{"class":178,"line":1433},14,[176,1435,1436],{"class":650},"-- 对索引列进行运算，可能导致索引失效\n",[176,1438,1440,1442,1444,1446,1448,1450,1453,1456,1459,1462,1465],{"class":178,"line":1439},15,[176,1441,183],{"class":182},[176,1443,186],{"class":182},[176,1445,189],{"class":182},[176,1447,193],{"class":192},[176,1449,196],{"class":182},[176,1451,1452],{"class":192}," Age ",[176,1454,1455],{"class":182},"+",[176,1457,1458],{"class":935}," 1",[176,1460,1461],{"class":182}," =",[176,1463,1464],{"class":935}," 23",[176,1466,209],{"class":192},[176,1468,1470],{"class":178,"line":1469},16,[176,1471,695],{"emptyLinePlaceholder":694},[176,1473,1475],{"class":178,"line":1474},17,[176,1476,1326],{"class":650},[176,1478,1480,1482,1484,1486,1488,1490,1492,1494,1497],{"class":178,"line":1479},18,[176,1481,183],{"class":182},[176,1483,186],{"class":182},[176,1485,189],{"class":182},[176,1487,193],{"class":192},[176,1489,196],{"class":182},[176,1491,1452],{"class":192},[176,1493,202],{"class":182},[176,1495,1496],{"class":935}," 22",[176,1498,209],{"class":192},[13,1500,251],{},[153,1502,1503,1506],{},[156,1504,1505],{},"索引是否生效取决于数据库优化器和具体写法。",[156,1507,1508,1509,1512],{},"用 ",[173,1510,1511],{},"EXPLAIN"," 可以查看执行计划，判断是否使用了索引。",[13,1514,164],{},[166,1516,1518],{"className":168,"code":1517,"language":170,"meta":171,"style":171},"EXPLAIN SELECT * FROM Customers WHERE MemberCategory = 'A';\n",[173,1519,1520],{"__ignoreMap":171},[176,1521,1522,1525,1527,1529,1531,1533,1535,1538,1540,1542],{"class":178,"line":179},[176,1523,1524],{"class":192},"EXPLAIN ",[176,1526,183],{"class":182},[176,1528,186],{"class":182},[176,1530,189],{"class":182},[176,1532,193],{"class":192},[176,1534,196],{"class":182},[176,1536,1537],{"class":192}," MemberCategory ",[176,1539,202],{"class":182},[176,1541,206],{"class":205},[176,1543,209],{"class":192},[146,1545],{},[22,1547,1549],{"id":1548},"_105-本章核心记忆点","10.5 本章核心记忆点",[166,1551,1554],{"className":1552,"code":1553,"language":246,"meta":171},[244],"索引加速查询，但增加存储和写入成本。\n索引通常使用 B 树 \u002F B+ 树结构。\nCREATE INDEX 创建普通索引，CREATE UNIQUE INDEX 创建唯一索引。\n复合索引有最左前缀原则，列顺序很重要。\n索引适合高频过滤、JOIN、排序的列。\n索引不适合小表、低选择性列、频繁写入的列。\n索引不是越多越好，读多写少适合建索引。\n",[173,1555,1553],{"__ignoreMap":171},[13,1557,1558],{},"本章重点：",[153,1560,1561,1564,1572,1575,1578],{},[156,1562,1563],{},"理解索引为什么能加快查询。",[156,1565,1566,1567,1569,1570,228],{},"会写 ",[173,1568,453],{},"、",[173,1571,459],{},[156,1573,1574],{},"理解复合索引和列顺序。",[156,1576,1577],{},"知道索引的代价和适用场景。",[156,1579,1580],{},"知道哪些写法可能导致索引失效。",[1582,1583,1584],"style",{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":171,"searchDepth":665,"depth":665,"links":1586},[1587,1590,1591,1592,1599,1605],{"id":24,"depth":429,"text":25,"children":1588},[1589],{"id":29,"depth":665,"text":30},{"id":150,"depth":429,"text":151},{"id":287,"depth":429,"text":288},{"id":402,"depth":429,"text":403,"children":1593},[1594,1595,1596,1597,1598],{"id":406,"depth":665,"text":407},{"id":492,"depth":665,"text":493},{"id":568,"depth":665,"text":569},{"id":730,"depth":665,"text":731},{"id":790,"depth":665,"text":791},{"id":874,"depth":429,"text":875,"children":1600},[1601,1602,1603,1604],{"id":878,"depth":665,"text":879},{"id":1003,"depth":665,"text":1004},{"id":1183,"depth":665,"text":1184},{"id":1281,"depth":665,"text":1282},{"id":1548,"depth":429,"text":1549},"学习",null,"2026-10-10","MySQL 学习笔记第七篇：索引的作用与 B+ 树结构、单列与复合索引的创建、索引的代价与适用场景。","md",false,{},"\u002Fposts\u002Fmysql\u002Fmysql-learning-07",{"title":5,"description":1609},"SQL 学习笔记","posts\u002Fmysql\u002Fmysql-learning-07",[1618,1619,1620],"SQL","MySQL","学习笔记","nhrFhIj_QS2Aaieq_UvvscZdd8NtIHhiZVbiFmhehX0",[1623,1630,1636,1642,1648,1654,1660,1666,1672,1678,1684,1689,1694,1699,1704,1709,1714,1719,1724,1729,1734,1739,1744,1749,1754,1759,1764,1769,1774,1780,1784,1788,1792,1796,1800,1804,1808,1812,1816,1820,1824,1830,1834,1838,1842,1846,1847,1851,1855,1859,1863,1868,1872,1876,1881,1886,1892,1896,1901,1905],{"path":1624,"title":1625,"description":1626,"date":1627,"series":1628,"seriesOrder":1629},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-28","软件分析与设计笔记 28：draw.io 作图手册：五张图的符号位置、落笔顺序与自检","不模拟软件界面，只讲清三件事：每个 UML 符号在 draw.io 里搜什么词、一张图该按什么顺序落笔、画完拿什么标准自查。覆盖用例图、活动图、顺序图、类图、界面草图五种图纸，末尾附「最容易画反的十二笔」总检表。","2026-11-19","软件分析与设计笔记",28,{"path":1631,"title":1632,"description":1633,"date":1634,"series":1628,"seriesOrder":1635},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-27","软件分析与设计笔记 27：Paper 1 应试：从读案例到交卷的一次完整演算","拿一份完整的案例，从头到尾走一遍：怎么读案例列角色、怎么在四个小问之间分配时间、每一张图从哪一句话推出来、两张表怎么填、交卷前拿什么清单自查。附 C25 真题与样卷的题型分值对照。","2026-11-18",27,{"path":1637,"title":1638,"description":1639,"date":1640,"series":1628,"seriesOrder":1641},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-26","软件分析与设计笔记 26：总结：把整条链路串起来","系列收尾：从需求到代码的完整链路回顾、每一环的产出与验收方式、六条贯穿全系列的原则，以及一份可以直接拿去做项目自查的清单。","2026-11-17",26,{"path":1643,"title":1644,"description":1645,"date":1646,"series":1628,"seriesOrder":1647},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-25","软件分析与设计笔记 25：代码重构：坏味道与手法","重构不改变外部行为、只改善内部结构：四种常见坏味道与对应手法、为什么动手之前必须先有测试、什么时候该重构，以及重构和前面那些设计模型是什么关系。","2026-11-16",25,{"path":1649,"title":1650,"description":1651,"date":1652,"series":1628,"seriesOrder":1653},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-24","软件分析与设计笔记 24：设计原则：内聚、耦合与多态","好设计怎么衡量：内聚与耦合分别在说什么、一次从「差设计」到「多态重设计」的完整对照、抽象类与接口的选择依据，以及复用为什么会从优势变成风险。","2026-11-15",24,{"path":1655,"title":1656,"description":1657,"date":1658,"series":1628,"seriesOrder":1659},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-23","软件分析与设计笔记 23：设计建模：从对象模型到能实现的方案","分析模型能用但还不能直接开工：对象模型怎么映射到关系模型、继承映射的三种方案怎么选、数据访问层与 Data Broker 解决什么问题，以及三层架构与 MVC 的分工。","2026-11-14",23,{"path":1661,"title":1662,"description":1663,"date":1664,"series":1628,"seriesOrder":1665},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-22","软件分析与设计笔记 22：分析建模：边界、实体与控制对象","不经过分析直接写代码会踩什么坑、三种分析对象各管一段、怎么用顺序图完成一次用例实现，以及从顺序图导出类的属性、操作与关系的方法。","2026-11-13",22,{"path":1667,"title":1668,"description":1669,"date":1670,"series":1628,"seriesOrder":1671},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-21","软件分析与设计笔记 21：UML：类图与顺序图","为什么需要一种共同的画法：类符号三格怎么画、五种关系的符号与设计含义、每种关系落到 Java 是什么写法，顺序图的三要素怎么摆；以及这三种最容易找不到的符号在 draw.io 里各搜什么词。","2026-11-12",21,{"path":1673,"title":1674,"description":1675,"date":1676,"series":1628,"seriesOrder":1677},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-20","软件分析与设计笔记 20：面向对象概念：对象、关系与多态","面向对象不是「用某个语言写代码」：对象与类的区别、封装真正在保护什么、五种关系（依赖、关联、聚合、组合、继承）各自何时用，以及多态为什么能减少条件判断。","2026-11-11",20,{"path":1679,"title":1680,"description":1681,"date":1682,"series":1628,"seriesOrder":1683},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-19","软件分析与设计笔记 19：规范化与逻辑数据模型","一次改地址引发的数据不一致，讲清规范化的真正动机；UNF 到 3NF 每一步在解决什么问题，派生字段为什么不能存，优化为什么和规范化方向相反，以及逻辑数据模型在 3NF 之上补了什么。","2026-11-10",19,{"path":1685,"title":1686,"description":1687,"date":1688,"series":1628,"seriesOrder":1479},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-18","软件分析与设计笔记 18：数据库分析：实体、关系与数据字典","为什么不能拿到需求就动手建表：实体与关系怎么判定、基数为什么必须由业务方确认、数据命名要遵守什么规则，以及数据字典四列里最容易被空着的那一列。","2026-11-09",{"path":1690,"title":1691,"description":1692,"date":1693,"series":1628,"seriesOrder":1474},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-17","软件分析与设计笔记 17：用生成式 AI 做设计与开发：能用的场景与要防的坑","生成式 AI 在工作流里真正站得住的三个场景 —— 造示例数据、解释陌生代码、起草初稿；它为什么会复现社会偏见、为什么会编得像真的，以及哪些内容绝不能直接采用。","2026-11-08",{"path":1695,"title":1696,"description":1697,"date":1698,"series":1628,"seriesOrder":1469},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-16","软件分析与设计笔记 16：从 persona 到 URS：一份交付物的完整骨架","把前面十几篇串成一份能交出去的东西：用户需求规格说明书的九个部分、每一章来自哪个环节、由谁确认，以及面向决策者的提案该讲清哪三件事。","2026-11-07",{"path":1700,"title":1701,"description":1702,"date":1703,"series":1628,"seriesOrder":1439},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-15","软件分析与设计笔记 15：非功能需求：把「快、稳、安全」变成数字","为什么「系统要快」不是一条需求：非功能需求怎么量化，决定性能的用户量、交易量、数据量三张表怎么估，安全需求要写清什么，以及可靠性的「不可用」该怎么定义。","2026-11-06",{"path":1705,"title":1706,"description":1707,"date":1708,"series":1628,"seriesOrder":1433},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-14","软件分析与设计笔记 14：软件测试：层级、方法与等价类划分","「我们测过了」这句话问题在哪：为什么静态评审和动态测试缺一不可，五个测试层级各自验证什么，白盒与黑盒的差别，以及用等价类划分和边界值把无限输入变成有限用例的方法。","2026-11-05",{"path":1710,"title":1711,"description":1712,"date":1713,"series":1628,"seriesOrder":1428},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-13","软件分析与设计笔记 13：需求获取：五种调查方法与一场访谈","需求从哪里来：五种事实调查方法各自适合回答什么、代价是什么，访谈从准备到记录的全过程，问卷设计里最容易犯的错，以及访谈记录必须写下来的那几项信息。","2026-11-04",{"path":1715,"title":1716,"description":1717,"date":1718,"series":1628,"seriesOrder":1406},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-12","软件分析与设计笔记 12：前端设计：把流程变成屏幕","界面设计不是「画得好看」：从活动图走到屏幕的三步，CRUD 任务流怎么当检查表，界面必须回答的三个问题，信息架构与五种导航类型，视觉一致性与留白，以及移动端与 Web 端的差别。","2026-11-03",{"path":1720,"title":1721,"description":1722,"date":1723,"series":1628,"seriesOrder":1096},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-11","软件分析与设计笔记 11：Agentic AI 与业务流程自动化","AI agent 到底是什么：五项能力与四个核心组件，什么场景该用定时任务、什么该用程序、什么才值得用 agent，两种实现路径的差别，必须提前防住的四类失败模式，以及设计方法从提示词到编排的演进。","2026-11-02",{"path":1725,"title":1726,"description":1727,"date":1728,"series":1628,"seriesOrder":982},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-10","软件分析与设计笔记 10：精炼用例模型：include、extend 与泛化","三种精炼用例关系的判断标准：include 什么时候用、它和 extend 的区别到底在哪、参与者泛化与用例泛化各解决什么问题，以及精炼到什么程度就该停手。","2026-11-01",{"path":1730,"title":1731,"description":1732,"date":1733,"series":1628,"seriesOrder":976},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-09","软件分析与设计笔记 09：功能需求（二）：从流程到业务实体","活动图画完之后，怎么从流程里筛出真正的业务实体：三条判断标准、哪些名词其实不该建实体、属性从哪里来，以及领域模型和后面的数据模型分别负责什么。","2026-10-31",{"path":1735,"title":1736,"description":1737,"date":1738,"series":1628,"seriesOrder":846},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-08","软件分析与设计笔记 08：功能需求（一）：用例模型与活动图","从用户故事到用例的翻译过程：什么才算「好」的参与者，系统边界怎么划，用例描述必须写清哪四件事，活动图怎么把分支、并行与泳道画清楚，以及这两个图在 draw.io 里各用哪个形状、最容易画错哪一笔。","2026-10-30",{"path":1740,"title":1741,"description":1742,"date":1743,"series":1628,"seriesOrder":715},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-07","软件分析与设计笔记 07：用户故事：把需求写成可验收的一句话","用户故事的三段结构，怎么从用户画像和数据地图里把它提取出来，一条故事多大算合适、时序为什么也是需求的一部分，以及验收标准怎么写才算真的可验收。","2026-10-29",{"path":1745,"title":1746,"description":1747,"date":1748,"series":1628,"seriesOrder":704},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-06","软件分析与设计笔记 06：范围界定（数据侧）：用数据校验你的判断","访谈结论和数字对不上时该怎么办：分析的四个层次分别在回答什么问题、为什么不能跳级、数据准备与搜集怎么做成一张应用地图，以及数据最容易在哪几个地方骗你。","2026-10-28",{"path":1750,"title":1751,"description":1752,"date":1753,"series":1628,"seriesOrder":698},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-05","软件分析与设计笔记 05：范围界定（用户侧）：用户画像与客户旅程图","为什么「所有用户」是一个陷阱，项目边界该怎么划，用户画像要写哪五个要素、最容易写成什么废纸，客户旅程图怎么把阶段、想法和情绪画在同一张纸上，以及怎么从这两样东西一步步推到可验证的需求。","2026-10-27",{"path":1755,"title":1756,"description":1757,"date":1758,"series":1628,"seriesOrder":691},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-04","软件分析与设计笔记 04：接手陌生案例的第一步：三问与一张表","拿到一个完全陌生的业务案例，怎么从零开始不跑偏：只问 Who、What、Why 三个问题，把「问题—事实—假设—理由」记成一张四列表，说清为什么假设必须写明理由，以及带着问题进访谈为什么比空手去有效得多。","2026-10-26",{"path":1760,"title":1761,"description":1762,"date":1763,"series":1628,"seriesOrder":665},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-03","软件分析与设计笔记 03：Agile 与 Scrum：迭代、角色与产出物","从「三周做完交付，客户说这不是我要的」讲起，说清瀑布式与迭代式的根本差别，Scrum 的三个角色和三种产出物，一个 sprint 里四次固定活动各自在解决什么问题，以及敏捷里最常见的几个误解。","2026-10-25",{"path":1765,"title":1766,"description":1767,"date":1768,"series":1628,"seriesOrder":429},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-02","软件分析与设计笔记 02：设计思维、用户驱动与数据驱动","从「客服要一个能一次看完所有排期的界面」这个翻车案例讲起，说清设计思维到底是什么、用户驱动与数据驱动各自能看见什么又看不见什么、为什么真正的落点是把两者合起来的 data-informed design，以及访谈中怎么问出有用的信息。","2026-10-24",{"path":1770,"title":1771,"description":1772,"date":1773,"series":1628,"seriesOrder":179},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-01","软件分析与设计笔记 01：课程地图与从需求到设计的主干","这一篇不写具体方法，先把整条链路铺开：软件分析与设计到底要解决什么问题，从需求到设计分成哪两段，中间有哪十几个环节，一份完整的交付物里应该有什么，以及后面 27 篇笔记怎么排、新人该按什么顺序读。","2026-10-23",{"path":1775,"title":1776,"description":1777,"date":1778,"series":1779,"seriesOrder":179},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-01","Spring 学习笔记 01：IoC、依赖注入与第一个 Spring Boot 应用","从「手写 new 出来的三层结构」的痛点讲起，说清 IoC 容器、Bean 与依赖注入是怎么回事，再用 Maven 与 Spring Initializr 跑起第一个能返回 JSON 的应用。","2026-10-15","Spring 学习笔记",{"path":1781,"title":1782,"description":1783,"date":1778,"series":1779,"seriesOrder":429},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-02","Spring 学习笔记 02：Spring Data JPA 基础——实体映射与 Repository","用 ORM 把类映射到表：@Entity、@Id、@GeneratedValue 怎么用，CrudRepository 三层接口怎么选，H2 与 MySQL 怎么配，以及 ddl-auto 到底该写哪个值。",{"path":1785,"title":1786,"description":1787,"date":1778,"series":1779,"seriesOrder":665},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-03","Spring 学习笔记 03：JPA 查询——派生方法、JPQL 与分页排序","Repository 自带的方法只够按主键查，这篇讲三种自己造查询的方式：方法名派生、@Query 写 JPQL、原生 SQL，以及 Pageable 分页排序和投影返回。",{"path":1789,"title":1790,"description":1791,"date":1778,"series":1779,"seriesOrder":691},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-04","Spring 学习笔记 04：JPA 关联映射与仓储测试","一对多、多对多怎么映射，谁负责维护外键，FetchType 与 CascadeType 该怎么选，顺带用 @DataJpaTest 与 AssertJ 把仓储层测起来。",{"path":1793,"title":1794,"description":1795,"date":1778,"series":1779,"seriesOrder":698},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-05","Spring 学习笔记 05：Spring MVC 与 Thymeleaf 入门","一次页面请求在 Spring MVC 里走了哪几步，@Controller 怎么把数据交给模板，@PathVariable 与 @RequestParam 怎么取参数，以及控制器为什么必须是无状态的。",{"path":1797,"title":1798,"description":1799,"date":1778,"series":1779,"seriesOrder":704},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-06","Spring 学习笔记 06：表单绑定与服务端校验","把表单字段绑到对象上，用 Bean Validation 注解做服务端校验，把错误信息显示回用户刚填的那一页，再补上自定义校验注解与全局异常处理。",{"path":1801,"title":1802,"description":1803,"date":1778,"series":1779,"seriesOrder":715},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-07","Spring 学习笔记 07：Thymeleaf 片段复用与国际化","用 th:fragment 与 th:insert\u002Fth:replace 把导航栏、页脚抽成片段，页面套用统一布局，再用 messages.properties 与 LocaleResolver 做中英文切换。",{"path":1805,"title":1806,"description":1807,"date":1778,"series":1779,"seriesOrder":846},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-08","Spring 学习笔记 08：HTTP Session 与会话状态管理","HTTP 无状态带来的问题，Cookie 与 Session 怎么分工，Spring 里读写会话的几种方式，以及会话固定、超时、多实例共享这些必须想清楚的事。",{"path":1809,"title":1810,"description":1811,"date":1778,"series":1779,"seriesOrder":976},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-09","Spring 学习笔记 09：拦截器 HandlerInterceptor","登录检查、权限判断、请求耗时统计这类横切逻辑该放哪：HandlerInterceptor 的三个回调各在什么时机执行，怎么注册与排除路径，以及它和过滤器的分工。",{"path":1813,"title":1814,"description":1815,"date":1778,"series":1779,"seriesOrder":982},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-10","Spring 学习笔记 10：Service 层与事务","一组数据库操作要么全成功要么全失败，靠 @Transactional 怎么做到；传播行为、隔离级别、回滚规则怎么选，以及自调用失效、乐观锁悲观锁这些实战里的坑。",{"path":1817,"title":1818,"description":1819,"date":1778,"series":1779,"seriesOrder":1096},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-11","Spring 学习笔记 11：REST API 基础——把 Service 暴露成接口","同一套 Service 怎么同时服务页面和客户端：@RestController 的路由与方法语义、状态码怎么给、DTO 怎么定，以及 MockMvc 端到端和切片测试的两种写法与各自的坑。",{"path":1821,"title":1822,"description":1823,"date":1778,"series":1779,"seriesOrder":1406},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-12","Spring 学习笔记 12：REST 进阶——版本、更新、跨域与文档化","接口能跑通之后要处理的几件事：URL 与响应结构怎么定、版本怎么演进、更新怎么做到可安全重试、CORS 怎么配、HATEOAS 与 Spring Data REST 该不该用。",{"path":1825,"title":1826,"description":1827,"date":1828,"series":1829,"seriesOrder":691},"\u002Fposts\u002Fjava\u002Fjava-functionalprogramming-notes","Java 函数式编程笔记：Lambda、函数式接口与 Streams","学习 Java 函数式编程时的个人笔记：Lambda 表达式的五步简写与方法引用、常见函数式接口、Stream 流水线与惰性求值，以及创建流、中间操作、终止操作与收集器的用法。示例代码均为自写，运行结果来自本地实测。","2026-10-13","Java 学习笔记",{"path":1831,"title":1832,"description":1833,"date":1828,"series":1829,"seriesOrder":665},"\u002Fposts\u002Fjava\u002Fjava-generics-notes","Java 泛型笔记：泛型方法、有界类型与泛型类","学习 Java 泛型时的个人笔记：从重复的重载方法与多态尝试讲起，再到泛型方法与类型推断、有界泛型类型、Comparable 与 Number 边界，以及泛型类在集合中的用法。示例代码均为自写，运行结果来自本地实测。",{"path":1835,"title":1836,"description":1837,"date":1608,"series":1615,"seriesOrder":691},"\u002Fposts\u002Fmysql\u002Fmysql-learning-04","MySQL 学习笔记 04：数据操纵（增删改）","MySQL 学习笔记第四篇：INSERT 插入（含批量插入与 INSERT ... SELECT）、UPDATE 更新、DELETE 删除，以及各自的坑。",{"path":1839,"title":1840,"description":1841,"date":1608,"series":1615,"seriesOrder":698},"\u002Fposts\u002Fmysql\u002Fmysql-learning-05","MySQL 学习笔记 05：DDL 概览","MySQL 学习笔记第五篇：数据定义语言概览——Schema、Table、View、Index、Constraints 这几类对象分别对应哪些 CREATE \u002F ALTER \u002F DROP 命令。",{"path":1843,"title":1844,"description":1845,"date":1608,"series":1615,"seriesOrder":704},"\u002Fposts\u002Fmysql\u002Fmysql-learning-06","MySQL 学习笔记 06：CREATE TABLE 与数据类型","MySQL 学习笔记第六篇：CREATE TABLE 的完整语法、常用数据类型的选型建议，以及外键的两种定义方式。",{"path":1613,"title":5,"description":1609,"date":1608,"series":1615,"seriesOrder":715},{"path":1848,"title":1849,"description":1850,"date":1608,"series":1615,"seriesOrder":846},"\u002Fposts\u002Fmysql\u002Fmysql-learning-08","MySQL 学习笔记 08：DROP 与 ALTER","MySQL 学习笔记第八篇：DROP 删除数据库对象、ALTER 修改表结构，以及常见的坑。",{"path":1852,"title":1853,"description":1854,"date":1608,"series":1615,"seriesOrder":976},"\u002Fposts\u002Fmysql\u002Fmysql-learning-09","MySQL 学习笔记 09：约束与数据完整性","MySQL 学习笔记第九篇：SQL 约束——NOT NULL、CHECK、PRIMARY KEY、FOREIGN KEY 等如何保证数据完整性，以及它们的对比。",{"path":1856,"title":1857,"description":1858,"date":1608,"series":1615,"seriesOrder":982},"\u002Fposts\u002Fmysql\u002Fmysql-learning-10","MySQL 学习笔记 10：视图","MySQL 学习笔记第十篇：视图（Views）的定义与特性、和表的对比、创建与使用，以及可更新视图的限制。",{"path":1860,"title":1861,"description":1862,"date":1608,"series":1615,"seriesOrder":1096},"\u002Fposts\u002Fmysql\u002Fmysql-learning-11","MySQL 学习笔记 11：存储过程","MySQL 学习笔记第十一篇：存储过程（Stored Procedures）的概念、创建与调用、带参数的写法，以及优缺点。",{"path":1864,"title":1865,"description":1866,"date":1867,"series":1615,"seriesOrder":179},"\u002Fposts\u002Fmysql\u002Fmysql-learning-01","MySQL 学习笔记 01：MySQL 基础知识","MySQL 学习笔记第一篇：MySQL Server 与 Workbench 的安装，数据库与 DBMS 的基本概念，关系模型、ACID 事务、常见数据类型，以及后续的学习路线。","2026-10-09",{"path":1869,"title":1870,"description":1871,"date":1867,"series":1615,"seriesOrder":429},"\u002Fposts\u002Fmysql\u002Fmysql-learning-02","MySQL 学习笔记 02：SELECT 查询与过滤","MySQL 学习笔记第二篇：SELECT 的基本语法与逻辑执行顺序，WHERE 过滤（比较、LIKE、IN、BETWEEN、NULL）、各种 JOIN、UNION 与子查询。",{"path":1873,"title":1874,"description":1875,"date":1867,"series":1615,"seriesOrder":665},"\u002Fposts\u002Fmysql\u002Fmysql-learning-03","MySQL 学习笔记 03：聚合、分组与输出控制","MySQL 学习笔记第三篇：COUNT\u002FSUM\u002FAVG 等聚合函数、GROUP BY 分组、HAVING 过滤、ORDER BY 与 LIMIT，以及一条 SELECT 的完整逻辑执行顺序。",{"path":1877,"title":1878,"description":1879,"date":1880,"series":1829,"seriesOrder":429},"\u002Fposts\u002Fjava\u002Fjava-oop-notes","Java 面向对象笔记：类、继承、多态与异常","学习 Java 面向对象编程时的个人笔记：类与对象、封装与访问修饰符、继承与多态、集合、抽象类与接口、异常处理。示例代码均为自写，运行结果来自本地实测。","2026-10-06",{"path":1882,"title":1883,"description":1884,"date":1885,"series":1829,"seriesOrder":179},"\u002Fposts\u002Fjava\u002Fjava-basics-notes","Java 基础语法笔记：从环境安装到方法与数组","学习 Java 基础语法时的个人笔记：JDK 安装、输入输出、变量与运算符、条件与循环、字符串、数组与方法。示例代码均为自写，运行结果来自本地实测。","2026-10-05",{"path":1887,"title":1888,"description":1889,"date":1890,"series":1891,"seriesOrder":179},"\u002Fposts\u002Fgit\u002Fgit-install-and-basics","Git 安装与基本使用","从装好 Git 到跑通日常提交流程，把最常用的几条命令和它们背后的三个区域讲清楚。","2026-10-04","Git 入门",{"path":1893,"title":1894,"description":1895,"date":1890,"series":1891,"seriesOrder":429},"\u002Fposts\u002Fgit\u002Flocal-remote-repository","本地远程仓库的部署","用自己的机器当 Git 服务器：建一个裸仓库、走 SSH 连上去，把本地提交推上去。",{"path":1897,"title":1898,"description":1899,"date":1890,"series":1900,"seriesOrder":179},"\u002Fposts\u002Fguide\u002Fguide-file-layout","文件都放在哪","文章、图片、封面分别放在仓库的什么位置，哪些目录可以放手改、哪些碰了会出问题。","博客使用指南",{"path":1902,"title":1903,"description":1904,"date":1890,"series":1900,"seriesOrder":429},"\u002Fposts\u002Fguide\u002Fguide-frontmatter","frontmatter 每个字段是什么意思","文章开头那段被横线包住的字段，逐个说明作用、默认行为和写错的后果。",{"path":1906,"title":1907,"description":1908,"date":1890,"series":1900,"seriesOrder":665},"\u002Fposts\u002Fguide\u002Fguide-markdown","正文怎么写 —— Markdown 与排版效果","标题、链接、图片、代码、表格分别怎么写，以及本站对它们做了哪些自定义处理。",1793268186220]