[{"data":1,"prerenderedAt":4787},["ShallowReactive",2],{"navigation_docs":3,"-tu-hoc-database-mysql-dac-thu-performance":420,"-tu-hoc-database-mysql-dac-thu-performance-surround":4782},[4,153],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":26},"Phỏng vấn .NET","i-lucide-briefcase","\u002Fphong-van","1.phong-van",[10,27,66,111,128],{"title":11,"icon":12,"path":13,"stem":14,"children":15,"page":26},"Tổng quan","i-lucide-compass","\u002Fphong-van\u002Ftong-quan","1.phong-van\u002F1.tong-quan",[16,21],{"title":17,"path":18,"stem":19,"icon":20},"Thị trường HCM","\u002Fphong-van\u002Ftong-quan\u002Fthi-truong","1.phong-van\u002F1.tong-quan\u002F1.thi-truong","i-lucide-trending-up",{"title":22,"path":23,"stem":24,"icon":25},"Lộ trình 7 ngày","\u002Fphong-van\u002Ftong-quan\u002Flo-trinh","1.phong-van\u002F1.tong-quan\u002F2.lo-trinh","i-lucide-calendar-days",false,{"title":22,"icon":25,"path":28,"stem":29,"children":30,"page":26},"\u002Fphong-van\u002Flo-trinh","1.phong-van\u002F2.lo-trinh",[31,36,41,46,51,56,61],{"title":32,"path":33,"stem":34,"icon":35},"Ngày 1 — C# cơ bản","\u002Fphong-van\u002Flo-trinh\u002Fngay-1-csharp-co-ban","1.phong-van\u002F2.lo-trinh\u002F1.ngay-1-csharp-co-ban","i-lucide-1",{"title":37,"path":38,"stem":39,"icon":40},"Ngày 2 — OOP và SOLID","\u002Fphong-van\u002Flo-trinh\u002Fngay-2-oop-solid","1.phong-van\u002F2.lo-trinh\u002F2.ngay-2-oop-solid","i-lucide-2",{"title":42,"path":43,"stem":44,"icon":45},"Ngày 3 — C# nâng cao","\u002Fphong-van\u002Flo-trinh\u002Fngay-3-csharp-nang-cao","1.phong-van\u002F2.lo-trinh\u002F3.ngay-3-csharp-nang-cao","i-lucide-3",{"title":47,"path":48,"stem":49,"icon":50},"Ngày 4 — Database và SQL","\u002Fphong-van\u002Flo-trinh\u002Fngay-4-database-sql","1.phong-van\u002F2.lo-trinh\u002F4.ngay-4-database-sql","i-lucide-4",{"title":52,"path":53,"stem":54,"icon":55},"Ngày 5 — ASP.NET Core và EF Core","\u002Fphong-van\u002Flo-trinh\u002Fngay-5-aspnet-efcore","1.phong-van\u002F2.lo-trinh\u002F5.ngay-5-aspnet-efcore","i-lucide-5",{"title":57,"path":58,"stem":59,"icon":60},"Ngày 6 — DSA coding","\u002Fphong-van\u002Flo-trinh\u002Fngay-6-dsa","1.phong-van\u002F2.lo-trinh\u002F6.ngay-6-dsa","i-lucide-6",{"title":62,"path":63,"stem":64,"icon":65},"Ngày 7 — Mock và HR","\u002Fphong-van\u002Flo-trinh\u002Fngay-7-mock-interview","1.phong-van\u002F2.lo-trinh\u002F7.ngay-7-mock-interview","i-lucide-7",{"title":67,"icon":68,"path":69,"stem":70,"children":71,"page":26},"Ngân hàng câu hỏi","i-lucide-database","\u002Fphong-van\u002Fngan-hang-cau-hoi","1.phong-van\u002F3.ngan-hang-cau-hoi",[72,77,82,87,92,97,102,107],{"title":73,"path":74,"stem":75,"icon":76},"C# (60+ câu)","\u002Fphong-van\u002Fngan-hang-cau-hoi\u002Fcsharp","1.phong-van\u002F3.ngan-hang-cau-hoi\u002F1.csharp","i-lucide-hash",{"title":78,"path":79,"stem":80,"icon":81},"OOP và SOLID (40+)","\u002Fphong-van\u002Fngan-hang-cau-hoi\u002Foop","1.phong-van\u002F3.ngan-hang-cau-hoi\u002F2.oop","i-lucide-shapes",{"title":83,"path":84,"stem":85,"icon":86},"SQL (40+)","\u002Fphong-van\u002Fngan-hang-cau-hoi\u002Fsql","1.phong-van\u002F3.ngan-hang-cau-hoi\u002F3.sql","i-lucide-database-zap",{"title":88,"path":89,"stem":90,"icon":91},"ASP.NET và EF (50+)","\u002Fphong-van\u002Fngan-hang-cau-hoi\u002Faspnet-ef","1.phong-van\u002F3.ngan-hang-cau-hoi\u002F4.aspnet-ef","i-lucide-server",{"title":93,"path":94,"stem":95,"icon":96},"DSA và Coding (30+)","\u002Fphong-van\u002Fngan-hang-cau-hoi\u002Fdsa-coding","1.phong-van\u002F3.ngan-hang-cau-hoi\u002F5.dsa-coding","i-lucide-binary",{"title":98,"path":99,"stem":100,"icon":101},"HR và Soft skill (25+)","\u002Fphong-van\u002Fngan-hang-cau-hoi\u002Fhr-behavior","1.phong-van\u002F3.ngan-hang-cau-hoi\u002F6.hr-behavior","i-lucide-users",{"title":103,"path":104,"stem":105,"icon":106},"Design Pattern (55+)","\u002Fphong-van\u002Fngan-hang-cau-hoi\u002Fdesign-pattern","1.phong-van\u002F3.ngan-hang-cau-hoi\u002F7.design-pattern","i-lucide-puzzle",{"title":108,"path":109,"stem":110,"icon":91},"ASP.NET MVC và Web API","\u002Fphong-van\u002Fngan-hang-cau-hoi\u002Faspnet-mvc-webapi","1.phong-van\u002F3.ngan-hang-cau-hoi\u002F8.aspnet-mvc-webapi",{"title":112,"itle":113,"icon":114,"path":115,"stem":116,"children":117,"page":26},"Tai Lieu","Tài liệu và Cheat sheet","i-lucide-book-marked","\u002Fphong-van\u002Ftai-lieu","1.phong-van\u002F4.tai-lieu",[118,123],{"title":119,"path":120,"stem":121,"icon":122},"Cheat Sheet","\u002Fphong-van\u002Ftai-lieu\u002Fcheatsheet","1.phong-van\u002F4.tai-lieu\u002F1.cheatsheet","i-lucide-clipboard-list",{"title":124,"path":125,"stem":126,"icon":127},"Nguồn tham khảo","\u002Fphong-van\u002Ftai-lieu\u002Fnguon","1.phong-van\u002F4.tai-lieu\u002F2.nguon","i-lucide-link",{"title":129,"icon":130,"path":131,"stem":132,"children":133,"page":26},"Phỏng vấn English","i-lucide-globe","\u002Fphong-van\u002Fenglish-interview","1.phong-van\u002F5.english-interview",[134,138,143,148],{"title":135,"path":136,"stem":137,"icon":130},"Tổng quan English","\u002Fphong-van\u002Fenglish-interview\u002Ftong-quan","1.phong-van\u002F5.english-interview\u002F1.tong-quan",{"title":139,"path":140,"stem":141,"icon":142},"Vocabulary","\u002Fphong-van\u002Fenglish-interview\u002Fvocabulary","1.phong-van\u002F5.english-interview\u002F2.vocabulary","i-lucide-book-a",{"title":144,"path":145,"stem":146,"icon":147},"Technical Q và A (EN)","\u002Fphong-van\u002Fenglish-interview\u002Ftechnical-en","1.phong-van\u002F5.english-interview\u002F3.technical-en","i-lucide-code",{"title":149,"path":150,"stem":151,"icon":152},"Behavioral (EN)","\u002Fphong-van\u002Fenglish-interview\u002Fbehavioral-en","1.phong-van\u002F5.english-interview\u002F4.behavioral-en","i-lucide-message-square-text",{"title":154,"icon":155,"path":156,"stem":157,"children":158},"Tự học","i-lucide-graduation-cap","\u002Ftu-hoc","2.tu-hoc",[159,161,287,310,333,357,384],{"title":154,"path":156,"stem":160,"icon":155},"2.tu-hoc\u002Findex",{"title":162,"icon":68,"path":163,"stem":164,"children":165},"Database","\u002Ftu-hoc\u002Fdatabase","2.tu-hoc\u002F1.database\u002Findex",[166,167,197,222,243,248],{"title":162,"path":163,"stem":164,"icon":68},{"title":168,"icon":91,"path":169,"stem":170,"children":171,"page":26},"SQL Server","\u002Ftu-hoc\u002Fdatabase\u002Fsql-server","2.tu-hoc\u002F1.database\u002F2.sql-server",[172,177,182,187,192],{"title":173,"path":174,"stem":175,"icon":176},"1. Giới thiệu và Cài đặt","\u002Ftu-hoc\u002Fdatabase\u002Fsql-server\u002Fgioi-thieu-cai-dat","2.tu-hoc\u002F1.database\u002F2.sql-server\u002F1.gioi-thieu-cai-dat","i-lucide-play-circle",{"title":178,"path":179,"stem":180,"icon":181},"2. Cú pháp cơ bản","\u002Ftu-hoc\u002Fdatabase\u002Fsql-server\u002Fcu-phap-co-ban","2.tu-hoc\u002F1.database\u002F2.sql-server\u002F2.cu-phap-co-ban","i-lucide-code-2",{"title":183,"path":184,"stem":185,"icon":186},"3. Truy vấn nâng cao","\u002Ftu-hoc\u002Fdatabase\u002Fsql-server\u002Ftruy-van-nang-cao","2.tu-hoc\u002F1.database\u002F2.sql-server\u002F3.truy-van-nang-cao","i-lucide-git-merge",{"title":188,"path":189,"stem":190,"icon":191},"4. Đặc thù và Performance","\u002Ftu-hoc\u002Fdatabase\u002Fsql-server\u002Fdac-thu-performance","2.tu-hoc\u002F1.database\u002F2.sql-server\u002F4.dac-thu-performance","i-lucide-gauge",{"title":193,"path":194,"stem":195,"icon":196},"5. Q và A phỏng vấn","\u002Ftu-hoc\u002Fdatabase\u002Fsql-server\u002Fcau-hoi-phong-van","2.tu-hoc\u002F1.database\u002F2.sql-server\u002F5.cau-hoi-phong-van","i-lucide-message-circle-question",{"title":198,"icon":199,"path":200,"stem":201,"children":202,"page":26},"PostgreSQL","i-simple-icons-postgresql","\u002Ftu-hoc\u002Fdatabase\u002Fpostgresql","2.tu-hoc\u002F1.database\u002F3.postgresql",[203,206,209,212,215,218],{"title":173,"path":204,"stem":205,"icon":176},"\u002Ftu-hoc\u002Fdatabase\u002Fpostgresql\u002Fgioi-thieu-cai-dat","2.tu-hoc\u002F1.database\u002F3.postgresql\u002F1.gioi-thieu-cai-dat",{"title":178,"path":207,"stem":208,"icon":181},"\u002Ftu-hoc\u002Fdatabase\u002Fpostgresql\u002Fcu-phap-co-ban","2.tu-hoc\u002F1.database\u002F3.postgresql\u002F2.cu-phap-co-ban",{"title":183,"path":210,"stem":211,"icon":186},"\u002Ftu-hoc\u002Fdatabase\u002Fpostgresql\u002Ftruy-van-nang-cao","2.tu-hoc\u002F1.database\u002F3.postgresql\u002F3.truy-van-nang-cao",{"title":188,"path":213,"stem":214,"icon":191},"\u002Ftu-hoc\u002Fdatabase\u002Fpostgresql\u002Fdac-thu-performance","2.tu-hoc\u002F1.database\u002F3.postgresql\u002F4.dac-thu-performance",{"title":193,"path":216,"stem":217,"icon":196},"\u002Ftu-hoc\u002Fdatabase\u002Fpostgresql\u002Fcau-hoi-phong-van","2.tu-hoc\u002F1.database\u002F3.postgresql\u002F5.cau-hoi-phong-van",{"title":219,"path":220,"stem":221,"icon":96},"6. Tích hợp C# và Postgres","\u002Ftu-hoc\u002Fdatabase\u002Fpostgresql\u002Ftich-hop-csharp","2.tu-hoc\u002F1.database\u002F3.postgresql\u002F6.tich-hop-csharp",{"title":223,"icon":224,"path":225,"stem":226,"children":227,"page":26},"MySQL","i-simple-icons-mysql","\u002Ftu-hoc\u002Fdatabase\u002Fmysql","2.tu-hoc\u002F1.database\u002F4.mysql",[228,231,234,237,240],{"title":173,"path":229,"stem":230,"icon":176},"\u002Ftu-hoc\u002Fdatabase\u002Fmysql\u002Fgioi-thieu-cai-dat","2.tu-hoc\u002F1.database\u002F4.mysql\u002F1.gioi-thieu-cai-dat",{"title":178,"path":232,"stem":233,"icon":181},"\u002Ftu-hoc\u002Fdatabase\u002Fmysql\u002Fcu-phap-co-ban","2.tu-hoc\u002F1.database\u002F4.mysql\u002F2.cu-phap-co-ban",{"title":183,"path":235,"stem":236,"icon":186},"\u002Ftu-hoc\u002Fdatabase\u002Fmysql\u002Ftruy-van-nang-cao","2.tu-hoc\u002F1.database\u002F4.mysql\u002F3.truy-van-nang-cao",{"title":188,"path":238,"stem":239,"icon":191},"\u002Ftu-hoc\u002Fdatabase\u002Fmysql\u002Fdac-thu-performance","2.tu-hoc\u002F1.database\u002F4.mysql\u002F4.dac-thu-performance",{"title":193,"path":241,"stem":242,"icon":196},"\u002Ftu-hoc\u002Fdatabase\u002Fmysql\u002Fcau-hoi-phong-van","2.tu-hoc\u002F1.database\u002F4.mysql\u002F5.cau-hoi-phong-van",{"title":244,"path":245,"stem":246,"icon":247},"So sánh 3 RDBMS","\u002Ftu-hoc\u002Fdatabase\u002Fso-sanh","2.tu-hoc\u002F1.database\u002F5.so-sanh","i-lucide-columns-3",{"title":249,"path":250,"stem":251,"children":252,"icon":254},"Supabase (BaaS)","\u002Ftu-hoc\u002Fdatabase\u002Fsupabase","2.tu-hoc\u002F1.database\u002F6.supabase\u002Findex",[253,255,259,264,269,274,278,283],{"title":249,"path":250,"stem":251,"icon":254},"i-simple-icons-supabase",{"title":256,"path":257,"stem":258,"icon":68},"1. Database","\u002Ftu-hoc\u002Fdatabase\u002Fsupabase\u002Fdatabase","2.tu-hoc\u002F1.database\u002F6.supabase\u002F1.database",{"title":260,"path":261,"stem":262,"icon":263},"2. Authentication","\u002Ftu-hoc\u002Fdatabase\u002Fsupabase\u002Fauthentication","2.tu-hoc\u002F1.database\u002F6.supabase\u002F2.authentication","i-lucide-key",{"title":265,"path":266,"stem":267,"icon":268},"3. Storage & Realtime","\u002Ftu-hoc\u002Fdatabase\u002Fsupabase\u002Fstorage-realtime","2.tu-hoc\u002F1.database\u002F6.supabase\u002F3.storage-realtime","i-lucide-zap",{"title":270,"path":271,"stem":272,"icon":273},"4. Edge Functions & AI","\u002Ftu-hoc\u002Fdatabase\u002Fsupabase\u002Ffunctions-ai","2.tu-hoc\u002F1.database\u002F6.supabase\u002F4.functions-ai","i-lucide-brain",{"title":275,"path":276,"stem":277,"icon":181},"5. API & SDKs","\u002Ftu-hoc\u002Fdatabase\u002Fsupabase\u002Fapi-sdk","2.tu-hoc\u002F1.database\u002F6.supabase\u002F5.api-sdk",{"title":279,"path":280,"stem":281,"icon":282},"6. Platform & Deploy","\u002Ftu-hoc\u002Fdatabase\u002Fsupabase\u002Fplatform-deploy","2.tu-hoc\u002F1.database\u002F6.supabase\u002F6.platform-deploy","i-lucide-cloud",{"title":284,"path":285,"stem":286,"icon":196},"7. Interview & Q&A","\u002Ftu-hoc\u002Fdatabase\u002Fsupabase\u002Finterview","2.tu-hoc\u002F1.database\u002F6.supabase\u002F7.interview",{"title":288,"icon":289,"path":290,"stem":291,"children":292},"TypeScript","i-simple-icons-typescript","\u002Ftu-hoc\u002Ftypescript","2.tu-hoc\u002F2.typescript\u002Findex",[293,294,297,301,306],{"title":288,"path":290,"stem":291,"icon":289},{"title":173,"path":295,"stem":296,"icon":176},"\u002Ftu-hoc\u002Ftypescript\u002Fgioi-thieu-cai-dat","2.tu-hoc\u002F2.typescript\u002F1.gioi-thieu-cai-dat",{"title":298,"path":299,"stem":300,"icon":81},"2. Type System","\u002Ftu-hoc\u002Ftypescript\u002Ftype-system","2.tu-hoc\u002F2.typescript\u002F2.type-system",{"title":302,"path":303,"stem":304,"icon":305},"3. Advanced types","\u002Ftu-hoc\u002Ftypescript\u002Fadvanced-types","2.tu-hoc\u002F2.typescript\u002F3.advanced-types","i-lucide-braces",{"title":307,"path":308,"stem":309,"icon":196},"4. Q và A phỏng vấn","\u002Ftu-hoc\u002Ftypescript\u002Fcau-hoi-phong-van","2.tu-hoc\u002F2.typescript\u002F4.cau-hoi-phong-van",{"title":311,"icon":81,"path":312,"stem":313,"children":314},"OOP","\u002Ftu-hoc\u002Foop","2.tu-hoc\u002F3.oop\u002Findex",[315,316,321,326,330],{"title":311,"path":312,"stem":313,"icon":81},{"title":317,"path":318,"stem":319,"icon":320},"1. Bốn tính chất","\u002Ftu-hoc\u002Foop\u002F4-tinh-chat","2.tu-hoc\u002F3.oop\u002F1.4-tinh-chat","i-lucide-circle-dot",{"title":322,"path":323,"stem":324,"icon":325},"2. SOLID","\u002Ftu-hoc\u002Foop\u002Fsolid","2.tu-hoc\u002F3.oop\u002F2.solid","i-lucide-shield",{"title":327,"path":328,"stem":329,"icon":106},"3. Design Pattern","\u002Ftu-hoc\u002Foop\u002Fdesign-pattern","2.tu-hoc\u002F3.oop\u002F3.design-pattern",{"title":307,"path":331,"stem":332,"icon":196},"\u002Ftu-hoc\u002Foop\u002Fcau-hoi-phong-van","2.tu-hoc\u002F3.oop\u002F4.cau-hoi-phong-van",{"title":334,"icon":305,"path":335,"stem":336,"children":337},"Generic","\u002Ftu-hoc\u002Fgeneric","2.tu-hoc\u002F4.generic\u002Findex",[338,339,344,349,354],{"title":334,"path":335,"stem":336,"icon":305},{"title":340,"path":341,"stem":342,"icon":343},"1. Khái niệm và cú pháp","\u002Ftu-hoc\u002Fgeneric\u002Fkhai-niem","2.tu-hoc\u002F4.generic\u002F1.khai-niem","i-lucide-book",{"title":345,"path":346,"stem":347,"icon":348},"2. Constraint và Variance","\u002Ftu-hoc\u002Fgeneric\u002Fconstraint-variance","2.tu-hoc\u002F4.generic\u002F2.constraint-variance","i-lucide-filter",{"title":350,"path":351,"stem":352,"icon":353},"3. C# vs TS","\u002Ftu-hoc\u002Fgeneric\u002Fso-sanh","2.tu-hoc\u002F4.generic\u002F3.so-sanh","i-lucide-git-compare",{"title":307,"path":355,"stem":356,"icon":196},"\u002Ftu-hoc\u002Fgeneric\u002Fcau-hoi-phong-van","2.tu-hoc\u002F4.generic\u002F4.cau-hoi-phong-van",{"title":358,"icon":359,"path":360,"stem":361,"children":362},"Nuxt 4","i-simple-icons-nuxt","\u002Ftu-hoc\u002Fnuxt-4","2.tu-hoc\u002F5.nuxt-4\u002Findex",[363,364,367,372,376,379],{"title":358,"path":360,"stem":361,"icon":359},{"title":173,"path":365,"stem":366,"icon":176},"\u002Ftu-hoc\u002Fnuxt-4\u002Fgioi-thieu-cai-dat","2.tu-hoc\u002F5.nuxt-4\u002F1.gioi-thieu-cai-dat",{"title":368,"path":369,"stem":370,"icon":371},"2. Routing và Rendering","\u002Ftu-hoc\u002Fnuxt-4\u002Frouting-rendering","2.tu-hoc\u002F5.nuxt-4\u002F2.routing-rendering","i-lucide-route",{"title":373,"path":374,"stem":375,"icon":91},"3. Data và Server","\u002Ftu-hoc\u002Fnuxt-4\u002Fdata-server","2.tu-hoc\u002F5.nuxt-4\u002F3.data-server",{"title":307,"path":377,"stem":378,"icon":196},"\u002Ftu-hoc\u002Fnuxt-4\u002Fcau-hoi-phong-van","2.tu-hoc\u002F5.nuxt-4\u002F4.cau-hoi-phong-van",{"title":380,"path":381,"stem":382,"icon":383},"5. Practical Todo App","\u002Ftu-hoc\u002Fnuxt-4\u002Fpractical-todo-app","2.tu-hoc\u002F5.nuxt-4\u002F5.practical-todo-app","i-lucide-check-square",{"title":385,"path":386,"stem":387,"children":388,"icon":390},"Tailwind CSS","\u002Ftu-hoc\u002Ftailwindcss","2.tu-hoc\u002F6.tailwindcss\u002Findex",[389,391,396,401,406,411,416],{"title":385,"path":386,"stem":387,"icon":390},"i-simple-icons-tailwindcss",{"title":392,"path":393,"stem":394,"icon":395},"1. Layout & Spacing","\u002Ftu-hoc\u002Ftailwindcss\u002Flayout-spacing","2.tu-hoc\u002F6.tailwindcss\u002F1.layout-spacing","i-lucide-layout",{"title":397,"path":398,"stem":399,"icon":400},"2. Typography & Colors","\u002Ftu-hoc\u002Ftailwindcss\u002Ftypography-colors","2.tu-hoc\u002F6.tailwindcss\u002F2.typography-colors","i-lucide-type",{"title":402,"path":403,"stem":404,"icon":405},"3. Interactive & Effects","\u002Ftu-hoc\u002Ftailwindcss\u002Finteractive-effects","2.tu-hoc\u002F6.tailwindcss\u002F3.interactive-effects","i-lucide-sparkles",{"title":407,"path":408,"stem":409,"icon":410},"4. Responsive & Dark Mode","\u002Ftu-hoc\u002Ftailwindcss\u002Fresponsive-darkmode","2.tu-hoc\u002F6.tailwindcss\u002F4.responsive-darkmode","i-lucide-monitor-smartphone",{"title":412,"path":413,"stem":414,"icon":415},"5. Customization & Theme","\u002Ftu-hoc\u002Ftailwindcss\u002Fcustomization","2.tu-hoc\u002F6.tailwindcss\u002F5.customization","i-lucide-settings-2",{"title":417,"path":418,"stem":419,"icon":181},"6. Practical Projects","\u002Ftu-hoc\u002Ftailwindcss\u002Fpractical-projects","2.tu-hoc\u002F6.tailwindcss\u002F6.practical-projects",{"id":421,"title":422,"body":423,"description":4775,"extension":4776,"links":4777,"meta":4778,"navigation":4779,"path":238,"seo":4780,"stem":239,"__hash__":4781},"docs\u002F2.tu-hoc\u002F1.database\u002F4.mysql\u002F4.dac-thu-performance.md","Đặc thù MySQL và Performance",{"type":424,"value":425,"toc":4699},"minimark",[426,431,436,483,487,507,511,514,518,533,537,540,557,561,568,591,595,599,606,632,640,654,658,665,716,751,755,762,791,805,809,812,868,876,880,886,910,913,931,955,959,962,1034,1038,1041,1084,1088,1108,1119,1123,1138,1141,1144,1148,1200,1204,1209,1244,1247,1252,1255,1324,1366,1371,1400,1403,1407,1410,1463,1499,1503,1510,1525,1529,1532,1536,1648,1652,1829,1833,1888,1906,1910,1963,1977,1981,1984,2077,2081,2085,2182,2186,2277,2321,2325,2516,2520,2642,2646,2717,2731,2735,2739,2769,2773,2814,2905,2909,2914,3019,3024,3053,3058,3112,3122,3126,3130,3202,3206,3209,3262,3266,3269,3320,3342,3346,3349,3426,3430,3433,3437,3485,3489,3621,3625,3687,3709,3713,3818,3842,3846,3850,3856,3859,3888,3957,3978,3982,3996,4000,4055,4059,4088,4144,4148,4196,4200,4204,4302,4308,4312,4315,4319,4322,4326,4337,4352,4356,4363,4367,4374,4378,4384,4440,4459,4463,4687,4695],[427,428,430],"h2",{"id":429},"_1-innodb-internals-cốt-lõi","1. InnoDB internals — cốt lõi",[432,433,435],"h3",{"id":434},"buffer-pool","Buffer Pool",[437,438,441,450,480],"callout",{"color":439,"icon":440},"warning","i-lucide-star",[442,443,444,445,449],"p",{},"⭐ ",[446,447,448],"strong",{},"Buffer Pool là vùng RAM InnoDB cache data + index page."," Là tham số tuning quan trọng NHẤT:",[451,452,457],"pre",{"className":453,"code":454,"language":455,"meta":456,"style":456},"language-ini shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","[mysqld]\ninnodb_buffer_pool_size = 6G              # 50-70% RAM cho dedicated DB server\ninnodb_buffer_pool_instances = 8          # chia nhỏ để giảm mutex contention\n","ini","",[458,459,460,468,474],"code",{"__ignoreMap":456},[461,462,465],"span",{"class":463,"line":464},"line",1,[461,466,467],{},"[mysqld]\n",[461,469,471],{"class":463,"line":470},2,[461,472,473],{},"innodb_buffer_pool_size = 6G              # 50-70% RAM cho dedicated DB server\n",[461,475,477],{"class":463,"line":476},3,[461,478,479],{},"innodb_buffer_pool_instances = 8          # chia nhỏ để giảm mutex contention\n",[442,481,482],{},"Buffer pool nhỏ → cache miss → IO disk → chậm.",[432,484,486],{"id":485},"redo-log-undo-log","Redo log + Undo log",[488,489,490,501],"ul",{},[491,492,493,496,497,500],"li",{},[446,494,495],{},"Redo log"," (file ",[458,498,499],{},"ib_logfile","): mọi thay đổi vật lý ghi vào trước khi flush data page → crash recovery.",[491,502,503,506],{},[446,504,505],{},"Undo log",": lưu version cũ cho MVCC và ROLLBACK.",[432,508,510],{"id":509},"page-16kb-default","Page (16KB default)",[442,512,513],{},"InnoDB lưu data + index ở page 16KB. PK là clustered → data và clustered index trong cùng B-tree.",[432,515,517],{"id":516},"doublewrite-buffer","Doublewrite Buffer",[442,519,520,521,524,525,528,529,532],{},"InnoDB ghi page ra doublewrite buffer trước khi ghi vào data file thật — bảo vệ chống ",[446,522,523],{},"torn page"," (page bị ghi dở khi crash). Trên SSD hiện đại, có thể tắt với ",[458,526,527],{},"innodb_doublewrite = 0"," nếu filesystem hỗ trợ atomic write (như ZFS, ext4 với ",[458,530,531],{},"data=journal",").",[432,534,536],{"id":535},"change-buffer","Change Buffer",[442,538,539],{},"Cache các thay đổi với secondary index page chưa có trong buffer pool. Merge khi page được load vào buffer pool → giảm IO cho secondary index update. Kiểm tra hiệu quả:",[451,541,545],{"className":542,"code":543,"language":544,"meta":456,"style":456},"language-sql shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","SHOW ENGINE INNODB STATUS\\G\n-- Xem \"INSERT BUFFER AND ADAPTIVE HASH INDEX\"\n","sql",[458,546,547,552],{"__ignoreMap":456},[461,548,549],{"class":463,"line":464},[461,550,551],{},"SHOW ENGINE INNODB STATUS\\G\n",[461,553,554],{"class":463,"line":470},[461,555,556],{},"-- Xem \"INSERT BUFFER AND ADAPTIVE HASH INDEX\"\n",[427,558,560],{"id":559},"_2-mvcc-trong-innodb","2. MVCC trong InnoDB",[442,562,563,564,567],{},"InnoDB MVCC qua ",[446,565,566],{},"undo log",". UPDATE\u002FDELETE để lại version cũ trong undo. SELECT trong REPEATABLE READ thấy snapshot tại thời điểm transaction bắt đầu.",[437,569,571,581],{"color":570,"icon":440},"primary",[442,572,444,573,576,577],{},[446,574,575],{},"Câu PV:"," ",[578,579,580],"em",{},"\"InnoDB MVCC khác Postgres MVCC ra sao?\"",[582,583,584],"blockquote",{},[442,585,586,587,590],{},"\"Postgres MVCC để row dead trong bảng chính → cần VACUUM thường xuyên reclaim space. InnoDB lưu version cũ trong ",[446,588,589],{},"undo tablespace"," riêng → bảng chính sạch hơn, ít bloat. Trade-off: undo log có thể phình lên nếu long transaction, gây lock và slowness.\"",[427,592,594],{"id":593},"_3-index-trong-innodb-chi-tiết","3. Index trong InnoDB — chi tiết",[432,596,598],{"id":597},"_31-b-tree-index-mặc-định","3.1 B-tree index (mặc định)",[442,600,601,602,605],{},"InnoDB dùng ",[446,603,604],{},"B+tree"," làm cấu trúc index mặc định cho cả clustered index (PK) và secondary index. Đặc điểm:",[488,607,608,614,620,626],{},[491,609,610,613],{},[446,611,612],{},"Balanced tree"," — mọi leaf node cùng depth → lookup O(log n).",[491,615,616,619],{},[446,617,618],{},"Sorted"," — data trong leaf node được sắp xếp → hỗ trợ range scan, ORDER BY, GROUP BY.",[491,621,622,625],{},[446,623,624],{},"Clustered index"," — PK chính là bảng: leaf node của PK index chứa toàn bộ row data.",[491,627,628,631],{},[446,629,630],{},"Secondary index"," — leaf node chứa PK value → mọi lookup qua secondary index cần thêm 1 lần seek về clustered index để lấy full row.",[451,633,638],{"className":634,"code":636,"language":637},[635],"language-text","Clustered index (PK) leaf:          (id=1, name='A', email='a@x.com')\nSecondary index leaf (idx_email):   (email='a@x.com', PK=1)\n                                              ↓ seek PK=1 về clustered\n                                              → (id=1, name='A', email='a@x.com')\n","text",[458,639,636],{"__ignoreMap":456},[437,641,642,649],{"color":439,"icon":440},[442,643,444,644,576,646],{},[446,645,575],{},[578,647,648],{},"\"Tại sao nên chọn PK là số tăng dần (AUTO_INCREMENT) thay vì UUID?\"",[582,650,651],{},[442,652,653],{},"\"PK trong InnoDB là clustered index — data được sắp xếp vật lý theo PK. UUID ngẫu nhiên → INSERT vào giữa page → page split liên tục → fragmentation, buffer pool lãng phí. AUTO_INCREMENT tăng dần → INSERT vào cuối → page fill đều, ít page split, index compact hơn.\"",[432,655,657],{"id":656},"_32-composite-index-leftmost-prefix-rule","3.2 Composite index & leftmost prefix rule",[442,659,660,661,664],{},"Composite index là index trên nhiều cột. MySQL dùng index theo ",[446,662,663],{},"leftmost prefix rule"," — chỉ dùng được index nếu WHERE reference các cột từ trái qua phải trong định nghĩa index.",[451,666,668],{"className":542,"code":667,"language":544,"meta":456,"style":456},"CREATE INDEX idx_abc ON orders(user_id, status, created_at);\n\n-- ✅ Dùng được idx_abc: WHERE user_id = 1\n-- ✅ Dùng được idx_abc: WHERE user_id = 1 AND status = 'done'\n-- ✅ Dùng được idx_abc: WHERE user_id = 1 AND created_at > '2025-01-01'\n-- ❌ KHÔNG dùng idx_abc: WHERE status = 'done'                     (thiếu leftmost)\n-- ❌ KHÔNG dùng idx_abc: WHERE user_id = 1 OR status = 'done'      (OR phá leftmost)\n-- ⚠️ Dùng 1 phần:       WHERE user_id = 1 AND created_at > '...'  (skip cột giữa → chỉ dùng user_id prefix)\n",[458,669,670,675,681,686,692,698,704,710],{"__ignoreMap":456},[461,671,672],{"class":463,"line":464},[461,673,674],{},"CREATE INDEX idx_abc ON orders(user_id, status, created_at);\n",[461,676,677],{"class":463,"line":470},[461,678,680],{"emptyLinePlaceholder":679},true,"\n",[461,682,683],{"class":463,"line":476},[461,684,685],{},"-- ✅ Dùng được idx_abc: WHERE user_id = 1\n",[461,687,689],{"class":463,"line":688},4,[461,690,691],{},"-- ✅ Dùng được idx_abc: WHERE user_id = 1 AND status = 'done'\n",[461,693,695],{"class":463,"line":694},5,[461,696,697],{},"-- ✅ Dùng được idx_abc: WHERE user_id = 1 AND created_at > '2025-01-01'\n",[461,699,701],{"class":463,"line":700},6,[461,702,703],{},"-- ❌ KHÔNG dùng idx_abc: WHERE status = 'done'                     (thiếu leftmost)\n",[461,705,707],{"class":463,"line":706},7,[461,708,709],{},"-- ❌ KHÔNG dùng idx_abc: WHERE user_id = 1 OR status = 'done'      (OR phá leftmost)\n",[461,711,713],{"class":463,"line":712},8,[461,714,715],{},"-- ⚠️ Dùng 1 phần:       WHERE user_id = 1 AND created_at > '...'  (skip cột giữa → chỉ dùng user_id prefix)\n",[437,717,720,725],{"color":718,"icon":719},"success","i-lucide-lightbulb",[442,721,722],{},[446,723,724],{},"Mẹo thiết kế composite index:",[488,726,727,734,740],{},[491,728,729,730,733],{},"Cột ",[458,731,732],{},"="," điều kiện equality đứng trước, cột range\u002Fsort đứng sau.",[491,735,736,737,739],{},"Cột có cardinality cao đứng trước (nhưng ",[458,738,732],{}," vẫn ưu tiên hơn cardinality).",[491,741,742,743,746,747,750],{},"WHERE ",[458,744,745],{},"a = ? AND b > ? ORDER BY c"," → index ",[458,748,749],{},"(a, b, c)",".",[432,752,754],{"id":753},"_33-covering-index","3.3 Covering index",[442,756,757,758,761],{},"Covering index là index chứa ",[446,759,760],{},"tất cả cột"," mà query cần → không cần lookup về clustered index. Rất quan trọng trong InnoDB vì tránh được double-lookup.",[451,763,765],{"className":542,"code":764,"language":544,"meta":456,"style":456},"-- Query: SELECT total FROM orders WHERE user_id = ? AND created_at > ?\nCREATE INDEX idx_cover ON orders(user_id, created_at, total);\n\n-- EXPLAIN hiển thị \"Using index\" → covering, không đụng vào clustered index\nEXPLAIN SELECT total FROM orders WHERE user_id = 1 AND created_at > '2025-01-01';\n",[458,766,767,772,777,781,786],{"__ignoreMap":456},[461,768,769],{"class":463,"line":464},[461,770,771],{},"-- Query: SELECT total FROM orders WHERE user_id = ? AND created_at > ?\n",[461,773,774],{"class":463,"line":470},[461,775,776],{},"CREATE INDEX idx_cover ON orders(user_id, created_at, total);\n",[461,778,779],{"class":463,"line":476},[461,780,680],{"emptyLinePlaceholder":679},[461,782,783],{"class":463,"line":688},[461,784,785],{},"-- EXPLAIN hiển thị \"Using index\" → covering, không đụng vào clustered index\n",[461,787,788],{"class":463,"line":694},[461,789,790],{},"EXPLAIN SELECT total FROM orders WHERE user_id = 1 AND created_at > '2025-01-01';\n",[437,792,793,800],{"color":439,"icon":440},[442,794,444,795,576,797],{},[446,796,575],{},[578,798,799],{},"\"Khi nào nên dùng covering index? Trade-off là gì?\"",[582,801,802],{},[442,803,804],{},"\"Covering index khi query thường xuyên chỉ cần vài cột và performance là critical — tránh double-lookup của InnoDB. Trade-off: index to hơn vì chứa thêm cột data → tốn disk + buffer pool, INSERT\u002FUPDATE chậm hơn vì phải maintain nhiều index page hơn.\"",[432,806,808],{"id":807},"_34-prefix-index","3.4 Prefix index",[442,810,811],{},"Index chỉ trên N ký tự đầu của cột VARCHAR\u002FTEXT — tiết kiệm không gian khi cột dài.",[451,813,815],{"className":542,"code":814,"language":544,"meta":456,"style":456},"-- Index trên 10 ký tự đầu của email\nCREATE INDEX idx_email_prefix ON users(email(10));\n\n-- Chọn độ dài prefix với cardinality gần bằng full column:\nSELECT COUNT(DISTINCT LEFT(email, 4)) \u002F COUNT(*) AS sel_4,\n       COUNT(DISTINCT LEFT(email, 8)) \u002F COUNT(*) AS sel_8,\n       COUNT(DISTINCT LEFT(email, 10)) \u002F COUNT(*) AS sel_10,\n       COUNT(DISTINCT email) \u002F COUNT(*) AS sel_full\nFROM users;\n-- Chọn độ dài prefix có selectivity gần với sel_full nhất\n",[458,816,817,822,827,831,836,841,846,851,856,862],{"__ignoreMap":456},[461,818,819],{"class":463,"line":464},[461,820,821],{},"-- Index trên 10 ký tự đầu của email\n",[461,823,824],{"class":463,"line":470},[461,825,826],{},"CREATE INDEX idx_email_prefix ON users(email(10));\n",[461,828,829],{"class":463,"line":476},[461,830,680],{"emptyLinePlaceholder":679},[461,832,833],{"class":463,"line":688},[461,834,835],{},"-- Chọn độ dài prefix với cardinality gần bằng full column:\n",[461,837,838],{"class":463,"line":694},[461,839,840],{},"SELECT COUNT(DISTINCT LEFT(email, 4)) \u002F COUNT(*) AS sel_4,\n",[461,842,843],{"class":463,"line":700},[461,844,845],{},"       COUNT(DISTINCT LEFT(email, 8)) \u002F COUNT(*) AS sel_8,\n",[461,847,848],{"class":463,"line":706},[461,849,850],{},"       COUNT(DISTINCT LEFT(email, 10)) \u002F COUNT(*) AS sel_10,\n",[461,852,853],{"class":463,"line":712},[461,854,855],{},"       COUNT(DISTINCT email) \u002F COUNT(*) AS sel_full\n",[461,857,859],{"class":463,"line":858},9,[461,860,861],{},"FROM users;\n",[461,863,865],{"class":463,"line":864},10,[461,866,867],{},"-- Chọn độ dài prefix có selectivity gần với sel_full nhất\n",[437,869,872,875],{"color":870,"icon":871},"danger","i-lucide-alert-triangle",[446,873,874],{},"Hạn chế:"," Prefix index KHÔNG dùng được cho ORDER BY và GROUP BY vì không có đủ giá trị đầy đủ để sort.",[432,877,879],{"id":878},"_35-fulltext-index","3.5 FULLTEXT index",[442,881,882,883,750],{},"FULLTEXT index cho tìm kiếm văn bản. InnoDB hỗ trợ từ MySQL 5.6. Chi tiết sử dụng → xem mục ",[446,884,885],{},"4. Full-Text Search",[451,887,889],{"className":542,"code":888,"language":544,"meta":456,"style":456},"CREATE FULLTEXT INDEX ft_content ON articles(title, body);\n\n-- Với ngram parser (cần cho tiếng Việt, CJK):\nCREATE FULLTEXT INDEX ft_content_ngram ON articles(title, body) WITH PARSER ngram;\n",[458,890,891,896,900,905],{"__ignoreMap":456},[461,892,893],{"class":463,"line":464},[461,894,895],{},"CREATE FULLTEXT INDEX ft_content ON articles(title, body);\n",[461,897,898],{"class":463,"line":470},[461,899,680],{"emptyLinePlaceholder":679},[461,901,902],{"class":463,"line":476},[461,903,904],{},"-- Với ngram parser (cần cho tiếng Việt, CJK):\n",[461,906,907],{"class":463,"line":688},[461,908,909],{},"CREATE FULLTEXT INDEX ft_content_ngram ON articles(title, body) WITH PARSER ngram;\n",[442,911,912],{},"MySQL có sẵn 2 parser cho FULLTEXT:",[488,914,915,921],{},[491,916,917,920],{},[446,918,919],{},"Default parser",": tách từ theo whitespace\u002Fdấu câu, bỏ qua stopword. Không hoạt động tốt với tiếng Việt (không có khoảng trắng phân tách từ đơn).",[491,922,923,926,927,930],{},[446,924,925],{},"ngram parser",": tách thành các n-gram token (mặc định bigram, cấu hình qua ",[458,928,929],{},"ngram_token_size","). Phù hợp với tiếng Việt, Trung, Nhật, Hàn.",[451,932,934],{"className":453,"code":933,"language":455,"meta":456,"style":456},"[mysqld]\nngram_token_size = 2   # bigram, mặc định\nft_min_word_len = 2    # độ dài từ tối thiểu\ninnodb_ft_min_token_size = 2\n",[458,935,936,940,945,950],{"__ignoreMap":456},[461,937,938],{"class":463,"line":464},[461,939,467],{},[461,941,942],{"class":463,"line":470},[461,943,944],{},"ngram_token_size = 2   # bigram, mặc định\n",[461,946,947],{"class":463,"line":476},[461,948,949],{},"ft_min_word_len = 2    # độ dài từ tối thiểu\n",[461,951,952],{"class":463,"line":688},[461,953,954],{},"innodb_ft_min_token_size = 2\n",[432,956,958],{"id":957},"_36-spatial-index","3.6 SPATIAL index",[442,960,961],{},"SPATIAL index dùng cho dữ liệu không gian (GIS\u002Fgeometry). InnoDB hỗ trợ SPATIAL index từ MySQL 5.7 với R-tree.",[451,963,965],{"className":542,"code":964,"language":544,"meta":456,"style":456},"-- Tạo cột geometry\nCREATE TABLE locations (\n    id INT PRIMARY KEY AUTO_INCREMENT,\n    name VARCHAR(255),\n    geo POINT NOT NULL SRID 4326,\n    SPATIAL INDEX idx_geo (geo)\n);\n\n-- Query tìm điểm trong bán kính\nSELECT name, ST_Distance_Sphere(geo, ST_GeomFromText('POINT(106.7 10.8)', 4326)) AS dist\nFROM locations\nWHERE ST_Within(geo, ST_Buffer(ST_GeomFromText('POINT(106.7 10.8)', 4326), 0.1))\nORDER BY dist;\n",[458,966,967,972,977,982,987,992,997,1002,1006,1011,1016,1022,1028],{"__ignoreMap":456},[461,968,969],{"class":463,"line":464},[461,970,971],{},"-- Tạo cột geometry\n",[461,973,974],{"class":463,"line":470},[461,975,976],{},"CREATE TABLE locations (\n",[461,978,979],{"class":463,"line":476},[461,980,981],{},"    id INT PRIMARY KEY AUTO_INCREMENT,\n",[461,983,984],{"class":463,"line":688},[461,985,986],{},"    name VARCHAR(255),\n",[461,988,989],{"class":463,"line":694},[461,990,991],{},"    geo POINT NOT NULL SRID 4326,\n",[461,993,994],{"class":463,"line":700},[461,995,996],{},"    SPATIAL INDEX idx_geo (geo)\n",[461,998,999],{"class":463,"line":706},[461,1000,1001],{},");\n",[461,1003,1004],{"class":463,"line":712},[461,1005,680],{"emptyLinePlaceholder":679},[461,1007,1008],{"class":463,"line":858},[461,1009,1010],{},"-- Query tìm điểm trong bán kính\n",[461,1012,1013],{"class":463,"line":864},[461,1014,1015],{},"SELECT name, ST_Distance_Sphere(geo, ST_GeomFromText('POINT(106.7 10.8)', 4326)) AS dist\n",[461,1017,1019],{"class":463,"line":1018},11,[461,1020,1021],{},"FROM locations\n",[461,1023,1025],{"class":463,"line":1024},12,[461,1026,1027],{},"WHERE ST_Within(geo, ST_Buffer(ST_GeomFromText('POINT(106.7 10.8)', 4326), 0.1))\n",[461,1029,1031],{"class":463,"line":1030},13,[461,1032,1033],{},"ORDER BY dist;\n",[432,1035,1037],{"id":1036},"_37-functional-index-8013","3.7 Functional index (8.0.13+)",[442,1039,1040],{},"Index trên biểu thức \u002F function thay vì giá trị gốc của cột.",[451,1042,1044],{"className":542,"code":1043,"language":544,"meta":456,"style":456},"-- Index trên expression — giải bài \"non-sargable\"\nCREATE INDEX idx_lower_email ON users ((LOWER(email)));\n\n-- Query dùng được:\nSELECT * FROM users WHERE LOWER(email) = 'a@b.com';\n\n-- Index trên JSON path (kết hợp với generated column — xem mục JSON)\nCREATE INDEX idx_json_city ON orders ((CAST(data->>'$.city' AS CHAR(50))));\n",[458,1045,1046,1051,1056,1060,1065,1070,1074,1079],{"__ignoreMap":456},[461,1047,1048],{"class":463,"line":464},[461,1049,1050],{},"-- Index trên expression — giải bài \"non-sargable\"\n",[461,1052,1053],{"class":463,"line":470},[461,1054,1055],{},"CREATE INDEX idx_lower_email ON users ((LOWER(email)));\n",[461,1057,1058],{"class":463,"line":476},[461,1059,680],{"emptyLinePlaceholder":679},[461,1061,1062],{"class":463,"line":688},[461,1063,1064],{},"-- Query dùng được:\n",[461,1066,1067],{"class":463,"line":694},[461,1068,1069],{},"SELECT * FROM users WHERE LOWER(email) = 'a@b.com';\n",[461,1071,1072],{"class":463,"line":700},[461,1073,680],{"emptyLinePlaceholder":679},[461,1075,1076],{"class":463,"line":706},[461,1077,1078],{},"-- Index trên JSON path (kết hợp với generated column — xem mục JSON)\n",[461,1080,1081],{"class":463,"line":712},[461,1082,1083],{},"CREATE INDEX idx_json_city ON orders ((CAST(data->>'$.city' AS CHAR(50))));\n",[432,1085,1087],{"id":1086},"_38-invisible-index-80","3.8 Invisible index (8.0+)",[451,1089,1091],{"className":542,"code":1090,"language":544,"meta":456,"style":456},"-- Tạo index nhưng optimizer không dùng — test trước khi enable\u002Fdisable\nALTER TABLE orders ALTER INDEX idx_status INVISIBLE;\nALTER TABLE orders ALTER INDEX idx_status VISIBLE;\n",[458,1092,1093,1098,1103],{"__ignoreMap":456},[461,1094,1095],{"class":463,"line":464},[461,1096,1097],{},"-- Tạo index nhưng optimizer không dùng — test trước khi enable\u002Fdisable\n",[461,1099,1100],{"class":463,"line":470},[461,1101,1102],{},"ALTER TABLE orders ALTER INDEX idx_status INVISIBLE;\n",[461,1104,1105],{"class":463,"line":476},[461,1106,1107],{},"ALTER TABLE orders ALTER INDEX idx_status VISIBLE;\n",[442,1109,1110,1111,1114,1115,1118],{},"Hữu ích để test impact trước khi drop index thật sự. ",[458,1112,1113],{},"optimizer_switch"," có flag ",[458,1116,1117],{},"use_invisible_indexes"," để override global.",[432,1120,1122],{"id":1121},"_39-descending-index-80","3.9 Descending index (8.0+)",[451,1124,1126],{"className":542,"code":1125,"language":544,"meta":456,"style":456},"CREATE INDEX idx_orders_desc ON orders(created_at DESC);\n-- Query \"ORDER BY created_at DESC\" dùng index thẳng, không cần filesort\n",[458,1127,1128,1133],{"__ignoreMap":456},[461,1129,1130],{"class":463,"line":464},[461,1131,1132],{},"CREATE INDEX idx_orders_desc ON orders(created_at DESC);\n",[461,1134,1135],{"class":463,"line":470},[461,1136,1137],{},"-- Query \"ORDER BY created_at DESC\" dùng index thẳng, không cần filesort\n",[442,1139,1140],{},"Trước 8.0, MySQL chỉ hỗ trợ ASC index, DESC trong định nghĩa index bị bỏ qua (parser chấp nhận nhưng engine không áp dụng).",[427,1142,885],{"id":1143},"_4-full-text-search",[432,1145,1147],{"id":1146},"_41-tạo-fulltext-index","4.1 Tạo FULLTEXT index",[451,1149,1151],{"className":542,"code":1150,"language":544,"meta":456,"style":456},"-- Trên bảng articles\nCREATE FULLTEXT INDEX ft_content ON articles(title, body);\n\n-- Hoặc khi CREATE TABLE\nCREATE TABLE articles (\n    id INT PRIMARY KEY AUTO_INCREMENT,\n    title VARCHAR(255),\n    body TEXT,\n    FULLTEXT INDEX ft_articles (title, body)\n) ENGINE=InnoDB;\n",[458,1152,1153,1158,1162,1166,1171,1176,1180,1185,1190,1195],{"__ignoreMap":456},[461,1154,1155],{"class":463,"line":464},[461,1156,1157],{},"-- Trên bảng articles\n",[461,1159,1160],{"class":463,"line":470},[461,1161,895],{},[461,1163,1164],{"class":463,"line":476},[461,1165,680],{"emptyLinePlaceholder":679},[461,1167,1168],{"class":463,"line":688},[461,1169,1170],{},"-- Hoặc khi CREATE TABLE\n",[461,1172,1173],{"class":463,"line":694},[461,1174,1175],{},"CREATE TABLE articles (\n",[461,1177,1178],{"class":463,"line":700},[461,1179,981],{},[461,1181,1182],{"class":463,"line":706},[461,1183,1184],{},"    title VARCHAR(255),\n",[461,1186,1187],{"class":463,"line":712},[461,1188,1189],{},"    body TEXT,\n",[461,1191,1192],{"class":463,"line":858},[461,1193,1194],{},"    FULLTEXT INDEX ft_articles (title, body)\n",[461,1196,1197],{"class":463,"line":864},[461,1198,1199],{},") ENGINE=InnoDB;\n",[432,1201,1203],{"id":1202},"_42-match-against-3-chế-độ-tìm-kiếm","4.2 MATCH ... AGAINST — 3 chế độ tìm kiếm",[442,1205,1206],{},[446,1207,1208],{},"a) Natural Language Mode (mặc định)",[451,1210,1212],{"className":542,"code":1211,"language":544,"meta":456,"style":456},"-- Tìm kiếm theo độ liên quan (relevance score)\nSELECT id, title,\n       MATCH(title, body) AGAINST('MySQL performance tuning') AS relevance\nFROM articles\nWHERE MATCH(title, body) AGAINST('MySQL performance tuning' IN NATURAL LANGUAGE MODE)\nORDER BY relevance DESC;\n",[458,1213,1214,1219,1224,1229,1234,1239],{"__ignoreMap":456},[461,1215,1216],{"class":463,"line":464},[461,1217,1218],{},"-- Tìm kiếm theo độ liên quan (relevance score)\n",[461,1220,1221],{"class":463,"line":470},[461,1222,1223],{},"SELECT id, title,\n",[461,1225,1226],{"class":463,"line":476},[461,1227,1228],{},"       MATCH(title, body) AGAINST('MySQL performance tuning') AS relevance\n",[461,1230,1231],{"class":463,"line":688},[461,1232,1233],{},"FROM articles\n",[461,1235,1236],{"class":463,"line":694},[461,1237,1238],{},"WHERE MATCH(title, body) AGAINST('MySQL performance tuning' IN NATURAL LANGUAGE MODE)\n",[461,1240,1241],{"class":463,"line":700},[461,1242,1243],{},"ORDER BY relevance DESC;\n",[442,1245,1246],{},"MySQL tính relevance dựa trên TF-IDF: từ xuất hiện nhiều trong document → score cao, từ xuất hiện ở nhiều document → bị giảm trọng số. Từ có trong > 50% số row → bị bỏ qua (50% threshold).",[442,1248,1249],{},[446,1250,1251],{},"b) Boolean Mode",[442,1253,1254],{},"Kiểm soát chính xác logic tìm kiếm với toán tử boolean.",[451,1256,1258],{"className":542,"code":1257,"language":544,"meta":456,"style":456},"-- Phải có 'MySQL', có thể có 'performance', không có 'postgres'\nSELECT * FROM articles\nWHERE MATCH(title, body) AGAINST('+MySQL performance -postgres' IN BOOLEAN MODE);\n\n-- Toán tử boolean:\n-- +word    : phải có\n-- -word    : không được có\n-- word     : optional (tăng relevance nếu có)\n-- \"phrase\" : exact phrase\n-- word*    : prefix wildcard\n-- (a b)    : nhóm\n-- >word \u003Cword : tăng\u002Fgiảm trọng số\n-- ~word    : negative contribution (giảm relevance)\n",[458,1259,1260,1265,1270,1275,1279,1284,1289,1294,1299,1304,1309,1314,1319],{"__ignoreMap":456},[461,1261,1262],{"class":463,"line":464},[461,1263,1264],{},"-- Phải có 'MySQL', có thể có 'performance', không có 'postgres'\n",[461,1266,1267],{"class":463,"line":470},[461,1268,1269],{},"SELECT * FROM articles\n",[461,1271,1272],{"class":463,"line":476},[461,1273,1274],{},"WHERE MATCH(title, body) AGAINST('+MySQL performance -postgres' IN BOOLEAN MODE);\n",[461,1276,1277],{"class":463,"line":688},[461,1278,680],{"emptyLinePlaceholder":679},[461,1280,1281],{"class":463,"line":694},[461,1282,1283],{},"-- Toán tử boolean:\n",[461,1285,1286],{"class":463,"line":700},[461,1287,1288],{},"-- +word    : phải có\n",[461,1290,1291],{"class":463,"line":706},[461,1292,1293],{},"-- -word    : không được có\n",[461,1295,1296],{"class":463,"line":712},[461,1297,1298],{},"-- word     : optional (tăng relevance nếu có)\n",[461,1300,1301],{"class":463,"line":858},[461,1302,1303],{},"-- \"phrase\" : exact phrase\n",[461,1305,1306],{"class":463,"line":864},[461,1307,1308],{},"-- word*    : prefix wildcard\n",[461,1310,1311],{"class":463,"line":1018},[461,1312,1313],{},"-- (a b)    : nhóm\n",[461,1315,1316],{"class":463,"line":1024},[461,1317,1318],{},"-- >word \u003Cword : tăng\u002Fgiảm trọng số\n",[461,1320,1321],{"class":463,"line":1030},[461,1322,1323],{},"-- ~word    : negative contribution (giảm relevance)\n",[437,1325,1326,1333],{"color":439,"icon":440},[442,1327,444,1328,576,1330],{},[446,1329,575],{},[578,1331,1332],{},"\"FULLTEXT Boolean mode dùng toán tử gì? Khi nào dùng Boolean thay vì Natural Language?\"",[582,1334,1335],{},[442,1336,1337,1338,1341,1342,1341,1345,1341,1348,1341,1351,1354,1355,1358,1359,576,1362,1365],{},"\"Boolean mode cho phép kiểm soát chính xác với ",[458,1339,1340],{},"+",", ",[458,1343,1344],{},"-",[458,1346,1347],{},"*",[458,1349,1350],{},"\"\"",[458,1352,1353],{},"~",". Dùng khi cần logic phức tạp (phải có A, không được có B) hoặc tìm prefix. Đánh đổi: Boolean mode ",[446,1356,1357],{},"không sắp xếp theo relevance"," mặc định (các row match có relevance = 0 trừ khi có toán tử ",[458,1360,1361],{},">",[458,1363,1364],{},"\u003C","). Natural Language phù hợp search kiểu Google — tìm theo độ liên quan, nhưng bị 50% threshold.\"",[442,1367,1368],{},[446,1369,1370],{},"c) Query Expansion Mode",[451,1372,1374],{"className":542,"code":1373,"language":544,"meta":456,"style":456},"-- MySQL chạy query 2 lần:\n-- Lần 1: tìm row match 'MySQL' → lấy ra các từ phổ biến trong những row đó\n-- Lần 2: tìm lại với cả 'MySQL' + các từ mở rộng đó\nSELECT * FROM articles\nWHERE MATCH(title, body) AGAINST('MySQL' WITH QUERY EXPANSION);\n",[458,1375,1376,1381,1386,1391,1395],{"__ignoreMap":456},[461,1377,1378],{"class":463,"line":464},[461,1379,1380],{},"-- MySQL chạy query 2 lần:\n",[461,1382,1383],{"class":463,"line":470},[461,1384,1385],{},"-- Lần 1: tìm row match 'MySQL' → lấy ra các từ phổ biến trong những row đó\n",[461,1387,1388],{"class":463,"line":476},[461,1389,1390],{},"-- Lần 2: tìm lại với cả 'MySQL' + các từ mở rộng đó\n",[461,1392,1393],{"class":463,"line":688},[461,1394,1269],{},[461,1396,1397],{"class":463,"line":694},[461,1398,1399],{},"WHERE MATCH(title, body) AGAINST('MySQL' WITH QUERY EXPANSION);\n",[442,1401,1402],{},"Hữu ích khi user gõ từ quá ngắn hoặc không biết từ khóa chính xác. Cẩn thận — có thể mở rộng ra noise.",[432,1404,1406],{"id":1405},"_43-ngram-parser-cho-tiếng-việt","4.3 ngram parser cho tiếng Việt",[442,1408,1409],{},"Mặc định FULLTEXT parser tách từ theo whitespace → không hoạt động với tiếng Việt (từ đơn âm tiết không có dấu cách phân tách).",[451,1411,1413],{"className":542,"code":1412,"language":544,"meta":456,"style":456},"-- Tạo FULLTEXT index với ngram parser\nCREATE FULLTEXT INDEX ft_vi ON articles_vn(title, body) WITH PARSER ngram;\n\n-- ngram_token_size = 2 (bigram) — mặc định\n-- Từ \"MySQL\" → token: \"My\", \"yS\", \"SQ\", \"QL\"\n-- Từ \"hiệu năng\" → token: \"hi\", \"iệ\", \"ệu\", \" n\", \"nă\", \"ăn\", \"ng\"\n\n-- Search bình thường:\nSELECT * FROM articles_vn\nWHERE MATCH(title, body) AGAINST('hiệu năng' IN BOOLEAN MODE);\n",[458,1414,1415,1420,1425,1429,1434,1439,1444,1448,1453,1458],{"__ignoreMap":456},[461,1416,1417],{"class":463,"line":464},[461,1418,1419],{},"-- Tạo FULLTEXT index với ngram parser\n",[461,1421,1422],{"class":463,"line":470},[461,1423,1424],{},"CREATE FULLTEXT INDEX ft_vi ON articles_vn(title, body) WITH PARSER ngram;\n",[461,1426,1427],{"class":463,"line":476},[461,1428,680],{"emptyLinePlaceholder":679},[461,1430,1431],{"class":463,"line":688},[461,1432,1433],{},"-- ngram_token_size = 2 (bigram) — mặc định\n",[461,1435,1436],{"class":463,"line":694},[461,1437,1438],{},"-- Từ \"MySQL\" → token: \"My\", \"yS\", \"SQ\", \"QL\"\n",[461,1440,1441],{"class":463,"line":700},[461,1442,1443],{},"-- Từ \"hiệu năng\" → token: \"hi\", \"iệ\", \"ệu\", \" n\", \"nă\", \"ăn\", \"ng\"\n",[461,1445,1446],{"class":463,"line":706},[461,1447,680],{"emptyLinePlaceholder":679},[461,1449,1450],{"class":463,"line":712},[461,1451,1452],{},"-- Search bình thường:\n",[461,1454,1455],{"class":463,"line":858},[461,1456,1457],{},"SELECT * FROM articles_vn\n",[461,1459,1460],{"class":463,"line":864},[461,1461,1462],{},"WHERE MATCH(title, body) AGAINST('hiệu năng' IN BOOLEAN MODE);\n",[437,1464,1466,1471,1494],{"color":570,"icon":1465},"i-lucide-info",[442,1467,1468],{},[446,1469,1470],{},"Cấu hình ngram:",[451,1472,1474],{"className":453,"code":1473,"language":455,"meta":456,"style":456},"[mysqld]\nngram_token_size = 2\ninnodb_ft_min_token_size = 2\nft_min_word_len = 2\n",[458,1475,1476,1480,1485,1489],{"__ignoreMap":456},[461,1477,1478],{"class":463,"line":464},[461,1479,467],{},[461,1481,1482],{"class":463,"line":470},[461,1483,1484],{},"ngram_token_size = 2\n",[461,1486,1487],{"class":463,"line":476},[461,1488,954],{},[461,1490,1491],{"class":463,"line":688},[461,1492,1493],{},"ft_min_word_len = 2\n",[442,1495,1496,1497,750],{},"Cần rebuild index sau khi đổi ",[458,1498,929],{},[432,1500,1502],{"id":1501},"_44-innodb-fulltext-cơ-chế-hoạt-động","4.4 InnoDB FULLTEXT — cơ chế hoạt động",[442,1504,1505,1506,1509],{},"InnoDB FULLTEXT dùng ",[446,1507,1508],{},"inverted index"," gồm 6 auxiliary table (FTS_INDEX_CACHE, FTS_COMMON_TABLE, v.v.) trong internal tablespace. FTS index được update async qua background thread — có thể có độ trễ sau INSERT\u002FUPDATE. Force sync:",[451,1511,1513],{"className":542,"code":1512,"language":544,"meta":456,"style":456},"SET GLOBAL innodb_ft_cache_size = 80000000;   -- cache size cho FTS\nOPTIMIZE TABLE articles;                       -- force merge FTS index\n",[458,1514,1515,1520],{"__ignoreMap":456},[461,1516,1517],{"class":463,"line":464},[461,1518,1519],{},"SET GLOBAL innodb_ft_cache_size = 80000000;   -- cache size cho FTS\n",[461,1521,1522],{"class":463,"line":470},[461,1523,1524],{},"OPTIMIZE TABLE articles;                       -- force merge FTS index\n",[427,1526,1528],{"id":1527},"_5-json-trong-mysql","5. JSON trong MySQL",[442,1530,1531],{},"MySQL hỗ trợ JSON data type native từ 5.7. JSON column lưu dạng binary (không phải text) → validation tự động, truy cập nhanh hơn LONGTEXT.",[432,1533,1535],{"id":1534},"_51-tạo-và-truy-vấn-json","5.1 Tạo và truy vấn JSON",[451,1537,1539],{"className":542,"code":1538,"language":544,"meta":456,"style":456},"CREATE TABLE users (\n    id INT PRIMARY KEY AUTO_INCREMENT,\n    name VARCHAR(100),\n    profile JSON\n);\n\n-- Insert\nINSERT INTO users (name, profile) VALUES\n('Vinh', JSON_OBJECT('age', 25, 'city', 'HCM', 'skills', JSON_ARRAY('SQL', 'Python'))),\n('An',   JSON_OBJECT('age', 30, 'city', 'HN',  'skills', JSON_ARRAY('Java', 'Go')));\n\n-- JSON_EXTRACT với cú pháp path $ (hoặc dùng -> shorthand)\nSELECT name,\n       profile->>'$.city' AS city,\n       profile->>'$.age'  AS age,\n       profile->'$.skills[0]' AS first_skill\nFROM users;\n\n-- JSON_CONTAINS: kiểm tra sự tồn tại\nSELECT * FROM users\nWHERE JSON_CONTAINS(profile->'$.skills', '\"Python\"');\n",[458,1540,1541,1546,1550,1555,1560,1564,1568,1573,1578,1583,1588,1592,1597,1602,1608,1614,1620,1625,1630,1636,1642],{"__ignoreMap":456},[461,1542,1543],{"class":463,"line":464},[461,1544,1545],{},"CREATE TABLE users (\n",[461,1547,1548],{"class":463,"line":470},[461,1549,981],{},[461,1551,1552],{"class":463,"line":476},[461,1553,1554],{},"    name VARCHAR(100),\n",[461,1556,1557],{"class":463,"line":688},[461,1558,1559],{},"    profile JSON\n",[461,1561,1562],{"class":463,"line":694},[461,1563,1001],{},[461,1565,1566],{"class":463,"line":700},[461,1567,680],{"emptyLinePlaceholder":679},[461,1569,1570],{"class":463,"line":706},[461,1571,1572],{},"-- Insert\n",[461,1574,1575],{"class":463,"line":712},[461,1576,1577],{},"INSERT INTO users (name, profile) VALUES\n",[461,1579,1580],{"class":463,"line":858},[461,1581,1582],{},"('Vinh', JSON_OBJECT('age', 25, 'city', 'HCM', 'skills', JSON_ARRAY('SQL', 'Python'))),\n",[461,1584,1585],{"class":463,"line":864},[461,1586,1587],{},"('An',   JSON_OBJECT('age', 30, 'city', 'HN',  'skills', JSON_ARRAY('Java', 'Go')));\n",[461,1589,1590],{"class":463,"line":1018},[461,1591,680],{"emptyLinePlaceholder":679},[461,1593,1594],{"class":463,"line":1024},[461,1595,1596],{},"-- JSON_EXTRACT với cú pháp path $ (hoặc dùng -> shorthand)\n",[461,1598,1599],{"class":463,"line":1030},[461,1600,1601],{},"SELECT name,\n",[461,1603,1605],{"class":463,"line":1604},14,[461,1606,1607],{},"       profile->>'$.city' AS city,\n",[461,1609,1611],{"class":463,"line":1610},15,[461,1612,1613],{},"       profile->>'$.age'  AS age,\n",[461,1615,1617],{"class":463,"line":1616},16,[461,1618,1619],{},"       profile->'$.skills[0]' AS first_skill\n",[461,1621,1623],{"class":463,"line":1622},17,[461,1624,861],{},[461,1626,1628],{"class":463,"line":1627},18,[461,1629,680],{"emptyLinePlaceholder":679},[461,1631,1633],{"class":463,"line":1632},19,[461,1634,1635],{},"-- JSON_CONTAINS: kiểm tra sự tồn tại\n",[461,1637,1639],{"class":463,"line":1638},20,[461,1640,1641],{},"SELECT * FROM users\n",[461,1643,1645],{"class":463,"line":1644},21,[461,1646,1647],{},"WHERE JSON_CONTAINS(profile->'$.skills', '\"Python\"');\n",[432,1649,1651],{"id":1650},"_52-json-functions-quan-trọng","5.2 JSON functions quan trọng",[1653,1654,1655,1671],"table",{},[1656,1657,1658],"thead",{},[1659,1660,1661,1665,1668],"tr",{},[1662,1663,1664],"th",{},"Function",[1662,1666,1667],{},"Mục đích",[1662,1669,1670],{},"Ví dụ",[1672,1673,1674,1694,1709,1724,1739,1754,1769,1784,1799,1814],"tbody",{},[1659,1675,1676,1686,1689],{},[1677,1678,1679,1682,1683],"td",{},[458,1680,1681],{},"JSON_EXTRACT(doc, path)"," \u002F ",[458,1684,1685],{},"->",[1677,1687,1688],{},"Trích xuất giá trị (giữ JSON type)",[1677,1690,1691],{},[458,1692,1693],{},"profile->'$.city'",[1659,1695,1696,1701,1704],{},[1677,1697,1698],{},[458,1699,1700],{},"->>",[1677,1702,1703],{},"Trích xuất giá trị (unquote thành string)",[1677,1705,1706],{},[458,1707,1708],{},"profile->>'$.city'",[1659,1710,1711,1716,1719],{},[1677,1712,1713],{},[458,1714,1715],{},"JSON_CONTAINS(doc, val)",[1677,1717,1718],{},"Kiểm tra có chứa value không",[1677,1720,1721],{},[458,1722,1723],{},"WHERE JSON_CONTAINS(profile->'$.skills', '\"Python\"')",[1659,1725,1726,1731,1734],{},[1677,1727,1728],{},[458,1729,1730],{},"JSON_OBJECT(k1,v1,...)",[1677,1732,1733],{},"Tạo JSON object",[1677,1735,1736],{},[458,1737,1738],{},"JSON_OBJECT('name', 'Vinh')",[1659,1740,1741,1746,1749],{},[1677,1742,1743],{},[458,1744,1745],{},"JSON_ARRAY(v1,v2,...)",[1677,1747,1748],{},"Tạo JSON array",[1677,1750,1751],{},[458,1752,1753],{},"JSON_ARRAY('a', 'b')",[1659,1755,1756,1761,1764],{},[1677,1757,1758],{},[458,1759,1760],{},"JSON_SET(doc, path, val)",[1677,1762,1763],{},"Set (hoặc update) value tại path",[1677,1765,1766],{},[458,1767,1768],{},"JSON_SET(profile, '$.age', 26)",[1659,1770,1771,1776,1779],{},[1677,1772,1773],{},[458,1774,1775],{},"JSON_REPLACE(doc, path, val)",[1677,1777,1778],{},"Chỉ update nếu path đã tồn tại",[1677,1780,1781],{},[458,1782,1783],{},"JSON_REPLACE(profile, '$.age', 26)",[1659,1785,1786,1791,1794],{},[1677,1787,1788],{},[458,1789,1790],{},"JSON_REMOVE(doc, path)",[1677,1792,1793],{},"Xóa key tại path",[1677,1795,1796],{},[458,1797,1798],{},"JSON_REMOVE(profile, '$.city')",[1659,1800,1801,1806,1809],{},[1677,1802,1803],{},[458,1804,1805],{},"JSON_KEYS(doc)",[1677,1807,1808],{},"Lấy tất cả key của JSON object",[1677,1810,1811],{},[458,1812,1813],{},"JSON_KEYS(profile)",[1659,1815,1816,1821,1824],{},[1677,1817,1818],{},[458,1819,1820],{},"JSON_LENGTH(doc)",[1677,1822,1823],{},"Đếm số phần tử array hoặc key object",[1677,1825,1826],{},[458,1827,1828],{},"JSON_LENGTH(profile->'$.skills')",[432,1830,1832],{"id":1831},"_53-json_table-chuyển-json-array-thành-bảng","5.3 JSON_TABLE() — chuyển JSON array thành bảng",[451,1834,1836],{"className":542,"code":1835,"language":544,"meta":456,"style":456},"-- Chuyển JSON array thành relational rows\nSELECT jt.* FROM orders,\nJSON_TABLE(\n    items,\n    '$[*]' COLUMNS (\n        product VARCHAR(100) PATH '$.product',\n        qty     INT          PATH '$.qty',\n        price   DECIMAL(10,2) PATH '$.price'\n    )\n) AS jt;\n",[458,1837,1838,1843,1848,1853,1858,1863,1868,1873,1878,1883],{"__ignoreMap":456},[461,1839,1840],{"class":463,"line":464},[461,1841,1842],{},"-- Chuyển JSON array thành relational rows\n",[461,1844,1845],{"class":463,"line":470},[461,1846,1847],{},"SELECT jt.* FROM orders,\n",[461,1849,1850],{"class":463,"line":476},[461,1851,1852],{},"JSON_TABLE(\n",[461,1854,1855],{"class":463,"line":688},[461,1856,1857],{},"    items,\n",[461,1859,1860],{"class":463,"line":694},[461,1861,1862],{},"    '$[*]' COLUMNS (\n",[461,1864,1865],{"class":463,"line":700},[461,1866,1867],{},"        product VARCHAR(100) PATH '$.product',\n",[461,1869,1870],{"class":463,"line":706},[461,1871,1872],{},"        qty     INT          PATH '$.qty',\n",[461,1874,1875],{"class":463,"line":712},[461,1876,1877],{},"        price   DECIMAL(10,2) PATH '$.price'\n",[461,1879,1880],{"class":463,"line":858},[461,1881,1882],{},"    )\n",[461,1884,1885],{"class":463,"line":864},[461,1886,1887],{},") AS jt;\n",[437,1889,1890,1897],{"color":439,"icon":440},[442,1891,444,1892,576,1894],{},[446,1893,575],{},[578,1895,1896],{},"\"Làm sao để query JSON array thành từng row trong MySQL?\"",[582,1898,1899],{},[442,1900,1901,1902,1905],{},"\"Dùng ",[446,1903,1904],{},"JSON_TABLE()"," (từ MySQL 8.0.4) — convert JSON array thành relational rows với COLUMNS định nghĩa theo JSON path. Có thể JOIN với bảng khác, dùng trong subquery. Trước 8.0 phải dùng stored procedure loop hoặc JSON functions kết hợp với recursive CTE.\"",[432,1907,1909],{"id":1908},"_54-generated-columns-từ-json-index","5.4 Generated columns từ JSON + index",[451,1911,1913],{"className":542,"code":1912,"language":544,"meta":456,"style":456},"-- Tạo generated column từ JSON path\nALTER TABLE users ADD COLUMN city VARCHAR(100)\n    GENERATED ALWAYS AS (profile->>'$.city') STORED;\n\n-- Index trên generated column → JSON query dùng index!\nCREATE INDEX idx_city ON users(city);\n\n-- Query này giờ dùng được index\nSELECT * FROM users WHERE profile->>'$.city' = 'HCM';\n-- Hoặc: SELECT * FROM users WHERE city = 'HCM';\n",[458,1914,1915,1920,1925,1930,1934,1939,1944,1948,1953,1958],{"__ignoreMap":456},[461,1916,1917],{"class":463,"line":464},[461,1918,1919],{},"-- Tạo generated column từ JSON path\n",[461,1921,1922],{"class":463,"line":470},[461,1923,1924],{},"ALTER TABLE users ADD COLUMN city VARCHAR(100)\n",[461,1926,1927],{"class":463,"line":476},[461,1928,1929],{},"    GENERATED ALWAYS AS (profile->>'$.city') STORED;\n",[461,1931,1932],{"class":463,"line":688},[461,1933,680],{"emptyLinePlaceholder":679},[461,1935,1936],{"class":463,"line":694},[461,1937,1938],{},"-- Index trên generated column → JSON query dùng index!\n",[461,1940,1941],{"class":463,"line":700},[461,1942,1943],{},"CREATE INDEX idx_city ON users(city);\n",[461,1945,1946],{"class":463,"line":706},[461,1947,680],{"emptyLinePlaceholder":679},[461,1949,1950],{"class":463,"line":712},[461,1951,1952],{},"-- Query này giờ dùng được index\n",[461,1954,1955],{"class":463,"line":858},[461,1956,1957],{},"SELECT * FROM users WHERE profile->>'$.city' = 'HCM';\n",[461,1959,1960],{"class":463,"line":864},[461,1961,1962],{},"-- Hoặc: SELECT * FROM users WHERE city = 'HCM';\n",[437,1964,1965,1968,1969,1972,1973,1976],{"color":718,"icon":719},[446,1966,1967],{},"Kinh nghiệm:"," Luôn tạo generated column + index cho JSON field được query thường xuyên. JSON column không có index tự động, query trực tiếp luôn là full scan. Dùng ",[458,1970,1971],{},"VIRTUAL"," generated column (không tốn disk) nếu không cần index, ",[458,1974,1975],{},"STORED"," nếu cần index.",[432,1978,1980],{"id":1979},"_55-json-duality-views-mysql-90-dml-đầy-đủ-từ-97","5.5 JSON Duality Views (MySQL 9.0+, DML đầy đủ từ 9.7)",[442,1982,1983],{},"JSON Duality View cho phép truy xuất cùng một data vừa dạng relational (table rows) vừa dạng JSON document, không cần ORM mapping.",[451,1985,1987],{"className":542,"code":1986,"language":544,"meta":456,"style":456},"-- Định nghĩa Duality View\nCREATE OR REPLACE JSON RELATIONAL DUALITY VIEW user_dv AS\nusers {\n    _id: id,\n    name: name,\n    email: email,\n    orders @unnest @outer [{\n        _id: id,\n        amount: amount,\n        status: status\n    }]\n};\n\n-- Đọc như JSON document\nSELECT * FROM user_dv WHERE JSON_CONTAINS(data, '{\"name\":\"Vinh\"}');\n\n-- Ghi như JSON document (từ 9.7 — Community Edition)\nUPDATE user_dv SET data = JSON_SET(data, '$.name', 'Vinh Updated') WHERE data->>'$._id' = '1';\n",[458,1988,1989,1994,1999,2004,2009,2014,2019,2024,2029,2034,2039,2044,2049,2053,2058,2063,2067,2072],{"__ignoreMap":456},[461,1990,1991],{"class":463,"line":464},[461,1992,1993],{},"-- Định nghĩa Duality View\n",[461,1995,1996],{"class":463,"line":470},[461,1997,1998],{},"CREATE OR REPLACE JSON RELATIONAL DUALITY VIEW user_dv AS\n",[461,2000,2001],{"class":463,"line":476},[461,2002,2003],{},"users {\n",[461,2005,2006],{"class":463,"line":688},[461,2007,2008],{},"    _id: id,\n",[461,2010,2011],{"class":463,"line":694},[461,2012,2013],{},"    name: name,\n",[461,2015,2016],{"class":463,"line":700},[461,2017,2018],{},"    email: email,\n",[461,2020,2021],{"class":463,"line":706},[461,2022,2023],{},"    orders @unnest @outer [{\n",[461,2025,2026],{"class":463,"line":712},[461,2027,2028],{},"        _id: id,\n",[461,2030,2031],{"class":463,"line":858},[461,2032,2033],{},"        amount: amount,\n",[461,2035,2036],{"class":463,"line":864},[461,2037,2038],{},"        status: status\n",[461,2040,2041],{"class":463,"line":1018},[461,2042,2043],{},"    }]\n",[461,2045,2046],{"class":463,"line":1024},[461,2047,2048],{},"};\n",[461,2050,2051],{"class":463,"line":1030},[461,2052,680],{"emptyLinePlaceholder":679},[461,2054,2055],{"class":463,"line":1604},[461,2056,2057],{},"-- Đọc như JSON document\n",[461,2059,2060],{"class":463,"line":1610},[461,2061,2062],{},"SELECT * FROM user_dv WHERE JSON_CONTAINS(data, '{\"name\":\"Vinh\"}');\n",[461,2064,2065],{"class":463,"line":1616},[461,2066,680],{"emptyLinePlaceholder":679},[461,2068,2069],{"class":463,"line":1622},[461,2070,2071],{},"-- Ghi như JSON document (từ 9.7 — Community Edition)\n",[461,2073,2074],{"class":463,"line":1627},[461,2075,2076],{},"UPDATE user_dv SET data = JSON_SET(data, '$.name', 'Vinh Updated') WHERE data->>'$._id' = '1';\n",[427,2078,2080],{"id":2079},"_6-stored-procedures","6. Stored Procedures",[432,2082,2084],{"id":2083},"_61-create-procedure-cơ-bản","6.1 CREATE PROCEDURE cơ bản",[451,2086,2088],{"className":542,"code":2087,"language":544,"meta":456,"style":456},"DELIMITER $$\n\nCREATE PROCEDURE GetOrdersByUser(\n    IN  p_user_id INT,\n    IN  p_status  VARCHAR(20)\n)\nBEGIN\n    SELECT id, total, created_at, status\n    FROM orders\n    WHERE user_id = p_user_id\n      AND (p_status IS NULL OR status = p_status)\n    ORDER BY created_at DESC;\nEND$$\n\nDELIMITER ;\n\n-- Gọi procedure\nCALL GetOrdersByUser(1, 'done');\nCALL GetOrdersByUser(1, NULL);   -- trả về tất cả status\n",[458,2089,2090,2095,2099,2104,2109,2114,2119,2124,2129,2134,2139,2144,2149,2154,2158,2163,2167,2172,2177],{"__ignoreMap":456},[461,2091,2092],{"class":463,"line":464},[461,2093,2094],{},"DELIMITER $$\n",[461,2096,2097],{"class":463,"line":470},[461,2098,680],{"emptyLinePlaceholder":679},[461,2100,2101],{"class":463,"line":476},[461,2102,2103],{},"CREATE PROCEDURE GetOrdersByUser(\n",[461,2105,2106],{"class":463,"line":688},[461,2107,2108],{},"    IN  p_user_id INT,\n",[461,2110,2111],{"class":463,"line":694},[461,2112,2113],{},"    IN  p_status  VARCHAR(20)\n",[461,2115,2116],{"class":463,"line":700},[461,2117,2118],{},")\n",[461,2120,2121],{"class":463,"line":706},[461,2122,2123],{},"BEGIN\n",[461,2125,2126],{"class":463,"line":712},[461,2127,2128],{},"    SELECT id, total, created_at, status\n",[461,2130,2131],{"class":463,"line":858},[461,2132,2133],{},"    FROM orders\n",[461,2135,2136],{"class":463,"line":864},[461,2137,2138],{},"    WHERE user_id = p_user_id\n",[461,2140,2141],{"class":463,"line":1018},[461,2142,2143],{},"      AND (p_status IS NULL OR status = p_status)\n",[461,2145,2146],{"class":463,"line":1024},[461,2147,2148],{},"    ORDER BY created_at DESC;\n",[461,2150,2151],{"class":463,"line":1030},[461,2152,2153],{},"END$$\n",[461,2155,2156],{"class":463,"line":1604},[461,2157,680],{"emptyLinePlaceholder":679},[461,2159,2160],{"class":463,"line":1610},[461,2161,2162],{},"DELIMITER ;\n",[461,2164,2165],{"class":463,"line":1616},[461,2166,680],{"emptyLinePlaceholder":679},[461,2168,2169],{"class":463,"line":1622},[461,2170,2171],{},"-- Gọi procedure\n",[461,2173,2174],{"class":463,"line":1627},[461,2175,2176],{},"CALL GetOrdersByUser(1, 'done');\n",[461,2178,2179],{"class":463,"line":1632},[461,2180,2181],{},"CALL GetOrdersByUser(1, NULL);   -- trả về tất cả status\n",[432,2183,2185],{"id":2184},"_62-tham-số-in-out-inout","6.2 Tham số IN, OUT, INOUT",[451,2187,2189],{"className":542,"code":2188,"language":544,"meta":456,"style":456},"DELIMITER $$\n\nCREATE PROCEDURE GetOrderStats(\n    IN   p_user_id INT,\n    OUT  p_total   DECIMAL(18,2),\n    OUT  p_count   INT\n)\nBEGIN\n    SELECT SUM(amount), COUNT(*)\n    INTO p_total, p_count\n    FROM orders\n    WHERE user_id = p_user_id;\nEND$$\n\nDELIMITER ;\n\n-- Gọi và lấy OUT params\nCALL GetOrderStats(1, @total, @count);\nSELECT @total, @count;\n",[458,2190,2191,2195,2199,2204,2209,2214,2219,2223,2227,2232,2237,2241,2246,2250,2254,2258,2262,2267,2272],{"__ignoreMap":456},[461,2192,2193],{"class":463,"line":464},[461,2194,2094],{},[461,2196,2197],{"class":463,"line":470},[461,2198,680],{"emptyLinePlaceholder":679},[461,2200,2201],{"class":463,"line":476},[461,2202,2203],{},"CREATE PROCEDURE GetOrderStats(\n",[461,2205,2206],{"class":463,"line":688},[461,2207,2208],{},"    IN   p_user_id INT,\n",[461,2210,2211],{"class":463,"line":694},[461,2212,2213],{},"    OUT  p_total   DECIMAL(18,2),\n",[461,2215,2216],{"class":463,"line":700},[461,2217,2218],{},"    OUT  p_count   INT\n",[461,2220,2221],{"class":463,"line":706},[461,2222,2118],{},[461,2224,2225],{"class":463,"line":712},[461,2226,2123],{},[461,2228,2229],{"class":463,"line":858},[461,2230,2231],{},"    SELECT SUM(amount), COUNT(*)\n",[461,2233,2234],{"class":463,"line":864},[461,2235,2236],{},"    INTO p_total, p_count\n",[461,2238,2239],{"class":463,"line":1018},[461,2240,2133],{},[461,2242,2243],{"class":463,"line":1024},[461,2244,2245],{},"    WHERE user_id = p_user_id;\n",[461,2247,2248],{"class":463,"line":1030},[461,2249,2153],{},[461,2251,2252],{"class":463,"line":1604},[461,2253,680],{"emptyLinePlaceholder":679},[461,2255,2256],{"class":463,"line":1610},[461,2257,2162],{},[461,2259,2260],{"class":463,"line":1616},[461,2261,680],{"emptyLinePlaceholder":679},[461,2263,2264],{"class":463,"line":1622},[461,2265,2266],{},"-- Gọi và lấy OUT params\n",[461,2268,2269],{"class":463,"line":1627},[461,2270,2271],{},"CALL GetOrderStats(1, @total, @count);\n",[461,2273,2274],{"class":463,"line":1632},[461,2275,2276],{},"SELECT @total, @count;\n",[1653,2278,2279,2289],{},[1656,2280,2281],{},[1659,2282,2283,2286],{},[1662,2284,2285],{},"Loại",[1662,2287,2288],{},"Mô tả",[1672,2290,2291,2301,2311],{},[1659,2292,2293,2298],{},[1677,2294,2295],{},[458,2296,2297],{},"IN",[1677,2299,2300],{},"Mặc định. Truyền giá trị vào procedure.",[1659,2302,2303,2308],{},[1677,2304,2305],{},[458,2306,2307],{},"OUT",[1677,2309,2310],{},"Procedure gán giá trị cho biến này trước khi kết thúc.",[1659,2312,2313,2318],{},[1677,2314,2315],{},[458,2316,2317],{},"INOUT",[1677,2319,2320],{},"Vừa truyền vào vừa có thể gán lại.",[432,2322,2324],{"id":2323},"_63-biến-điều-kiện-vòng-lặp","6.3 Biến, điều kiện, vòng lặp",[451,2326,2328],{"className":542,"code":2327,"language":544,"meta":456,"style":456},"DELIMITER $$\n\nCREATE PROCEDURE ProcessOrders(IN p_user_id INT)\nBEGIN\n    DECLARE v_order_id INT;\n    DECLARE v_amount DECIMAL(18,2);\n    DECLARE v_done INT DEFAULT 0;\n\n    -- CURSOR\n    DECLARE cur CURSOR FOR\n        SELECT id, amount FROM orders WHERE user_id = p_user_id;\n\n    -- HANDLER: khi cursor hết row → set v_done = 1\n    DECLARE CONTINUE HANDLER FOR NOT FOUND SET v_done = 1;\n\n    OPEN cur;\n\n    read_loop: LOOP\n        FETCH cur INTO v_order_id, v_amount;\n\n        IF v_done = 1 THEN\n            LEAVE read_loop;\n        END IF;\n\n        -- Xử lý từng row\n        IF v_amount > 1000 THEN\n            UPDATE orders SET priority = 'HIGH' WHERE id = v_order_id;\n        ELSE\n            UPDATE orders SET priority = 'NORMAL' WHERE id = v_order_id;\n        END IF;\n\n    END LOOP read_loop;\n\n    CLOSE cur;\nEND$$\n\nDELIMITER ;\n",[458,2329,2330,2334,2338,2343,2347,2352,2357,2362,2366,2371,2376,2381,2385,2390,2395,2399,2404,2408,2413,2418,2422,2427,2433,2439,2444,2450,2456,2462,2468,2474,2479,2484,2490,2495,2501,2506,2511],{"__ignoreMap":456},[461,2331,2332],{"class":463,"line":464},[461,2333,2094],{},[461,2335,2336],{"class":463,"line":470},[461,2337,680],{"emptyLinePlaceholder":679},[461,2339,2340],{"class":463,"line":476},[461,2341,2342],{},"CREATE PROCEDURE ProcessOrders(IN p_user_id INT)\n",[461,2344,2345],{"class":463,"line":688},[461,2346,2123],{},[461,2348,2349],{"class":463,"line":694},[461,2350,2351],{},"    DECLARE v_order_id INT;\n",[461,2353,2354],{"class":463,"line":700},[461,2355,2356],{},"    DECLARE v_amount DECIMAL(18,2);\n",[461,2358,2359],{"class":463,"line":706},[461,2360,2361],{},"    DECLARE v_done INT DEFAULT 0;\n",[461,2363,2364],{"class":463,"line":712},[461,2365,680],{"emptyLinePlaceholder":679},[461,2367,2368],{"class":463,"line":858},[461,2369,2370],{},"    -- CURSOR\n",[461,2372,2373],{"class":463,"line":864},[461,2374,2375],{},"    DECLARE cur CURSOR FOR\n",[461,2377,2378],{"class":463,"line":1018},[461,2379,2380],{},"        SELECT id, amount FROM orders WHERE user_id = p_user_id;\n",[461,2382,2383],{"class":463,"line":1024},[461,2384,680],{"emptyLinePlaceholder":679},[461,2386,2387],{"class":463,"line":1030},[461,2388,2389],{},"    -- HANDLER: khi cursor hết row → set v_done = 1\n",[461,2391,2392],{"class":463,"line":1604},[461,2393,2394],{},"    DECLARE CONTINUE HANDLER FOR NOT FOUND SET v_done = 1;\n",[461,2396,2397],{"class":463,"line":1610},[461,2398,680],{"emptyLinePlaceholder":679},[461,2400,2401],{"class":463,"line":1616},[461,2402,2403],{},"    OPEN cur;\n",[461,2405,2406],{"class":463,"line":1622},[461,2407,680],{"emptyLinePlaceholder":679},[461,2409,2410],{"class":463,"line":1627},[461,2411,2412],{},"    read_loop: LOOP\n",[461,2414,2415],{"class":463,"line":1632},[461,2416,2417],{},"        FETCH cur INTO v_order_id, v_amount;\n",[461,2419,2420],{"class":463,"line":1638},[461,2421,680],{"emptyLinePlaceholder":679},[461,2423,2424],{"class":463,"line":1644},[461,2425,2426],{},"        IF v_done = 1 THEN\n",[461,2428,2430],{"class":463,"line":2429},22,[461,2431,2432],{},"            LEAVE read_loop;\n",[461,2434,2436],{"class":463,"line":2435},23,[461,2437,2438],{},"        END IF;\n",[461,2440,2442],{"class":463,"line":2441},24,[461,2443,680],{"emptyLinePlaceholder":679},[461,2445,2447],{"class":463,"line":2446},25,[461,2448,2449],{},"        -- Xử lý từng row\n",[461,2451,2453],{"class":463,"line":2452},26,[461,2454,2455],{},"        IF v_amount > 1000 THEN\n",[461,2457,2459],{"class":463,"line":2458},27,[461,2460,2461],{},"            UPDATE orders SET priority = 'HIGH' WHERE id = v_order_id;\n",[461,2463,2465],{"class":463,"line":2464},28,[461,2466,2467],{},"        ELSE\n",[461,2469,2471],{"class":463,"line":2470},29,[461,2472,2473],{},"            UPDATE orders SET priority = 'NORMAL' WHERE id = v_order_id;\n",[461,2475,2477],{"class":463,"line":2476},30,[461,2478,2438],{},[461,2480,2482],{"class":463,"line":2481},31,[461,2483,680],{"emptyLinePlaceholder":679},[461,2485,2487],{"class":463,"line":2486},32,[461,2488,2489],{},"    END LOOP read_loop;\n",[461,2491,2493],{"class":463,"line":2492},33,[461,2494,680],{"emptyLinePlaceholder":679},[461,2496,2498],{"class":463,"line":2497},34,[461,2499,2500],{},"    CLOSE cur;\n",[461,2502,2504],{"class":463,"line":2503},35,[461,2505,2153],{},[461,2507,2509],{"class":463,"line":2508},36,[461,2510,680],{"emptyLinePlaceholder":679},[461,2512,2514],{"class":463,"line":2513},37,[461,2515,2162],{},[432,2517,2519],{"id":2518},"_64-conditional-logic-đầy-đủ","6.4 Conditional logic đầy đủ",[451,2521,2523],{"className":542,"code":2522,"language":544,"meta":456,"style":456},"-- IF \u002F ELSEIF \u002F ELSE\nIF v_amount > 10000 THEN\n    SET v_discount = 20;\nELSEIF v_amount > 5000 THEN\n    SET v_discount = 10;\nELSE\n    SET v_discount = 0;\nEND IF;\n\n-- CASE expression\nSET v_status = CASE\n    WHEN v_days_overdue > 30 THEN 'Bad Debt'\n    WHEN v_days_overdue > 0  THEN 'Overdue'\n    ELSE 'Current'\nEND;\n\n-- LOOP \u002F WHILE \u002F REPEAT\nWHILE v_counter \u003C 10 DO\n    SET v_counter = v_counter + 1;\nEND WHILE;\n\nREPEAT\n    SET v_counter = v_counter - 1;\nUNTIL v_counter = 0 END REPEAT;\n",[458,2524,2525,2530,2535,2540,2545,2550,2555,2560,2565,2569,2574,2579,2584,2589,2594,2599,2603,2608,2613,2618,2623,2627,2632,2637],{"__ignoreMap":456},[461,2526,2527],{"class":463,"line":464},[461,2528,2529],{},"-- IF \u002F ELSEIF \u002F ELSE\n",[461,2531,2532],{"class":463,"line":470},[461,2533,2534],{},"IF v_amount > 10000 THEN\n",[461,2536,2537],{"class":463,"line":476},[461,2538,2539],{},"    SET v_discount = 20;\n",[461,2541,2542],{"class":463,"line":688},[461,2543,2544],{},"ELSEIF v_amount > 5000 THEN\n",[461,2546,2547],{"class":463,"line":694},[461,2548,2549],{},"    SET v_discount = 10;\n",[461,2551,2552],{"class":463,"line":700},[461,2553,2554],{},"ELSE\n",[461,2556,2557],{"class":463,"line":706},[461,2558,2559],{},"    SET v_discount = 0;\n",[461,2561,2562],{"class":463,"line":712},[461,2563,2564],{},"END IF;\n",[461,2566,2567],{"class":463,"line":858},[461,2568,680],{"emptyLinePlaceholder":679},[461,2570,2571],{"class":463,"line":864},[461,2572,2573],{},"-- CASE expression\n",[461,2575,2576],{"class":463,"line":1018},[461,2577,2578],{},"SET v_status = CASE\n",[461,2580,2581],{"class":463,"line":1024},[461,2582,2583],{},"    WHEN v_days_overdue > 30 THEN 'Bad Debt'\n",[461,2585,2586],{"class":463,"line":1030},[461,2587,2588],{},"    WHEN v_days_overdue > 0  THEN 'Overdue'\n",[461,2590,2591],{"class":463,"line":1604},[461,2592,2593],{},"    ELSE 'Current'\n",[461,2595,2596],{"class":463,"line":1610},[461,2597,2598],{},"END;\n",[461,2600,2601],{"class":463,"line":1616},[461,2602,680],{"emptyLinePlaceholder":679},[461,2604,2605],{"class":463,"line":1622},[461,2606,2607],{},"-- LOOP \u002F WHILE \u002F REPEAT\n",[461,2609,2610],{"class":463,"line":1627},[461,2611,2612],{},"WHILE v_counter \u003C 10 DO\n",[461,2614,2615],{"class":463,"line":1632},[461,2616,2617],{},"    SET v_counter = v_counter + 1;\n",[461,2619,2620],{"class":463,"line":1638},[461,2621,2622],{},"END WHILE;\n",[461,2624,2625],{"class":463,"line":1644},[461,2626,680],{"emptyLinePlaceholder":679},[461,2628,2629],{"class":463,"line":2429},[461,2630,2631],{},"REPEAT\n",[461,2633,2634],{"class":463,"line":2435},[461,2635,2636],{},"    SET v_counter = v_counter - 1;\n",[461,2638,2639],{"class":463,"line":2441},[461,2640,2641],{},"UNTIL v_counter = 0 END REPEAT;\n",[432,2643,2645],{"id":2644},"_65-procedure-vs-function","6.5 Procedure vs Function",[1653,2647,2648,2660],{},[1656,2649,2650],{},[1659,2651,2652,2654,2657],{},[1662,2653],{},[1662,2655,2656],{},"Stored Procedure",[1662,2658,2659],{},"Stored Function",[1672,2661,2662,2673,2684,2695,2706],{},[1659,2663,2664,2667,2670],{},[1677,2665,2666],{},"Trả về",[1677,2668,2669],{},"Nhiều result set + OUT params",[1677,2671,2672],{},"1 giá trị (scalar)",[1659,2674,2675,2678,2681],{},[1677,2676,2677],{},"Dùng trong SQL",[1677,2679,2680],{},"❌ Không (CALL riêng)",[1677,2682,2683],{},"✅ Trong SELECT\u002FWHERE",[1659,2685,2686,2689,2692],{},[1677,2687,2688],{},"Transaction",[1677,2690,2691],{},"✅ Có COMMIT\u002FROLLBACK",[1677,2693,2694],{},"❌ Không (limited)",[1659,2696,2697,2700,2703],{},[1677,2698,2699],{},"Side effect",[1677,2701,2702],{},"✅ UPDATE\u002FINSERT\u002FDELETE",[1677,2704,2705],{},"Hạn chế (READS SQL DATA)",[1659,2707,2708,2711,2714],{},[1677,2709,2710],{},"Use case",[1677,2712,2713],{},"Business logic, batch process",[1677,2715,2716],{},"Tính toán, transform data",[437,2718,2719,2726],{"color":439,"icon":440},[442,2720,444,2721,576,2723],{},[446,2722,575],{},[578,2724,2725],{},"\"Khi nào dùng Function, khi nào dùng Procedure trong MySQL?\"",[582,2727,2728],{},[442,2729,2730],{},"\"Function khi cần tính toán trả về 1 giá trị và dùng được trong SELECT\u002FWHERE — giống built-in function. Procedure khi cần logic phức tạp: transaction, nhiều result set, batch processing, business logic nhiều bước. Function bị hạn chế về side effect (không COMMIT\u002FROLLBACK, nên tránh UPDATE), Procedure thì không.\"",[427,2732,2734],{"id":2733},"_7-triggers","7. Triggers",[432,2736,2738],{"id":2737},"_71-create-trigger-cơ-bản","7.1 CREATE TRIGGER cơ bản",[451,2740,2742],{"className":542,"code":2741,"language":544,"meta":456,"style":456},"CREATE TRIGGER trigger_name\n{BEFORE | AFTER} {INSERT | UPDATE | DELETE}\nON table_name\nFOR EACH ROW\ntrigger_body;\n",[458,2743,2744,2749,2754,2759,2764],{"__ignoreMap":456},[461,2745,2746],{"class":463,"line":464},[461,2747,2748],{},"CREATE TRIGGER trigger_name\n",[461,2750,2751],{"class":463,"line":470},[461,2752,2753],{},"{BEFORE | AFTER} {INSERT | UPDATE | DELETE}\n",[461,2755,2756],{"class":463,"line":476},[461,2757,2758],{},"ON table_name\n",[461,2760,2761],{"class":463,"line":688},[461,2762,2763],{},"FOR EACH ROW\n",[461,2765,2766],{"class":463,"line":694},[461,2767,2768],{},"trigger_body;\n",[432,2770,2772],{"id":2771},"_72-old-và-new","7.2 OLD và NEW",[488,2774,2775,2785,2794],{},[491,2776,2777,2780,2781,2784],{},[446,2778,2779],{},"INSERT",": chỉ có ",[458,2782,2783],{},"NEW"," — giá trị row sắp insert.",[491,2786,2787,2780,2790,2793],{},[446,2788,2789],{},"DELETE",[458,2791,2792],{},"OLD"," — giá trị row sắp bị xóa.",[491,2795,2796,2799,2800,2802,2803,2805,2806,2809,2810,2813],{},[446,2797,2798],{},"UPDATE",": cả ",[458,2801,2792],{}," và ",[458,2804,2783],{}," — ",[458,2807,2808],{},"OLD.col"," là giá trị cũ, ",[458,2811,2812],{},"NEW.col"," là giá trị mới.",[451,2815,2817],{"className":542,"code":2816,"language":544,"meta":456,"style":456},"DELIMITER $$\n\nCREATE TRIGGER before_order_insert\nBEFORE INSERT ON orders\nFOR EACH ROW\nBEGIN\n    -- Tự động set created_at nếu NULL\n    IF NEW.created_at IS NULL THEN\n        SET NEW.created_at = NOW();\n    END IF;\n\n    -- Validate amount > 0\n    IF NEW.amount \u003C= 0 THEN\n        SIGNAL SQLSTATE '45000'\n            SET MESSAGE_TEXT = 'amount must be positive';\n    END IF;\nEND$$\n\nDELIMITER ;\n",[458,2818,2819,2823,2827,2832,2837,2841,2845,2850,2855,2860,2865,2869,2874,2879,2884,2889,2893,2897,2901],{"__ignoreMap":456},[461,2820,2821],{"class":463,"line":464},[461,2822,2094],{},[461,2824,2825],{"class":463,"line":470},[461,2826,680],{"emptyLinePlaceholder":679},[461,2828,2829],{"class":463,"line":476},[461,2830,2831],{},"CREATE TRIGGER before_order_insert\n",[461,2833,2834],{"class":463,"line":688},[461,2835,2836],{},"BEFORE INSERT ON orders\n",[461,2838,2839],{"class":463,"line":694},[461,2840,2763],{},[461,2842,2843],{"class":463,"line":700},[461,2844,2123],{},[461,2846,2847],{"class":463,"line":706},[461,2848,2849],{},"    -- Tự động set created_at nếu NULL\n",[461,2851,2852],{"class":463,"line":712},[461,2853,2854],{},"    IF NEW.created_at IS NULL THEN\n",[461,2856,2857],{"class":463,"line":858},[461,2858,2859],{},"        SET NEW.created_at = NOW();\n",[461,2861,2862],{"class":463,"line":864},[461,2863,2864],{},"    END IF;\n",[461,2866,2867],{"class":463,"line":1018},[461,2868,680],{"emptyLinePlaceholder":679},[461,2870,2871],{"class":463,"line":1024},[461,2872,2873],{},"    -- Validate amount > 0\n",[461,2875,2876],{"class":463,"line":1030},[461,2877,2878],{},"    IF NEW.amount \u003C= 0 THEN\n",[461,2880,2881],{"class":463,"line":1604},[461,2882,2883],{},"        SIGNAL SQLSTATE '45000'\n",[461,2885,2886],{"class":463,"line":1610},[461,2887,2888],{},"            SET MESSAGE_TEXT = 'amount must be positive';\n",[461,2890,2891],{"class":463,"line":1616},[461,2892,2864],{},[461,2894,2895],{"class":463,"line":1622},[461,2896,2153],{},[461,2898,2899],{"class":463,"line":1627},[461,2900,680],{"emptyLinePlaceholder":679},[461,2902,2903],{"class":463,"line":1632},[461,2904,2162],{},[432,2906,2908],{"id":2907},"_73-use-cases-điển-hình","7.3 Use cases điển hình",[442,2910,2911],{},[446,2912,2913],{},"a) Audit log",[451,2915,2917],{"className":542,"code":2916,"language":544,"meta":456,"style":456},"CREATE TABLE orders_audit (\n    audit_id INT PRIMARY KEY AUTO_INCREMENT,\n    order_id INT,\n    old_status VARCHAR(20),\n    new_status VARCHAR(20),\n    changed_at DATETIME DEFAULT NOW(),\n    changed_by VARCHAR(100)\n);\n\nDELIMITER $$\n\nCREATE TRIGGER audit_order_update\nAFTER UPDATE ON orders\nFOR EACH ROW\nBEGIN\n    IF OLD.status != NEW.status THEN\n        INSERT INTO orders_audit (order_id, old_status, new_status, changed_by)\n        VALUES (OLD.id, OLD.status, NEW.status, CURRENT_USER());\n    END IF;\nEND$$\n\nDELIMITER ;\n",[458,2918,2919,2924,2929,2934,2939,2944,2949,2954,2958,2962,2966,2970,2975,2980,2984,2988,2993,2998,3003,3007,3011,3015],{"__ignoreMap":456},[461,2920,2921],{"class":463,"line":464},[461,2922,2923],{},"CREATE TABLE orders_audit (\n",[461,2925,2926],{"class":463,"line":470},[461,2927,2928],{},"    audit_id INT PRIMARY KEY AUTO_INCREMENT,\n",[461,2930,2931],{"class":463,"line":476},[461,2932,2933],{},"    order_id INT,\n",[461,2935,2936],{"class":463,"line":688},[461,2937,2938],{},"    old_status VARCHAR(20),\n",[461,2940,2941],{"class":463,"line":694},[461,2942,2943],{},"    new_status VARCHAR(20),\n",[461,2945,2946],{"class":463,"line":700},[461,2947,2948],{},"    changed_at DATETIME DEFAULT NOW(),\n",[461,2950,2951],{"class":463,"line":706},[461,2952,2953],{},"    changed_by VARCHAR(100)\n",[461,2955,2956],{"class":463,"line":712},[461,2957,1001],{},[461,2959,2960],{"class":463,"line":858},[461,2961,680],{"emptyLinePlaceholder":679},[461,2963,2964],{"class":463,"line":864},[461,2965,2094],{},[461,2967,2968],{"class":463,"line":1018},[461,2969,680],{"emptyLinePlaceholder":679},[461,2971,2972],{"class":463,"line":1024},[461,2973,2974],{},"CREATE TRIGGER audit_order_update\n",[461,2976,2977],{"class":463,"line":1030},[461,2978,2979],{},"AFTER UPDATE ON orders\n",[461,2981,2982],{"class":463,"line":1604},[461,2983,2763],{},[461,2985,2986],{"class":463,"line":1610},[461,2987,2123],{},[461,2989,2990],{"class":463,"line":1616},[461,2991,2992],{},"    IF OLD.status != NEW.status THEN\n",[461,2994,2995],{"class":463,"line":1622},[461,2996,2997],{},"        INSERT INTO orders_audit (order_id, old_status, new_status, changed_by)\n",[461,2999,3000],{"class":463,"line":1627},[461,3001,3002],{},"        VALUES (OLD.id, OLD.status, NEW.status, CURRENT_USER());\n",[461,3004,3005],{"class":463,"line":1632},[461,3006,2864],{},[461,3008,3009],{"class":463,"line":1638},[461,3010,2153],{},[461,3012,3013],{"class":463,"line":1644},[461,3014,680],{"emptyLinePlaceholder":679},[461,3016,3017],{"class":463,"line":2429},[461,3018,2162],{},[442,3020,3021],{},[446,3022,3023],{},"b) Derived column",[451,3025,3027],{"className":542,"code":3026,"language":544,"meta":456,"style":456},"-- Tự động tính total = quantity * unit_price\nCREATE TRIGGER calc_total\nBEFORE INSERT ON order_items\nFOR EACH ROW\nSET NEW.total = NEW.quantity * NEW.unit_price;\n",[458,3028,3029,3034,3039,3044,3048],{"__ignoreMap":456},[461,3030,3031],{"class":463,"line":464},[461,3032,3033],{},"-- Tự động tính total = quantity * unit_price\n",[461,3035,3036],{"class":463,"line":470},[461,3037,3038],{},"CREATE TRIGGER calc_total\n",[461,3040,3041],{"class":463,"line":476},[461,3042,3043],{},"BEFORE INSERT ON order_items\n",[461,3045,3046],{"class":463,"line":688},[461,3047,2763],{},[461,3049,3050],{"class":463,"line":694},[461,3051,3052],{},"SET NEW.total = NEW.quantity * NEW.unit_price;\n",[442,3054,3055],{},[446,3056,3057],{},"c) Validation",[451,3059,3061],{"className":542,"code":3060,"language":544,"meta":456,"style":456},"CREATE TRIGGER validate_stock\nBEFORE INSERT ON order_items\nFOR EACH ROW\nBEGIN\n    DECLARE v_stock INT;\n    SELECT stock INTO v_stock FROM products WHERE id = NEW.product_id;\n    IF v_stock \u003C NEW.quantity THEN\n        SIGNAL SQLSTATE '45000'\n            SET MESSAGE_TEXT = 'Insufficient stock';\n    END IF;\nEND;\n",[458,3062,3063,3068,3072,3076,3080,3085,3090,3095,3099,3104,3108],{"__ignoreMap":456},[461,3064,3065],{"class":463,"line":464},[461,3066,3067],{},"CREATE TRIGGER validate_stock\n",[461,3069,3070],{"class":463,"line":470},[461,3071,3043],{},[461,3073,3074],{"class":463,"line":476},[461,3075,2763],{},[461,3077,3078],{"class":463,"line":688},[461,3079,2123],{},[461,3081,3082],{"class":463,"line":694},[461,3083,3084],{},"    DECLARE v_stock INT;\n",[461,3086,3087],{"class":463,"line":700},[461,3088,3089],{},"    SELECT stock INTO v_stock FROM products WHERE id = NEW.product_id;\n",[461,3091,3092],{"class":463,"line":706},[461,3093,3094],{},"    IF v_stock \u003C NEW.quantity THEN\n",[461,3096,3097],{"class":463,"line":712},[461,3098,2883],{},[461,3100,3101],{"class":463,"line":858},[461,3102,3103],{},"            SET MESSAGE_TEXT = 'Insufficient stock';\n",[461,3105,3106],{"class":463,"line":864},[461,3107,2864],{},[461,3109,3110],{"class":463,"line":1018},[461,3111,2598],{},[437,3113,3114,3117,3118,3121],{"color":870,"icon":871},[446,3115,3116],{},"Cảnh báo:"," Trigger chạy ",[446,3119,3120],{},"FOR EACH ROW"," — với batch INSERT\u002FUPDATE hàng triệu row, trigger biến 1 câu SQL thành triệu lần thực thi. Cân nhắc dùng stored procedure batch hoặc application-level thay thế cho high-volume operations.",[427,3123,3125],{"id":3124},"_8-views","8. Views",[432,3127,3129],{"id":3128},"_81-create-view","8.1 CREATE VIEW",[451,3131,3133],{"className":542,"code":3132,"language":544,"meta":456,"style":456},"-- View cơ bản\nCREATE VIEW active_users AS\nSELECT id, name, email, created_at\nFROM users\nWHERE status = 'active';\n\n-- Query view như bảng thường\nSELECT * FROM active_users WHERE email LIKE '%@gmail.com';\n\n-- CREATE OR REPLACE (sửa view không cần DROP)\nCREATE OR REPLACE VIEW active_users AS\nSELECT id, name, email, phone, created_at\nFROM users\nWHERE status = 'active' AND deleted_at IS NULL;\n",[458,3134,3135,3140,3145,3150,3155,3160,3164,3169,3174,3178,3183,3188,3193,3197],{"__ignoreMap":456},[461,3136,3137],{"class":463,"line":464},[461,3138,3139],{},"-- View cơ bản\n",[461,3141,3142],{"class":463,"line":470},[461,3143,3144],{},"CREATE VIEW active_users AS\n",[461,3146,3147],{"class":463,"line":476},[461,3148,3149],{},"SELECT id, name, email, created_at\n",[461,3151,3152],{"class":463,"line":688},[461,3153,3154],{},"FROM users\n",[461,3156,3157],{"class":463,"line":694},[461,3158,3159],{},"WHERE status = 'active';\n",[461,3161,3162],{"class":463,"line":700},[461,3163,680],{"emptyLinePlaceholder":679},[461,3165,3166],{"class":463,"line":706},[461,3167,3168],{},"-- Query view như bảng thường\n",[461,3170,3171],{"class":463,"line":712},[461,3172,3173],{},"SELECT * FROM active_users WHERE email LIKE '%@gmail.com';\n",[461,3175,3176],{"class":463,"line":858},[461,3177,680],{"emptyLinePlaceholder":679},[461,3179,3180],{"class":463,"line":864},[461,3181,3182],{},"-- CREATE OR REPLACE (sửa view không cần DROP)\n",[461,3184,3185],{"class":463,"line":1018},[461,3186,3187],{},"CREATE OR REPLACE VIEW active_users AS\n",[461,3189,3190],{"class":463,"line":1024},[461,3191,3192],{},"SELECT id, name, email, phone, created_at\n",[461,3194,3195],{"class":463,"line":1030},[461,3196,3154],{},[461,3198,3199],{"class":463,"line":1604},[461,3200,3201],{},"WHERE status = 'active' AND deleted_at IS NULL;\n",[432,3203,3205],{"id":3204},"_82-updatable-views","8.2 Updatable views",[442,3207,3208],{},"View có thể UPDATE\u002FINSERT\u002FDELETE nếu thỏa điều kiện: không có JOIN, UNION, GROUP BY, DISTINCT, aggregation, subquery trong SELECT list.",[451,3210,3212],{"className":542,"code":3211,"language":544,"meta":456,"style":456},"-- Updatable view (chỉ SELECT đơn giản từ 1 bảng)\nCREATE VIEW vip_users AS\nSELECT id, name, email, vip_level FROM users WHERE vip_level > 0;\n\n-- Có thể UPDATE qua view\nUPDATE vip_users SET vip_level = 3 WHERE id = 1;\n-- Dù cột vip_level > 0 sau UPDATE, row vẫn trong view (vip_level = 3 > 0)\n\n-- Có thể DELETE qua view\nDELETE FROM vip_users WHERE id = 2;\n",[458,3213,3214,3219,3224,3229,3233,3238,3243,3248,3252,3257],{"__ignoreMap":456},[461,3215,3216],{"class":463,"line":464},[461,3217,3218],{},"-- Updatable view (chỉ SELECT đơn giản từ 1 bảng)\n",[461,3220,3221],{"class":463,"line":470},[461,3222,3223],{},"CREATE VIEW vip_users AS\n",[461,3225,3226],{"class":463,"line":476},[461,3227,3228],{},"SELECT id, name, email, vip_level FROM users WHERE vip_level > 0;\n",[461,3230,3231],{"class":463,"line":688},[461,3232,680],{"emptyLinePlaceholder":679},[461,3234,3235],{"class":463,"line":694},[461,3236,3237],{},"-- Có thể UPDATE qua view\n",[461,3239,3240],{"class":463,"line":700},[461,3241,3242],{},"UPDATE vip_users SET vip_level = 3 WHERE id = 1;\n",[461,3244,3245],{"class":463,"line":706},[461,3246,3247],{},"-- Dù cột vip_level > 0 sau UPDATE, row vẫn trong view (vip_level = 3 > 0)\n",[461,3249,3250],{"class":463,"line":712},[461,3251,680],{"emptyLinePlaceholder":679},[461,3253,3254],{"class":463,"line":858},[461,3255,3256],{},"-- Có thể DELETE qua view\n",[461,3258,3259],{"class":463,"line":864},[461,3260,3261],{},"DELETE FROM vip_users WHERE id = 2;\n",[432,3263,3265],{"id":3264},"_83-with-check-option","8.3 WITH CHECK OPTION",[442,3267,3268],{},"Ngăn UPDATE\u002FINSERT tạo ra row không thỏa WHERE condition của view.",[451,3270,3272],{"className":542,"code":3271,"language":544,"meta":456,"style":456},"CREATE VIEW vip_users AS\nSELECT id, name, email, vip_level FROM users WHERE vip_level > 0\nWITH CHECK OPTION;\n\n-- ✅ OK: vip_level = 3 > 0, vẫn thỏa view condition\nUPDATE vip_users SET vip_level = 3 WHERE id = 1;\n\n-- ❌ Lỗi: vip_level = 0, không thỏa view condition → bị từ chối\nUPDATE vip_users SET vip_level = 0 WHERE id = 1;\n-- Error: CHECK OPTION failed\n",[458,3273,3274,3278,3283,3288,3292,3297,3301,3305,3310,3315],{"__ignoreMap":456},[461,3275,3276],{"class":463,"line":464},[461,3277,3223],{},[461,3279,3280],{"class":463,"line":470},[461,3281,3282],{},"SELECT id, name, email, vip_level FROM users WHERE vip_level > 0\n",[461,3284,3285],{"class":463,"line":476},[461,3286,3287],{},"WITH CHECK OPTION;\n",[461,3289,3290],{"class":463,"line":688},[461,3291,680],{"emptyLinePlaceholder":679},[461,3293,3294],{"class":463,"line":694},[461,3295,3296],{},"-- ✅ OK: vip_level = 3 > 0, vẫn thỏa view condition\n",[461,3298,3299],{"class":463,"line":700},[461,3300,3242],{},[461,3302,3303],{"class":463,"line":706},[461,3304,680],{"emptyLinePlaceholder":679},[461,3306,3307],{"class":463,"line":712},[461,3308,3309],{},"-- ❌ Lỗi: vip_level = 0, không thỏa view condition → bị từ chối\n",[461,3311,3312],{"class":463,"line":858},[461,3313,3314],{},"UPDATE vip_users SET vip_level = 0 WHERE id = 1;\n",[461,3316,3317],{"class":463,"line":864},[461,3318,3319],{},"-- Error: CHECK OPTION failed\n",[437,3321,3322,3329],{"color":439,"icon":440},[442,3323,444,3324,576,3326],{},[446,3325,575],{},[578,3327,3328],{},"\"WITH CHECK OPTION trong MySQL view dùng để làm gì?\"",[582,3330,3331],{},[442,3332,3333,3334,3337,3338,3341],{},"\"Đảm bảo mọi INSERT\u002FUPDATE qua view đều tạo ra row thỏa mãn điều kiện WHERE của view đó. Giống constraint enforcement qua view. Ví dụ: view ",[458,3335,3336],{},"vip_users WHERE vip_level > 0"," với CHECK OPTION → không thể UPDATE ",[458,3339,3340],{},"vip_level = 0"," qua view này. Không có CHECK OPTION, row sau UPDATE sẽ 'biến mất' khỏi view (không còn thỏa WHERE) nhưng vẫn tồn tại trong bảng.\"",[432,3343,3345],{"id":3344},"_84-materialized-views-workaround","8.4 Materialized views (workaround)",[442,3347,3348],{},"MySQL không có materialized view native. Workaround: tạo bảng snapshot + Event\u002FTrigger refresh định kỳ.",[451,3350,3352],{"className":542,"code":3351,"language":544,"meta":456,"style":456},"-- Tạo bảng snapshot\nCREATE TABLE mv_daily_stats AS\nSELECT DATE(created_at) AS day, COUNT(*) AS order_count, SUM(amount) AS revenue\nFROM orders\nGROUP BY DATE(created_at);\n\n-- Refresh bằng Event (xem mục Events bên dưới)\nCREATE EVENT refresh_mv\nON SCHEDULE EVERY 1 HOUR\nDO\nBEGIN\n    TRUNCATE mv_daily_stats;\n    INSERT INTO mv_daily_stats\n    SELECT DATE(created_at), COUNT(*), SUM(amount) FROM orders GROUP BY DATE(created_at);\nEND;\n",[458,3353,3354,3359,3364,3369,3374,3379,3383,3388,3393,3398,3403,3407,3412,3417,3422],{"__ignoreMap":456},[461,3355,3356],{"class":463,"line":464},[461,3357,3358],{},"-- Tạo bảng snapshot\n",[461,3360,3361],{"class":463,"line":470},[461,3362,3363],{},"CREATE TABLE mv_daily_stats AS\n",[461,3365,3366],{"class":463,"line":476},[461,3367,3368],{},"SELECT DATE(created_at) AS day, COUNT(*) AS order_count, SUM(amount) AS revenue\n",[461,3370,3371],{"class":463,"line":688},[461,3372,3373],{},"FROM orders\n",[461,3375,3376],{"class":463,"line":694},[461,3377,3378],{},"GROUP BY DATE(created_at);\n",[461,3380,3381],{"class":463,"line":700},[461,3382,680],{"emptyLinePlaceholder":679},[461,3384,3385],{"class":463,"line":706},[461,3386,3387],{},"-- Refresh bằng Event (xem mục Events bên dưới)\n",[461,3389,3390],{"class":463,"line":712},[461,3391,3392],{},"CREATE EVENT refresh_mv\n",[461,3394,3395],{"class":463,"line":858},[461,3396,3397],{},"ON SCHEDULE EVERY 1 HOUR\n",[461,3399,3400],{"class":463,"line":864},[461,3401,3402],{},"DO\n",[461,3404,3405],{"class":463,"line":1018},[461,3406,2123],{},[461,3408,3409],{"class":463,"line":1024},[461,3410,3411],{},"    TRUNCATE mv_daily_stats;\n",[461,3413,3414],{"class":463,"line":1030},[461,3415,3416],{},"    INSERT INTO mv_daily_stats\n",[461,3418,3419],{"class":463,"line":1604},[461,3420,3421],{},"    SELECT DATE(created_at), COUNT(*), SUM(amount) FROM orders GROUP BY DATE(created_at);\n",[461,3423,3424],{"class":463,"line":1610},[461,3425,2598],{},[427,3427,3429],{"id":3428},"_9-events-scheduled-tasks","9. Events (Scheduled Tasks)",[442,3431,3432],{},"MySQL Event Scheduler giống cron job — thực thi SQL theo lịch định sẵn trong database.",[432,3434,3436],{"id":3435},"_91-bật-event-scheduler","9.1 Bật Event Scheduler",[451,3438,3440],{"className":542,"code":3439,"language":544,"meta":456,"style":456},"-- Kiểm tra trạng thái\nSHOW VARIABLES LIKE 'event_scheduler';\n\n-- Bật\nSET GLOBAL event_scheduler = ON;\n\n-- Config permanent\n-- [mysqld]\n-- event_scheduler = ON\n",[458,3441,3442,3447,3452,3456,3461,3466,3470,3475,3480],{"__ignoreMap":456},[461,3443,3444],{"class":463,"line":464},[461,3445,3446],{},"-- Kiểm tra trạng thái\n",[461,3448,3449],{"class":463,"line":470},[461,3450,3451],{},"SHOW VARIABLES LIKE 'event_scheduler';\n",[461,3453,3454],{"class":463,"line":476},[461,3455,680],{"emptyLinePlaceholder":679},[461,3457,3458],{"class":463,"line":688},[461,3459,3460],{},"-- Bật\n",[461,3462,3463],{"class":463,"line":694},[461,3464,3465],{},"SET GLOBAL event_scheduler = ON;\n",[461,3467,3468],{"class":463,"line":700},[461,3469,680],{"emptyLinePlaceholder":679},[461,3471,3472],{"class":463,"line":706},[461,3473,3474],{},"-- Config permanent\n",[461,3476,3477],{"class":463,"line":712},[461,3478,3479],{},"-- [mysqld]\n",[461,3481,3482],{"class":463,"line":858},[461,3483,3484],{},"-- event_scheduler = ON\n",[432,3486,3488],{"id":3487},"_92-create-event","9.2 CREATE EVENT",[451,3490,3492],{"className":542,"code":3491,"language":544,"meta":456,"style":456},"-- Chạy 1 lần vào thời điểm cụ thể (AT)\nCREATE EVENT clean_old_logs\nON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 HOUR\nDO\n    DELETE FROM access_logs WHERE created_at \u003C DATE_SUB(NOW(), INTERVAL 90 DAY);\n\n-- Chạy định kỳ (EVERY)\nCREATE EVENT daily_report\nON SCHEDULE EVERY 1 DAY\n    STARTS '2025-01-01 00:00:00'\n    ENDS '2025-12-31 23:59:59'\nDO\nBEGIN\n    INSERT INTO daily_stats (day, total_orders, revenue)\n    SELECT CURDATE(), COUNT(*), SUM(amount)\n    FROM orders\n    WHERE DATE(created_at) = CURDATE()\n    ON DUPLICATE KEY UPDATE\n        total_orders = VALUES(total_orders),\n        revenue = VALUES(revenue);\nEND;\n\n-- Bảo trì: OPTIMIZE mỗi tuần\nCREATE EVENT weekly_optimize\nON SCHEDULE EVERY 1 WEEK\nDO\n    OPTIMIZE TABLE orders, order_items, users;\n",[458,3493,3494,3499,3504,3509,3513,3518,3522,3527,3532,3537,3542,3547,3551,3555,3560,3565,3569,3574,3579,3584,3589,3593,3597,3602,3607,3612,3616],{"__ignoreMap":456},[461,3495,3496],{"class":463,"line":464},[461,3497,3498],{},"-- Chạy 1 lần vào thời điểm cụ thể (AT)\n",[461,3500,3501],{"class":463,"line":470},[461,3502,3503],{},"CREATE EVENT clean_old_logs\n",[461,3505,3506],{"class":463,"line":476},[461,3507,3508],{},"ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 HOUR\n",[461,3510,3511],{"class":463,"line":688},[461,3512,3402],{},[461,3514,3515],{"class":463,"line":694},[461,3516,3517],{},"    DELETE FROM access_logs WHERE created_at \u003C DATE_SUB(NOW(), INTERVAL 90 DAY);\n",[461,3519,3520],{"class":463,"line":700},[461,3521,680],{"emptyLinePlaceholder":679},[461,3523,3524],{"class":463,"line":706},[461,3525,3526],{},"-- Chạy định kỳ (EVERY)\n",[461,3528,3529],{"class":463,"line":712},[461,3530,3531],{},"CREATE EVENT daily_report\n",[461,3533,3534],{"class":463,"line":858},[461,3535,3536],{},"ON SCHEDULE EVERY 1 DAY\n",[461,3538,3539],{"class":463,"line":864},[461,3540,3541],{},"    STARTS '2025-01-01 00:00:00'\n",[461,3543,3544],{"class":463,"line":1018},[461,3545,3546],{},"    ENDS '2025-12-31 23:59:59'\n",[461,3548,3549],{"class":463,"line":1024},[461,3550,3402],{},[461,3552,3553],{"class":463,"line":1030},[461,3554,2123],{},[461,3556,3557],{"class":463,"line":1604},[461,3558,3559],{},"    INSERT INTO daily_stats (day, total_orders, revenue)\n",[461,3561,3562],{"class":463,"line":1610},[461,3563,3564],{},"    SELECT CURDATE(), COUNT(*), SUM(amount)\n",[461,3566,3567],{"class":463,"line":1616},[461,3568,2133],{},[461,3570,3571],{"class":463,"line":1622},[461,3572,3573],{},"    WHERE DATE(created_at) = CURDATE()\n",[461,3575,3576],{"class":463,"line":1627},[461,3577,3578],{},"    ON DUPLICATE KEY UPDATE\n",[461,3580,3581],{"class":463,"line":1632},[461,3582,3583],{},"        total_orders = VALUES(total_orders),\n",[461,3585,3586],{"class":463,"line":1638},[461,3587,3588],{},"        revenue = VALUES(revenue);\n",[461,3590,3591],{"class":463,"line":1644},[461,3592,2598],{},[461,3594,3595],{"class":463,"line":2429},[461,3596,680],{"emptyLinePlaceholder":679},[461,3598,3599],{"class":463,"line":2435},[461,3600,3601],{},"-- Bảo trì: OPTIMIZE mỗi tuần\n",[461,3603,3604],{"class":463,"line":2441},[461,3605,3606],{},"CREATE EVENT weekly_optimize\n",[461,3608,3609],{"class":463,"line":2446},[461,3610,3611],{},"ON SCHEDULE EVERY 1 WEEK\n",[461,3613,3614],{"class":463,"line":2452},[461,3615,3402],{},[461,3617,3618],{"class":463,"line":2458},[461,3619,3620],{},"    OPTIMIZE TABLE orders, order_items, users;\n",[432,3622,3624],{"id":3623},"_93-quản-lý-events","9.3 Quản lý Events",[451,3626,3628],{"className":542,"code":3627,"language":544,"meta":456,"style":456},"-- Liệt kê events\nSHOW EVENTS;\n\n-- Xem chi tiết\nSHOW CREATE EVENT daily_report;\n\n-- Tạm dừng \u002F kích hoạt\nALTER EVENT daily_report DISABLE;\nALTER EVENT daily_report ENABLE;\n\n-- Xóa\nDROP EVENT IF EXISTS daily_report;\n",[458,3629,3630,3635,3640,3644,3649,3654,3658,3663,3668,3673,3677,3682],{"__ignoreMap":456},[461,3631,3632],{"class":463,"line":464},[461,3633,3634],{},"-- Liệt kê events\n",[461,3636,3637],{"class":463,"line":470},[461,3638,3639],{},"SHOW EVENTS;\n",[461,3641,3642],{"class":463,"line":476},[461,3643,680],{"emptyLinePlaceholder":679},[461,3645,3646],{"class":463,"line":688},[461,3647,3648],{},"-- Xem chi tiết\n",[461,3650,3651],{"class":463,"line":694},[461,3652,3653],{},"SHOW CREATE EVENT daily_report;\n",[461,3655,3656],{"class":463,"line":700},[461,3657,680],{"emptyLinePlaceholder":679},[461,3659,3660],{"class":463,"line":706},[461,3661,3662],{},"-- Tạm dừng \u002F kích hoạt\n",[461,3664,3665],{"class":463,"line":712},[461,3666,3667],{},"ALTER EVENT daily_report DISABLE;\n",[461,3669,3670],{"class":463,"line":858},[461,3671,3672],{},"ALTER EVENT daily_report ENABLE;\n",[461,3674,3675],{"class":463,"line":864},[461,3676,680],{"emptyLinePlaceholder":679},[461,3678,3679],{"class":463,"line":1018},[461,3680,3681],{},"-- Xóa\n",[461,3683,3684],{"class":463,"line":1024},[461,3685,3686],{},"DROP EVENT IF EXISTS daily_report;\n",[437,3688,3689,3696],{"color":439,"icon":440},[442,3690,444,3691,576,3693],{},[446,3692,575],{},[578,3694,3695],{},"\"MySQL Event Scheduler dùng để làm gì? So với cron job ở OS level?\"",[582,3697,3698],{},[442,3699,3700,3701,3704,3705,3708],{},"\"MySQL Event dùng cho scheduled tasks chạy trong database — partition maintenance, archive cũ, refresh materialized view workaround, aggregate report. ",[446,3702,3703],{},"Ưu điểm:"," quản lý cùng schema (migration reproducible), không phụ thuộc OS, fail cùng database. ",[446,3706,3707],{},"Nhược điểm:"," chỉ chạy SQL, không gọi external API; nếu Event Scheduler tắt → task không chạy mà không có alert; khó monitor hơn cron + monitoring tool.\"",[427,3710,3712],{"id":3711},"_10-partitioning","10. Partitioning",[451,3714,3716],{"className":542,"code":3715,"language":544,"meta":456,"style":456},"-- RANGE partitioning theo year\nCREATE TABLE sales (\n    id BIGINT,\n    sold_at DATE,\n    amount DECIMAL(18, 2)\n)\nPARTITION BY RANGE (YEAR(sold_at)) (\n    PARTITION p2023 VALUES LESS THAN (2024),\n    PARTITION p2024 VALUES LESS THAN (2025),\n    PARTITION p2025 VALUES LESS THAN (2026),\n    PARTITION p_max VALUES LESS THAN MAXVALUE\n);\n\n-- HASH partitioning\nPARTITION BY HASH(user_id) PARTITIONS 8;\n\n-- LIST\nPARTITION BY LIST (region) (\n    PARTITION p_north VALUES IN ('HN', 'HP'),\n    PARTITION p_south VALUES IN ('HCM', 'CT')\n);\n",[458,3717,3718,3723,3728,3733,3738,3743,3747,3752,3757,3762,3767,3772,3776,3780,3785,3790,3794,3799,3804,3809,3814],{"__ignoreMap":456},[461,3719,3720],{"class":463,"line":464},[461,3721,3722],{},"-- RANGE partitioning theo year\n",[461,3724,3725],{"class":463,"line":470},[461,3726,3727],{},"CREATE TABLE sales (\n",[461,3729,3730],{"class":463,"line":476},[461,3731,3732],{},"    id BIGINT,\n",[461,3734,3735],{"class":463,"line":688},[461,3736,3737],{},"    sold_at DATE,\n",[461,3739,3740],{"class":463,"line":694},[461,3741,3742],{},"    amount DECIMAL(18, 2)\n",[461,3744,3745],{"class":463,"line":700},[461,3746,2118],{},[461,3748,3749],{"class":463,"line":706},[461,3750,3751],{},"PARTITION BY RANGE (YEAR(sold_at)) (\n",[461,3753,3754],{"class":463,"line":712},[461,3755,3756],{},"    PARTITION p2023 VALUES LESS THAN (2024),\n",[461,3758,3759],{"class":463,"line":858},[461,3760,3761],{},"    PARTITION p2024 VALUES LESS THAN (2025),\n",[461,3763,3764],{"class":463,"line":864},[461,3765,3766],{},"    PARTITION p2025 VALUES LESS THAN (2026),\n",[461,3768,3769],{"class":463,"line":1018},[461,3770,3771],{},"    PARTITION p_max VALUES LESS THAN MAXVALUE\n",[461,3773,3774],{"class":463,"line":1024},[461,3775,1001],{},[461,3777,3778],{"class":463,"line":1030},[461,3779,680],{"emptyLinePlaceholder":679},[461,3781,3782],{"class":463,"line":1604},[461,3783,3784],{},"-- HASH partitioning\n",[461,3786,3787],{"class":463,"line":1610},[461,3788,3789],{},"PARTITION BY HASH(user_id) PARTITIONS 8;\n",[461,3791,3792],{"class":463,"line":1616},[461,3793,680],{"emptyLinePlaceholder":679},[461,3795,3796],{"class":463,"line":1622},[461,3797,3798],{},"-- LIST\n",[461,3800,3801],{"class":463,"line":1627},[461,3802,3803],{},"PARTITION BY LIST (region) (\n",[461,3805,3806],{"class":463,"line":1632},[461,3807,3808],{},"    PARTITION p_north VALUES IN ('HN', 'HP'),\n",[461,3810,3811],{"class":463,"line":1638},[461,3812,3813],{},"    PARTITION p_south VALUES IN ('HCM', 'CT')\n",[461,3815,3816],{"class":463,"line":1644},[461,3817,1001],{},[437,3819,3820,3825,3836],{"color":570,"icon":1465},[442,3821,3822],{},[446,3823,3824],{},"Khi nào dùng partitioning?",[488,3826,3827,3830,3833],{},[491,3828,3829],{},"Bảng cực lớn (> 100M row).",[491,3831,3832],{},"Archive theo thời gian (drop partition cũ thay vì DELETE).",[491,3834,3835],{},"Query luôn filter theo partition key (partition pruning).",[442,3837,3838,3841],{},[446,3839,3840],{},"Không phải bừa bãi áp dụng."," Nếu query không filter partition key → partition gây chậm.",[427,3843,3845],{"id":3844},"_11-replication-source-replica","11. Replication — Source \u002F Replica",[432,3847,3849],{"id":3848},"async-replication-default","Async replication (default)",[451,3851,3854],{"className":3852,"code":3853,"language":637},[635],"Source                      Replica\n  ↓ write binlog              ↓ read binlog\n  COMMIT (không chờ)          apply\n",[458,3855,3853],{"__ignoreMap":456},[442,3857,3858],{},"Cấu hình ngắn gọn:",[451,3860,3862],{"className":453,"code":3861,"language":455,"meta":456,"style":456},"# Source\n[mysqld]\nserver-id = 1\nlog-bin = mysql-bin\nbinlog-format = ROW              # ROW > STATEMENT > MIXED\n",[458,3863,3864,3869,3873,3878,3883],{"__ignoreMap":456},[461,3865,3866],{"class":463,"line":464},[461,3867,3868],{},"# Source\n",[461,3870,3871],{"class":463,"line":470},[461,3872,467],{},[461,3874,3875],{"class":463,"line":476},[461,3876,3877],{},"server-id = 1\n",[461,3879,3880],{"class":463,"line":688},[461,3881,3882],{},"log-bin = mysql-bin\n",[461,3884,3885],{"class":463,"line":694},[461,3886,3887],{},"binlog-format = ROW              # ROW > STATEMENT > MIXED\n",[451,3889,3891],{"className":542,"code":3890,"language":544,"meta":456,"style":456},"-- Source: tạo user replication\nCREATE USER 'repl'@'%' IDENTIFIED BY 'replpwd';\nGRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';\n\n-- Replica: setup\nCHANGE REPLICATION SOURCE TO\n    SOURCE_HOST='source.example.com',\n    SOURCE_USER='repl',\n    SOURCE_PASSWORD='replpwd',\n    SOURCE_LOG_FILE='mysql-bin.000001',\n    SOURCE_LOG_POS=0;\nSTART REPLICA;\nSHOW REPLICA STATUS\\G\n",[458,3892,3893,3898,3903,3908,3912,3917,3922,3927,3932,3937,3942,3947,3952],{"__ignoreMap":456},[461,3894,3895],{"class":463,"line":464},[461,3896,3897],{},"-- Source: tạo user replication\n",[461,3899,3900],{"class":463,"line":470},[461,3901,3902],{},"CREATE USER 'repl'@'%' IDENTIFIED BY 'replpwd';\n",[461,3904,3905],{"class":463,"line":476},[461,3906,3907],{},"GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';\n",[461,3909,3910],{"class":463,"line":688},[461,3911,680],{"emptyLinePlaceholder":679},[461,3913,3914],{"class":463,"line":694},[461,3915,3916],{},"-- Replica: setup\n",[461,3918,3919],{"class":463,"line":700},[461,3920,3921],{},"CHANGE REPLICATION SOURCE TO\n",[461,3923,3924],{"class":463,"line":706},[461,3925,3926],{},"    SOURCE_HOST='source.example.com',\n",[461,3928,3929],{"class":463,"line":712},[461,3930,3931],{},"    SOURCE_USER='repl',\n",[461,3933,3934],{"class":463,"line":858},[461,3935,3936],{},"    SOURCE_PASSWORD='replpwd',\n",[461,3938,3939],{"class":463,"line":864},[461,3940,3941],{},"    SOURCE_LOG_FILE='mysql-bin.000001',\n",[461,3943,3944],{"class":463,"line":1018},[461,3945,3946],{},"    SOURCE_LOG_POS=0;\n",[461,3948,3949],{"class":463,"line":1024},[461,3950,3951],{},"START REPLICA;\n",[461,3953,3954],{"class":463,"line":1030},[461,3955,3956],{},"SHOW REPLICA STATUS\\G\n",[437,3958,444,3959,3962,3963,3966,3967,3970,3971,3966,3974,3977],{"color":439,"icon":440},[446,3960,3961],{},"8.4 terminology:"," dùng ",[458,3964,3965],{},"SOURCE","\u002F",[458,3968,3969],{},"REPLICA"," thay cho ",[458,3972,3973],{},"MASTER",[458,3975,3976],{},"SLAVE"," cũ. Old commands deprecated nhưng vẫn chạy.",[432,3979,3981],{"id":3980},"semi-sync-và-group-replication","Semi-sync và Group Replication",[488,3983,3984,3990],{},[491,3985,3986,3989],{},[446,3987,3988],{},"Semi-sync",": source chờ ít nhất 1 replica ack trước khi return COMMIT.",[491,3991,3992,3995],{},[446,3993,3994],{},"Group Replication"," (InnoDB Cluster): multi-master, Paxos consensus, auto-failover.",[427,3997,3999],{"id":3998},"_12-query-optimization-checklist","12. Query optimization checklist",[437,4001,4002],{"color":718,"icon":383},[4003,4004,4005,4016,4019,4022,4029,4032,4035,4042,4045,4048],"ol",{},[491,4006,4007,4008,4011,4012,4015],{},"✅ EXPLAIN xem ",[458,4009,4010],{},"type"," — tránh ",[458,4013,4014],{},"ALL"," (full scan).",[491,4017,4018],{},"✅ Composite index theo left-most prefix.",[491,4020,4021],{},"✅ Covering index khi cần (vì InnoDB secondary phải lookup PK).",[491,4023,4024,4025,4028],{},"✅ Tránh ",[458,4026,4027],{},"SELECT *"," — phá covering.",[491,4030,4031],{},"✅ WHERE sargable — không function lên cột (hoặc dùng functional index).",[491,4033,4034],{},"✅ Index trên FK column (không tự tạo, MyISAM thì không tạo được, InnoDB tạo manual).",[491,4036,4037,4038,4041],{},"✅ ",[458,4039,4040],{},"LIMIT"," để dừng sớm.",[491,4043,4044],{},"✅ Buffer pool đủ lớn (50–70% RAM).",[491,4046,4047],{},"✅ Slow query log để bắt query chậm.",[491,4049,4050,4051,4054],{},"✅ Pagination keyset (",[458,4052,4053],{},"WHERE id > last_id",") thay OFFSET deep.",[427,4056,4058],{"id":4057},"_13-slow-query-log","13. Slow query log",[451,4060,4062],{"className":453,"code":4061,"language":455,"meta":456,"style":456},"[mysqld]\nslow_query_log = 1\nslow_query_log_file = \u002Fvar\u002Flog\u002Fmysql\u002Fslow.log\nlong_query_time = 1            # > 1 giây\nlog_queries_not_using_indexes = 1\n",[458,4063,4064,4068,4073,4078,4083],{"__ignoreMap":456},[461,4065,4066],{"class":463,"line":464},[461,4067,467],{},[461,4069,4070],{"class":463,"line":470},[461,4071,4072],{},"slow_query_log = 1\n",[461,4074,4075],{"class":463,"line":476},[461,4076,4077],{},"slow_query_log_file = \u002Fvar\u002Flog\u002Fmysql\u002Fslow.log\n",[461,4079,4080],{"class":463,"line":688},[461,4081,4082],{},"long_query_time = 1            # > 1 giây\n",[461,4084,4085],{"class":463,"line":694},[461,4086,4087],{},"log_queries_not_using_indexes = 1\n",[451,4089,4093],{"className":4090,"code":4091,"language":4092,"meta":456,"style":456},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","# Phân tích\nmysqldumpslow -s t \u002Fvar\u002Flog\u002Fmysql\u002Fslow.log | head -20\n\n# Hoặc dùng pt-query-digest (Percona Toolkit)\npt-query-digest slow.log\n","bash",[458,4094,4095,4101,4127,4131,4136],{"__ignoreMap":456},[461,4096,4097],{"class":463,"line":464},[461,4098,4100],{"class":4099},"sHwdD","# Phân tích\n",[461,4102,4103,4107,4111,4114,4117,4121,4124],{"class":463,"line":470},[461,4104,4106],{"class":4105},"sBMFI","mysqldumpslow",[461,4108,4110],{"class":4109},"sfazB"," -s",[461,4112,4113],{"class":4109}," t",[461,4115,4116],{"class":4109}," \u002Fvar\u002Flog\u002Fmysql\u002Fslow.log",[461,4118,4120],{"class":4119},"sMK4o"," |",[461,4122,4123],{"class":4105}," head",[461,4125,4126],{"class":4109}," -20\n",[461,4128,4129],{"class":463,"line":476},[461,4130,680],{"emptyLinePlaceholder":679},[461,4132,4133],{"class":463,"line":688},[461,4134,4135],{"class":4099},"# Hoặc dùng pt-query-digest (Percona Toolkit)\n",[461,4137,4138,4141],{"class":463,"line":694},[461,4139,4140],{"class":4105},"pt-query-digest",[461,4142,4143],{"class":4109}," slow.log\n",[427,4145,4147],{"id":4146},"_14-performance-schema-sys-schema","14. Performance schema & sys schema",[451,4149,4151],{"className":542,"code":4150,"language":544,"meta":456,"style":456},"-- Top query theo time\nSELECT * FROM performance_schema.events_statements_summary_by_digest\nORDER BY sum_timer_wait DESC LIMIT 10;\n\n-- Sys schema — view trên performance_schema\nSELECT * FROM sys.statement_analysis ORDER BY total_latency DESC LIMIT 10;\n\n-- Index không dùng (sau khi DB chạy 1 thời gian)\nSELECT * FROM sys.schema_unused_indexes;\n",[458,4152,4153,4158,4163,4168,4172,4177,4182,4186,4191],{"__ignoreMap":456},[461,4154,4155],{"class":463,"line":464},[461,4156,4157],{},"-- Top query theo time\n",[461,4159,4160],{"class":463,"line":470},[461,4161,4162],{},"SELECT * FROM performance_schema.events_statements_summary_by_digest\n",[461,4164,4165],{"class":463,"line":476},[461,4166,4167],{},"ORDER BY sum_timer_wait DESC LIMIT 10;\n",[461,4169,4170],{"class":463,"line":688},[461,4171,680],{"emptyLinePlaceholder":679},[461,4173,4174],{"class":463,"line":694},[461,4175,4176],{},"-- Sys schema — view trên performance_schema\n",[461,4178,4179],{"class":463,"line":700},[461,4180,4181],{},"SELECT * FROM sys.statement_analysis ORDER BY total_latency DESC LIMIT 10;\n",[461,4183,4184],{"class":463,"line":706},[461,4185,680],{"emptyLinePlaceholder":679},[461,4187,4188],{"class":463,"line":712},[461,4189,4190],{},"-- Index không dùng (sau khi DB chạy 1 thời gian)\n",[461,4192,4193],{"class":463,"line":858},[461,4194,4195],{},"SELECT * FROM sys.schema_unused_indexes;\n",[427,4197,4199],{"id":4198},"_15-backup-restore","15. Backup & restore",[432,4201,4203],{"id":4202},"logical-backup-mysqldump","Logical backup — mysqldump",[451,4205,4207],{"className":4090,"code":4206,"language":4092,"meta":456,"style":456},"# Single DB\nmysqldump -u root -p --single-transaction --routines --triggers learnmysql > dump.sql\n\n# All DBs\nmysqldump -u root -p --all-databases --single-transaction > all.sql\n\n# Restore\nmysql -u root -p learnmysql \u003C dump.sql\n",[458,4208,4209,4214,4246,4250,4255,4275,4279,4284],{"__ignoreMap":456},[461,4210,4211],{"class":463,"line":464},[461,4212,4213],{"class":4099},"# Single DB\n",[461,4215,4216,4219,4222,4225,4228,4231,4234,4237,4240,4243],{"class":463,"line":470},[461,4217,4218],{"class":4105},"mysqldump",[461,4220,4221],{"class":4109}," -u",[461,4223,4224],{"class":4109}," root",[461,4226,4227],{"class":4109}," -p",[461,4229,4230],{"class":4109}," --single-transaction",[461,4232,4233],{"class":4109}," --routines",[461,4235,4236],{"class":4109}," --triggers",[461,4238,4239],{"class":4109}," learnmysql",[461,4241,4242],{"class":4119}," >",[461,4244,4245],{"class":4109}," dump.sql\n",[461,4247,4248],{"class":463,"line":476},[461,4249,680],{"emptyLinePlaceholder":679},[461,4251,4252],{"class":463,"line":688},[461,4253,4254],{"class":4099},"# All DBs\n",[461,4256,4257,4259,4261,4263,4265,4268,4270,4272],{"class":463,"line":694},[461,4258,4218],{"class":4105},[461,4260,4221],{"class":4109},[461,4262,4224],{"class":4109},[461,4264,4227],{"class":4109},[461,4266,4267],{"class":4109}," --all-databases",[461,4269,4230],{"class":4109},[461,4271,4242],{"class":4119},[461,4273,4274],{"class":4109}," all.sql\n",[461,4276,4277],{"class":463,"line":700},[461,4278,680],{"emptyLinePlaceholder":679},[461,4280,4281],{"class":463,"line":706},[461,4282,4283],{"class":4099},"# Restore\n",[461,4285,4286,4289,4291,4293,4295,4297,4300],{"class":463,"line":712},[461,4287,4288],{"class":4105},"mysql",[461,4290,4221],{"class":4109},[461,4292,4224],{"class":4109},[461,4294,4227],{"class":4109},[461,4296,4239],{"class":4109},[461,4298,4299],{"class":4119}," \u003C",[461,4301,4245],{"class":4109},[442,4303,4304,4307],{},[458,4305,4306],{},"--single-transaction"," cho consistent snapshot (chỉ InnoDB) — không lock bảng.",[432,4309,4311],{"id":4310},"physical-backup-mysqlbackup-percona-xtrabackup","Physical backup — mysqlbackup \u002F Percona XtraBackup",[442,4313,4314],{},"Faster cho DB lớn. XtraBackup hot backup không downtime.",[427,4316,4318],{"id":4317},"_16-mysql-97-cải-tiến-innodb","16. MySQL 9.7 — Cải tiến InnoDB",[437,4320,4321],{"color":570,"icon":268},"MySQL 9.7 (Innovation Release) mang đến một số cải tiến InnoDB đáng chú ý cho production workloads:",[432,4323,4325],{"id":4324},"cpuset-cgroup-support","cpuset cgroup support",[442,4327,4328,4329,4332,4333,4336],{},"InnoDB giờ tự động phát hiện giới hạn CPU trong cgroup (container\u002FDocker\u002FK8s). Trước đây, ",[458,4330,4331],{},"innodb_buffer_pool_instances"," và các internal thread count dựa trên tổng CPU của host → sai trong container. Từ 9.7, InnoDB đọc ",[458,4334,4335],{},"cpuset.cpus.effective"," từ cgroup fs để xác định chính xác số CPU được cấp.",[451,4338,4340],{"className":453,"code":4339,"language":455,"meta":456,"style":456},"# Từ 9.7, có thể bỏ qua các setting manual này nếu chạy trong container:\n# innodb_buffer_pool_instances sẽ auto-detected từ cgroup CPU limit\n",[458,4341,4342,4347],{"__ignoreMap":456},[461,4343,4344],{"class":463,"line":464},[461,4345,4346],{},"# Từ 9.7, có thể bỏ qua các setting manual này nếu chạy trong container:\n",[461,4348,4349],{"class":463,"line":470},[461,4350,4351],{},"# innodb_buffer_pool_instances sẽ auto-detected từ cgroup CPU limit\n",[432,4353,4355],{"id":4354},"fts-index-memory-optimization","FTS index memory optimization",[442,4357,4358,4359,4362],{},"Full-Text Search index memory usage được optimize — giảm đáng kể RAM cần cho ",[458,4360,4361],{},"innodb_ft_cache_size"," khi có nhiều FULLTEXT index. Quan trọng cho ứng dụng tiếng Việt dùng ngram (token nhiều hơn → cache lớn hơn).",[432,4364,4366],{"id":4365},"parallel-reader-fixes","Parallel reader fixes",[442,4368,4369,4370,4373],{},"Parallel read (từ MySQL 8.0.17) được sửa lỗi — ",[458,4371,4372],{},"innodb_parallel_read_threads"," giờ ổn định hơn với CHECK TABLE, COUNT(*), và một số DDL operations. Trước 9.7, parallel reader có thể gây crash với degraded index page.",[432,4375,4377],{"id":4376},"innodb-dedicated-server-auto-config","InnoDB dedicated server auto-config",[442,4379,4380,4383],{},[458,4381,4382],{},"innodb_dedicated_server"," (từ 8.0) được cải thiện — tự động detect thêm các tham số:",[1653,4385,4386,4396],{},[1656,4387,4388],{},[1659,4389,4390,4393],{},[1662,4391,4392],{},"Tham số",[1662,4394,4395],{},"Auto-configure",[1672,4397,4398,4408,4418,4428],{},[1659,4399,4400,4405],{},[1677,4401,4402],{},[458,4403,4404],{},"innodb_buffer_pool_size",[1677,4406,4407],{},"50-75% RAM",[1659,4409,4410,4415],{},[1677,4411,4412],{},[458,4413,4414],{},"innodb_log_file_size",[1677,4416,4417],{},"Dựa trên buffer pool size",[1659,4419,4420,4425],{},[1677,4421,4422],{},[458,4423,4424],{},"innodb_flush_method",[1677,4426,4427],{},"Tối ưu cho OS filesystem",[1659,4429,4430,4435],{},[1677,4431,4432],{},[458,4433,4434],{},"innodb_redo_log_capacity",[1677,4436,4437,4438],{},"Từ 9.7 — thay cho ",[458,4439,4414],{},[451,4441,4443],{"className":453,"code":4442,"language":455,"meta":456,"style":456},"[mysqld]\ninnodb_dedicated_server = ON\n# Từ 9.7, tự động detect cả redo log capacity phù hợp\n",[458,4444,4445,4449,4454],{"__ignoreMap":456},[461,4446,4447],{"class":463,"line":464},[461,4448,467],{},[461,4450,4451],{"class":463,"line":470},[461,4452,4453],{},"innodb_dedicated_server = ON\n",[461,4455,4456],{"class":463,"line":476},[461,4457,4458],{},"# Từ 9.7, tự động detect cả redo log capacity phù hợp\n",[427,4460,4462],{"id":4461},"_17-mysql-đặc-thù-khác-postgressql-server","17. MySQL đặc thù — khác Postgres\u002FSQL Server",[1653,4464,4465,4478],{},[1656,4466,4467],{},[1659,4468,4469,4471,4473,4476],{},[1662,4470],{},[1662,4472,223],{},[1662,4474,4475],{},"Postgres",[1662,4477,168],{},[1672,4479,4480,4495,4510,4531,4553,4576,4596,4609,4621,4632,4646,4660,4673],{},[1659,4481,4482,4485,4490,4493],{},[1677,4483,4484],{},"Default isolation",[1677,4486,4487],{},[446,4488,4489],{},"REPEATABLE READ",[1677,4491,4492],{},"READ COMMITTED",[1677,4494,4492],{},[1659,4496,4497,4500,4505,4508],{},[1677,4498,4499],{},"Schema vs DB",[1677,4501,4502],{},[446,4503,4504],{},"DB = schema",[1677,4506,4507],{},"DB chứa nhiều schema",[1677,4509,4507],{},[1659,4511,4512,4515,4521,4526],{},[1677,4513,4514],{},"Identifier",[1677,4516,4517,4518],{},"backtick ",[458,4519,4520],{},"`Name`",[1677,4522,4523],{},[458,4524,4525],{},"\"Name\"",[1677,4527,4528],{},[458,4529,4530],{},"[Name]",[1659,4532,4533,4536,4542,4548],{},[1677,4534,4535],{},"Boolean",[1677,4537,4538,4541],{},[458,4539,4540],{},"TINYINT(1)"," 0\u002F1",[1677,4543,4544,4547],{},[458,4545,4546],{},"BOOLEAN"," thật",[1677,4549,4550,4541],{},[458,4551,4552],{},"BIT",[1659,4554,4555,4558,4563,4571],{},[1677,4556,4557],{},"Auto-id",[1677,4559,4560],{},[458,4561,4562],{},"AUTO_INCREMENT",[1677,4564,4565,1682,4568],{},[458,4566,4567],{},"GENERATED AS IDENTITY",[458,4569,4570],{},"SERIAL",[1677,4572,4573],{},[458,4574,4575],{},"IDENTITY",[1659,4577,4578,4581,4586,4591],{},[1677,4579,4580],{},"UPSERT",[1677,4582,4583],{},[458,4584,4585],{},"ON DUPLICATE KEY UPDATE",[1677,4587,4588],{},[458,4589,4590],{},"ON CONFLICT DO UPDATE",[1677,4592,4593],{},[458,4594,4595],{},"MERGE",[1659,4597,4598,4601,4604,4607],{},[1677,4599,4600],{},"FULL OUTER JOIN",[1677,4602,4603],{},"❌ (UNION mô phỏng)",[1677,4605,4606],{},"✅",[1677,4608,4606],{},[1659,4610,4611,4614,4617,4619],{},[1677,4612,4613],{},"Window function",[1677,4615,4616],{},"✅ (từ 8.0)",[1677,4618,4606],{},[1677,4620,4606],{},[1659,4622,4623,4626,4628,4630],{},[1677,4624,4625],{},"Recursive CTE",[1677,4627,4616],{},[1677,4629,4606],{},[1677,4631,4606],{},[1659,4633,4634,4637,4640,4643],{},[1677,4635,4636],{},"Array type",[1677,4638,4639],{},"❌ (JSON thay)",[1677,4641,4642],{},"✅ native",[1677,4644,4645],{},"❌",[1659,4647,4648,4651,4654,4657],{},[1677,4649,4650],{},"Procedure language",[1677,4652,4653],{},"SQL stored proc",[1677,4655,4656],{},"PL\u002FpgSQL + nhiều",[1677,4658,4659],{},"T-SQL",[1659,4661,4662,4665,4668,4671],{},[1677,4663,4664],{},"Default storage",[1677,4666,4667],{},"InnoDB",[1677,4669,4670],{},"–",[1677,4672,4670],{},[1659,4674,4675,4678,4681,4684],{},[1677,4676,4677],{},"utf8 vs utf8mb4",[1677,4679,4680],{},"gotcha lớn",[1677,4682,4683],{},"UTF-8 luôn",[1677,4685,4686],{},"NVARCHAR Unicode",[437,4688,4690,4691],{"color":570,"icon":4689},"i-lucide-arrow-right","Tiếp theo: ",[4692,4693,4694],"a",{"href":241},"Câu hỏi phỏng vấn MySQL",[4696,4697,4698],"style",{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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 .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}",{"title":456,"searchDepth":470,"depth":470,"links":4700},[4701,4708,4709,4720,4726,4733,4740,4745,4751,4756,4757,4761,4762,4763,4764,4768,4774],{"id":429,"depth":470,"text":430,"children":4702},[4703,4704,4705,4706,4707],{"id":434,"depth":476,"text":435},{"id":485,"depth":476,"text":486},{"id":509,"depth":476,"text":510},{"id":516,"depth":476,"text":517},{"id":535,"depth":476,"text":536},{"id":559,"depth":470,"text":560},{"id":593,"depth":470,"text":594,"children":4710},[4711,4712,4713,4714,4715,4716,4717,4718,4719],{"id":597,"depth":476,"text":598},{"id":656,"depth":476,"text":657},{"id":753,"depth":476,"text":754},{"id":807,"depth":476,"text":808},{"id":878,"depth":476,"text":879},{"id":957,"depth":476,"text":958},{"id":1036,"depth":476,"text":1037},{"id":1086,"depth":476,"text":1087},{"id":1121,"depth":476,"text":1122},{"id":1143,"depth":470,"text":885,"children":4721},[4722,4723,4724,4725],{"id":1146,"depth":476,"text":1147},{"id":1202,"depth":476,"text":1203},{"id":1405,"depth":476,"text":1406},{"id":1501,"depth":476,"text":1502},{"id":1527,"depth":470,"text":1528,"children":4727},[4728,4729,4730,4731,4732],{"id":1534,"depth":476,"text":1535},{"id":1650,"depth":476,"text":1651},{"id":1831,"depth":476,"text":1832},{"id":1908,"depth":476,"text":1909},{"id":1979,"depth":476,"text":1980},{"id":2079,"depth":470,"text":2080,"children":4734},[4735,4736,4737,4738,4739],{"id":2083,"depth":476,"text":2084},{"id":2184,"depth":476,"text":2185},{"id":2323,"depth":476,"text":2324},{"id":2518,"depth":476,"text":2519},{"id":2644,"depth":476,"text":2645},{"id":2733,"depth":470,"text":2734,"children":4741},[4742,4743,4744],{"id":2737,"depth":476,"text":2738},{"id":2771,"depth":476,"text":2772},{"id":2907,"depth":476,"text":2908},{"id":3124,"depth":470,"text":3125,"children":4746},[4747,4748,4749,4750],{"id":3128,"depth":476,"text":3129},{"id":3204,"depth":476,"text":3205},{"id":3264,"depth":476,"text":3265},{"id":3344,"depth":476,"text":3345},{"id":3428,"depth":470,"text":3429,"children":4752},[4753,4754,4755],{"id":3435,"depth":476,"text":3436},{"id":3487,"depth":476,"text":3488},{"id":3623,"depth":476,"text":3624},{"id":3711,"depth":470,"text":3712},{"id":3844,"depth":470,"text":3845,"children":4758},[4759,4760],{"id":3848,"depth":476,"text":3849},{"id":3980,"depth":476,"text":3981},{"id":3998,"depth":470,"text":3999},{"id":4057,"depth":470,"text":4058},{"id":4146,"depth":470,"text":4147},{"id":4198,"depth":470,"text":4199,"children":4765},[4766,4767],{"id":4202,"depth":476,"text":4203},{"id":4310,"depth":476,"text":4311},{"id":4317,"depth":470,"text":4318,"children":4769},[4770,4771,4772,4773],{"id":4324,"depth":476,"text":4325},{"id":4354,"depth":476,"text":4355},{"id":4365,"depth":476,"text":4366},{"id":4376,"depth":476,"text":4377},{"id":4461,"depth":470,"text":4462},"InnoDB internals, buffer pool, replication source\u002Freplica, partitioning, performance tuning cho MySQL 8.4\u002F9.x.","md",null,{},{"title":188,"icon":191},{"title":422,"description":4775},"-ZIEAd5isgN0b2L2xuWiKl7PSTI7A1UmwYOHcYysvCg",[4783,4785],{"title":183,"path":235,"stem":236,"description":4784,"icon":186,"children":-1},"JOIN, subquery, derived tables & LATERAL (8.0.14+), set operations, CTE recursive, window function (8.0+), regular expressions, JSON query, transaction, InnoDB locking, EXPLAIN, Hypergraph Optimizer (9.7), index.",{"title":193,"path":241,"stem":242,"description":4786,"icon":196,"children":-1},"65+ câu hỏi phỏng vấn MySQL cho dev backend \u002F fullstack — InnoDB, gap lock, replication, optimizer, MySQL 8.4 & 9.7 LTS.",1783490367014]