[{"content":"使用 语法：alloftext(predicate, \u0026quot;space-separated text\u0026quot;) and anyoftext(predicate, \u0026quot;space-separated text\u0026quot;)\nalloftext 只有包含所有token的节点会被返回（取交集 Intersect）。\nanyoftext 返回包含任意token的节点。\n应用带有词干和停用词的全文搜索来查找与所有或任何给定文本匹配的字符串。\n实现 DGraph 使用 bleve 进行全文搜索索引。\nTokenizer 分词部分的代码在tok包。\n核心的接口：Tokenizer\n// Tokenizer defines what a tokenizer must provide. type Tokenizer interface { // 分词器的名称.不允许重复. Name() string // Type returns the string representation of the typeID that we care about. Type() string // Tokens return tokens for a given value. The tokens shouldn\u0026#39;t be encoded // with the byte identifier. Tokens(interface{}) ([]string, error) // Identifier returns the prefix byte for this token type. This should be // unique. The range 0x80 to 0xff (inclusive) is reserved for user-provided // custom tokenizers. Identifier() byte // IsSortable returns true if the tokenizer can be used for sorting/ordering. IsSortable() bool // IsLossy returns true if we don\u0026#39;t store the values directly as index keys // during tokenization. If a predicate is tokenized using an IsLossy() tokenizer, // then we need to fetch the actual value and compare. IsLossy() bool } FullTextTokenizer FullTextTokenizer 类实现了全文检索的分词器。\ntype FullTextTokenizer struct{ lang string } FullTextTokenizer 采用cjk-bigram 对中文TokenStream进行词干提取。\ncjk-bigram\ncjk-bigram是一种用于处理中文、日文和韩文（CJK即Chinese, Japanese, Korean的缩写）文本的方法，尤其在信息检索和文本分析中应用广泛。尽管称其为\u0026quot;stemmers filter\u0026quot;可能有些误导，因为它并不直接涉及到传统意义上的\u0026quot;词干提取\u0026quot;（stemming），而更多是关于文本的分词和索引优化。\nBigram 分词： 在处理中文时，cjk-bigram方法主要采用bigram（二元组）方式来分析文本。Bigram是指连续的两个字符或两个词的组合。例如，对于句子\u0026quot;我们学习\u0026quot;，bigram分割将产生\u0026quot;我们\u0026quot;和\u0026quot;学习\u0026quot;（如果按词分割），或\u0026quot;我 们\u0026quot;、\u0026ldquo;们 学\u0026rdquo;、\u0026ldquo;学 习\u0026rdquo;（如果按字符分割）。 索引构建： 在信息检索系统中，使用bigram分割可以帮助构建更为细致的索引，使得搜索引擎可以在没有明确分词界限的情况下，更有效地匹配文本。这种方法特别适用于中文这样的语言，因为中文没有明显的单词界限（如空格分隔）。 提高匹配灵活性： 使用bigram可以增加文本匹配的灵活性。例如，搜索\u0026quot;学习\u0026quot;时，即使文本中没有精确的\u0026quot;学习\u0026quot;词汇，但只要有连续的\u0026quot;学\u0026quot;和\u0026quot;习\u0026quot;出现，系统也可能将其视为相关文本。 处理效率： Bigram方法虽然可以增加处理的复杂度（因为生成的索引更大），但它提供了一种相对简单直接的方式来处理中文文本，特别是在缺乏精确分词工具的情况下。 总体流程：\n核心代码：\nconst unicodenormName = \u0026#34;unicodenorm_nfkc\u0026#34; func setupBleve{ ... // fulltext analyzer - does language stop-words removal and stemming. fulltextAnalyzer, err = bleveCache.DefineAnalyzer(\u0026#34;fulltext\u0026#34;, map[string]interface{}{ \u0026#34;type\u0026#34;: custom.Name, \u0026#34;tokenizer\u0026#34;: unicode.Name, // splits on word boundaries. \u0026#34;token_filters\u0026#34;: []string{ lowercase.Name, // lowercase unicodenormName, // normalize tokens by unicodenorm_nfkc. }, }) ... } func (t FullTextTokenizer) Tokens(v interface{}) ([]string, error) { str, ok := v.(string) if !ok || str == \u0026#34;\u0026#34; { return []string{}, nil } lang := LangBase(t.lang) // pass 1 - lowercase and normalize input 转换为小写并且使用Unicode 标准来分割文本. tokens := fulltextAnalyzer.Analyze([]byte(str)) // pass 2 - filter stop words tokens = filterStopwords(lang, tokens) // pass 3 - filter stems tokens = filterStemmers(lang, tokens) // finally, return the terms. return uniqueTerms(tokens), nil } fulltextAnalyzer代表一个bleve里面的分析器，它的功能为将字符串转换为小写并且使用Unicode 标准来分割文本。\nfilterStemmers(lang, tokens) 内部通过语言和filter类型的映射选择语言对应的filter进行词干分析。\n索引的存储 DGraph 索引的存储结构与值一致，都是通过PostingList 数据结构存储。区别是key不同，数据的key为\u0026lt;predicate, uid\u0026gt;, 而索引的key为\u0026lt;predicate, token\u0026gt;。Token 即上文中介绍的通过分词器 Tokenizer处理谓词值生成的。\nmessage PostingList { UidPack pack = 1; repeated Posting postings = 2; int64 commit_ts = 3; repeated uint64 splits = 4; } 例子：\n以NQuad: \u0026lt;0xa\u0026gt; \u0026ldquo;running fast\u0026rdquo; . 为例，key1的schema类型为 fulltext，通过fulltext 分词器处理后，会生成两个token：\n\u0026ldquo;run\u0026rdquo;, \u0026ldquo;fast\u0026rdquo;。\n根据这两个token，dgraph将其组织为两条边(DirectedEdge)：\n\u0026lt;key, run\u0026gt; -\u0026gt; [0xa]\n\u0026lt;key, fast\u0026gt; -\u0026gt; [0xa]\n这样，值\u0026quot;run fast\u0026quot; 的索引就转换为后端存储的KVs。\n","permalink":"https://catundercar.github.io/2024/04/14/dgraph-%E5%85%A8%E6%96%87%E6%A3%80%E7%B4%A2/","summary":"\u003ch1 id=\"使用\"\u003e使用\u003c/h1\u003e\n\u003cp\u003e语法：\u003ccode\u003ealloftext(predicate, \u0026quot;space-separated text\u0026quot;)\u003c/code\u003e and \u003ccode\u003eanyoftext(predicate, \u0026quot;space-separated text\u0026quot;)\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003ealloftext\u003c/code\u003e 只有包含所有\u003ccode\u003etoken\u003c/code\u003e的节点会被返回（取交集 Intersect）。\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eanyoftext\u003c/code\u003e 返回包含任意\u003ccode\u003etoken\u003c/code\u003e的节点。\u003c/p\u003e\n\u003cp\u003e应用带有词干和停用词的全文搜索来查找与所有或任何给定文本匹配的字符串。\u003c/p\u003e\n\u003ch1 id=\"实现\"\u003e实现\u003c/h1\u003e\n\u003cp\u003eDGraph 使用 \u003ca href=\"https://github.com/blevesearch/bleve\"\u003ebleve\u003c/a\u003e 进行全文搜索索引。\u003c/p\u003e\n\u003ch2 id=\"tokenizer\"\u003eTokenizer\u003c/h2\u003e\n\u003cp\u003e分词部分的代码在\u003ccode\u003etok\u003c/code\u003e包。\u003c/p\u003e\n\u003cp\u003e核心的接口：\u003ccode\u003eTokenizer\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-go\" data-lang=\"go\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Tokenizer defines what a tokenizer must provide.\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003etype\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003eTokenizer\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kd\"\u003einterface\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\t\u003c/span\u003e\u003cspan class=\"c1\"\u003e// 分词器的名称.不允许重复.\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\t\u003c/span\u003e\u003cspan class=\"nf\"\u003eName\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\t\u003c/span\u003e\u003cspan class=\"c1\"\u003e// Type returns the string representation of the typeID that we care about.\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\t\u003c/span\u003e\u003cspan class=\"nf\"\u003eType\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\t\u003c/span\u003e\u003cspan class=\"c1\"\u003e// Tokens return tokens for a given value. The tokens shouldn\u0026#39;t be encoded\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\t\u003c/span\u003e\u003cspan class=\"c1\"\u003e// with the byte identifier.\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\t\u003c/span\u003e\u003cspan class=\"nf\"\u003eTokens\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kd\"\u003einterface\u003c/span\u003e\u003cspan class=\"p\"\u003e{})\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e([]\u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003eerror\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\t\u003c/span\u003e\u003cspan class=\"c1\"\u003e// Identifier returns the prefix byte for this token type. This should be\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\t\u003c/span\u003e\u003cspan class=\"c1\"\u003e// unique. The range 0x80 to 0xff (inclusive) is reserved for user-provided\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\t\u003c/span\u003e\u003cspan class=\"c1\"\u003e// custom tokenizers.\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\t\u003c/span\u003e\u003cspan class=\"nf\"\u003eIdentifier\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003ebyte\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\t\u003c/span\u003e\u003cspan class=\"c1\"\u003e// IsSortable returns true if the tokenizer can be used for sorting/ordering.\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\t\u003c/span\u003e\u003cspan class=\"nf\"\u003eIsSortable\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003ebool\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\t\u003c/span\u003e\u003cspan class=\"c1\"\u003e// IsLossy returns true if we don\u0026#39;t store the values directly as index keys\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\t\u003c/span\u003e\u003cspan class=\"c1\"\u003e// during tokenization. If a predicate is tokenized using an IsLossy() tokenizer,\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\t\u003c/span\u003e\u003cspan class=\"c1\"\u003e// then we need to fetch the actual value and compare.\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\t\u003c/span\u003e\u003cspan class=\"nf\"\u003eIsLossy\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003ebool\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"fulltexttokenizer\"\u003eFullTextTokenizer\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003eFullTextTokenizer\u003c/code\u003e 类实现了全文检索的分词器。\u003c/p\u003e","title":"DGraph 全文检索"},{"content":"背景 参考官方文档： golang_setup · Tencent/MMKV Wiki (github.com) 在 amd64 host 环境上交叉编译aarch64架构时出现以下的问题：\n编译mmkv静态库时：提示selected processor does not support \u0026quot;aese v6.16b,v0.16...\u0026quot; 报错信息.\ncgo提示 cannot find -lz\ncgo链接报错：\n# mmkv_test /usr/local/go/pkg/tool/linux_amd64/link: running aarch64-linux-g++ failed: exit status 1 /tmp/go-link-1100075970/000002.o: In function `_cgo_eea72cd326f0_Cfunc_checkReSetCryptKey\u0026#39;: /tmp/go-build/cgo-gcc-prolog:146: undefined reference to `checkReSetCryptKey\u0026#39; /tmp/go-link-1100075970/000002.o: In function `_cgo_eea72cd326f0_Cfunc_cryptKey\u0026#39;: /tmp/go-build/cgo-gcc-prolog:228: undefined reference to `cryptKey\u0026#39; /tmp/go-link-1100075970/000002.o: In function `_cgo_eea72cd326f0_Cfunc_reKey\u0026#39;: /tmp/go-build/cgo-gcc-prolog:1004: undefined reference to `reKey\u0026#39; collect2: error: ld returned 1 exit status 相关issue: https://github.com/Tencent/MMKV/issues/1212\n解决方案： 问题 1: 如果不需要加密功能，直接开启MMKV_DISABLE_CRYPT宏，然后再按文档进行编译。\n问题 2: 这是由于未找到aarch64版本的libz导致, 解决方式为：\n静态编译zlib. 将libz.a文件放到第一个问题编译出来的mmkv静态库的lib目录中. 重新编译. 问题3: 这是由于开启MMKV_DISABLE_CRYPT时，没有兼容golang引发的问题，相关pr： https://github.com/Tencent/MMKV/pull/1213\n整体编译过程： 下载aarch64 交叉编译链工具： https://toolchains.bootlin.com 下载zlib源码 wget https://www.zlib.net/zlib-1.3.tar.gz 静态编译zlib源码 ./configure --static --prefix=. #需要先设置CC,CXX等环境变量 复制zlib.a到lib目录 go build ","permalink":"https://catundercar.github.io/2024/01/01/mmkv-%E7%9A%84%E4%BA%A4%E5%8F%89%E7%BC%96%E8%AF%91/","summary":"\u003ch2 id=\"背景\"\u003e背景\u003c/h2\u003e\n\u003cp\u003e参考官方文档：\n\u003ca href=\"https://github.com/Tencent/MMKV/wiki/golang_setup\"\u003egolang_setup · Tencent/MMKV Wiki (github.com)\u003c/a\u003e\n在 \u003ccode\u003eamd64\u003c/code\u003e host 环境上交叉编译\u003ccode\u003eaarch64\u003c/code\u003e架构时出现以下的问题：\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\n\u003cp\u003e编译\u003ccode\u003emmkv\u003c/code\u003e静态库时：提示\u003ccode\u003eselected processor does not support \u0026quot;aese v6.16b,v0.16...\u0026quot;\u003c/code\u003e 报错信息.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ccode\u003ecgo\u003c/code\u003e提示 \u003ccode\u003ecannot find -lz\u003c/code\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ccode\u003ecgo\u003c/code\u003e链接报错：\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e# mmkv_test\n/usr/local/go/pkg/tool/linux_amd64/link: running aarch64-linux-g++ failed: exit status 1\n/tmp/go-link-1100075970/000002.o: In function `_cgo_eea72cd326f0_Cfunc_checkReSetCryptKey\u0026#39;:\n/tmp/go-build/cgo-gcc-prolog:146: undefined reference to `checkReSetCryptKey\u0026#39;\n/tmp/go-link-1100075970/000002.o: In function `_cgo_eea72cd326f0_Cfunc_cryptKey\u0026#39;:\n/tmp/go-build/cgo-gcc-prolog:228: undefined reference to `cryptKey\u0026#39;\n/tmp/go-link-1100075970/000002.o: In function `_cgo_eea72cd326f0_Cfunc_reKey\u0026#39;:\n/tmp/go-build/cgo-gcc-prolog:1004: undefined reference to `reKey\u0026#39;\ncollect2: error: ld returned 1 exit status\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e相关\u003ccode\u003eissue\u003c/code\u003e:\n\u003ca href=\"https://github.com/Tencent/MMKV/issues/1212\"\u003ehttps://github.com/Tencent/MMKV/issues/1212\u003c/a\u003e\u003c/p\u003e\n\u003ch2 id=\"解决方案\"\u003e解决方案：\u003c/h2\u003e\n\u003cp\u003e问题 1:\n如果不需要加密功能，直接开启\u003ccode\u003eMMKV_DISABLE_CRYPT\u003c/code\u003e宏，然后再按文档进行编译。\u003c/p\u003e","title":"MMKV 的交叉编译"},{"content":"最近在使用 MySQL 数据库时遇到了一个问题，就是在进行查询时，MySQL 并不会区分查询条件中的大小写。\n经过一番研究，发现这是 MySQL 的一个特性：**MySQL 默认使用的编码是 utf8mb4，在 utf8mb4 编码下，MySQL 默认情况下是不区分大小写的。**这是因为在 utf8mb4 编码下，大小写字母在二进制表示上是相同的，因此在进行比较时，MySQL 会忽略大小写。\n所以无论在查询条件中使用的是大写字母、小写字母还是混合字母，MySQL 都会将其视为相同的查询条件。\n解决方案： 使用 COLLATE 关键字来指定查询时的字符集。 具体来说，我们可以将查询条件中的字符串转换为特定的字符集，以便在查询时进行区分。例如，我们可以使用以下语句来进行查询：\nSELECT * FROM table WHERE column COLLATE utf8mb4_bin = \u0026#39;SearchString\u0026#39;; // SELECT * FROM table WHERE column COLLATE utf8_bin = \u0026#39;SearchString\u0026#39;; 使用 BINARY 运算符 SELECT * FROM table WHERE BINARY column = \u0026#39;SearchString\u0026#39;; COLLATE 和 BINARY 运算符在 MySQL 中的效率都比较高。COLLATE 关键字通常用于对特定列或查询进行设置，而 BINARY 运算符通常用于对整个查询进行设置。\n在使用 COLLATE关键字时，由于会对查询条件进行字符集转换，因此可能会稍微降低一些查询性能。但是，这种性能下降通常是可以忽略不计的。在实际使用中，我们可以先尝试使用 COLLATE关键字，如果查询性能不足，再考虑使用其他优化策略。\n在使用 BINARY运算符时，由于不需要进行字符集转换，因此查询性能通常比使用 COLLATE关键字要高一些。但是，这种性能优势也是非常小的，通常不会对查询性能产生重大影响。\n因此，在实际使用中，我们可以根据具体情况选择使用 COLLATE 关键字还是 BINARY 运算符。如果查询量较大，建议使用 BINARY 运算符。如果查询量较小，可以使用 COLLATE 关键字进行查询。\n创建表时指定字符集 CREATE TABLE table_name ( column_name VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin ); 将 column_name 列的字符集设置为 utf8mb4，使用 utf8mb4_bin 作为排序规则，以便在查询时进行区分大小写。\n","permalink":"https://catundercar.github.io/2023/03/14/%E8%B8%A9%E5%9D%91%E8%AE%B0%E5%BD%95mysql-%E6%9F%A5%E8%AF%A2%E4%B8%8D%E5%8C%BA%E5%88%86%E5%A4%A7%E5%B0%8F%E5%86%99/","summary":"\u003cp\u003e最近在使用 MySQL 数据库时遇到了一个问题，就是在进行查询时，MySQL 并不会区分查询条件中的大小写。\u003c/p\u003e\n\u003cp\u003e经过一番研究，发现这是 MySQL 的一个特性：**MySQL 默认使用的编码是 \u003ccode\u003eutf8mb4\u003c/code\u003e，在 \u003ccode\u003eutf8mb4\u003c/code\u003e 编码下，MySQL 默认情况下是不区分大小写的。**这是因为在 \u003ccode\u003eutf8mb4\u003c/code\u003e 编码下，大小写字母在二进制表示上是相同的，因此在进行比较时，MySQL 会忽略大小写。\u003c/p\u003e\n\u003cp\u003e所以\u003cstrong\u003e无论在查询条件中使用的是大写字母、小写字母还是混合字母，MySQL 都会将其视为相同的查询条件。\u003c/strong\u003e\u003c/p\u003e\n\u003ch3 id=\"解决方案\"\u003e解决方案：\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003e使用 \u003cstrong\u003eCOLLATE 关键字来指定查询时的字符集\u003c/strong\u003e。\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e具体来说，我们可以将查询条件中的字符串转换为特定的字符集，以便在查询时进行区分。例如，我们可以使用以下语句来进行查询：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-sql\" data-lang=\"sql\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eSELECT\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e*\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003eFROM\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003etable\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003eWHERE\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003ecolumn\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003eCOLLATE\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eutf8mb4_bin\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;SearchString\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"o\"\u003e//\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003eSELECT\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e*\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003eFROM\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003etable\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003eWHERE\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003ecolumn\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003eCOLLATE\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eutf8_bin\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;SearchString\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003col start=\"2\"\u003e\n\u003cli\u003e使用 \u003ccode\u003eBINARY\u003c/code\u003e 运算符\u003c/li\u003e\n\u003c/ol\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-sql\" data-lang=\"sql\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eSELECT\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e*\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003eFROM\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003etable\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003eWHERE\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nb\"\u003eBINARY\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003ecolumn\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;SearchString\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003ccode\u003eCOLLATE\u003c/code\u003e 和 \u003ccode\u003eBINARY\u003c/code\u003e 运算符在 MySQL 中的效率都比较高。\u003ccode\u003eCOLLATE\u003c/code\u003e 关键字通常用于对特定列或查询进行设置，而 \u003ccode\u003eBINARY\u003c/code\u003e 运算符通常用于对整个查询进行设置。\u003c/p\u003e\n\u003cp\u003e在使用 \u003ccode\u003eCOLLATE\u003c/code\u003e关键字时，由于会对查询条件进行字符集转换，因此可能会稍微降低一些查询性能。但是，这种性能下降通常是可以忽略不计的。在实际使用中，我们可以先尝试使用 \u003ccode\u003eCOLLATE\u003c/code\u003e关键字，如果查询性能不足，再考虑使用其他优化策略。\u003c/p\u003e\n\u003cp\u003e在使用 \u003ccode\u003eBINARY\u003c/code\u003e运算符时，由于不需要进行字符集转换，因此查询性能通常比使用 \u003ccode\u003eCOLLATE\u003c/code\u003e关键字要高一些。但是，这种性能优势也是非常小的，通常不会对查询性能产生重大影响。\u003c/p\u003e","title":"踩坑记录：MySQL 查询不区分大小写"},{"content":"MySQL 分为Server层和存储层。Server层包括连接器、查询缓存、分析器、优化器和执行器。\n连接器 连接器负责跟客户端建立连接、获取权限、维持和管理连接。 查看连接：\nSHOW PROCESSLIST Sleep 表示空闲连接，默认8小时自动断开，通过参数wait_timeout控制。\n长连接 长连接是指连接成功后，如果客户端持续有请求，则一直使用同一个连接。 MySQL在执行过程中，临时使用的内存是管理在连接对象里面的，这些资源会在连接断开的时候才会释放。 所以长连接累积下来，可能导致内存占用太大，被系统强行杀掉（OOM），会导致MySQL异常重启。 解决方案：\n定期关闭长连接。使用一段时间之后，获者程序里面判断执行过一个占用内存的大查询后，断开连接，之后查询再重连。 每次执行一个比较大的操作后，通过执行mysql_reset_connection来重新初始化连接资源。这个过程不需要重连和重新做权限验证，但是回不将连接恢复到刚刚创建完时的状态。 分析器 词法分析和语法分析。\n优化器 索引优化和多表关联优化。\n执行器 判断是否有表的权限 调用存储引擎接口 ","permalink":"https://catundercar.github.io/2023/03/12/mysql-00-%E5%9F%BA%E7%A1%80%E6%9E%B6%E6%9E%84/","summary":"\u003cp\u003eMySQL 分为Server层和存储层。Server层包括连接器、查询缓存、分析器、优化器和执行器。\u003c/p\u003e\n\u003ch2 id=\"连接器\"\u003e连接器\u003c/h2\u003e\n\u003cp\u003e连接器负责跟客户端建立连接、获取权限、维持和管理连接。\n查看连接：\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eSHOW PROCESSLIST\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/images/post/190DC58A-F184-4DF8-8911-9E9F3553B59D_4_5005_c.jpeg\"\u003e\n\u003ccode\u003eSleep\u003c/code\u003e 表示空闲连接，默认8小时自动断开，通过参数wait_timeout控制。\u003c/p\u003e\n\u003ch3 id=\"长连接\"\u003e长连接\u003c/h3\u003e\n\u003cp\u003e长连接是指连接成功后，如果客户端持续有请求，则一直使用同一个连接。\n\u003cstrong\u003eMySQL在执行过程中，临时使用的内存是管理在连接对象里面的，这些资源会在连接断开的时候才会释放。\u003c/strong\u003e\n所以长连接累积下来，可能导致内存占用太大，被系统强行杀掉（OOM），会导致MySQL异常重启。\n解决方案：\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e定期关闭长连接。使用一段时间之后，获者程序里面判断执行过一个占用内存的大查询后，断开连接，之后查询再重连。\u003c/li\u003e\n\u003cli\u003e每次执行一个比较大的操作后，通过执行mysql_reset_connection来重新初始化连接资源。这个过程不需要重连和重新做权限验证，但是回不将连接恢复到刚刚创建完时的状态。\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch2 id=\"分析器\"\u003e分析器\u003c/h2\u003e\n\u003cp\u003e词法分析和语法分析。\u003c/p\u003e\n\u003ch2 id=\"优化器\"\u003e优化器\u003c/h2\u003e\n\u003cp\u003e索引优化和多表关联优化。\u003c/p\u003e\n\u003ch2 id=\"执行器\"\u003e执行器\u003c/h2\u003e\n\u003col\u003e\n\u003cli\u003e判断是否有表的权限\u003c/li\u003e\n\u003cli\u003e调用存储引擎接口\u003c/li\u003e\n\u003c/ol\u003e","title":"MySQL-00-基础架构"},{"content":" redo log binlog 重做日志：redo log redo log 是InnoDB特有的日志。 WAL（Write-Ahead-Log）: 先写日志，再写磁盘。 具体来说： 当有一条记录需要更新的时候，InnoDB 引擎就会先把记录写到 redo log（粉板）里面，并更新内存，这个时候更新就算完成了。同时，InnoDB 引擎会在适当的时候，将这个操作记录更新到磁盘里面，而这个更新往往是在系统比较空闲的时候做。 InnoDB 的 redo log 是固定大小的，比如可以配置为一组 4 个文件，每个文件的大小是 1GB，那么这块\u0026quot;粉板\u0026quot;总共就可以记录 4GB 的操作。从头开始写，写到末尾就又回到开头循环写，如下面这个图所示。 有了 redo log，InnoDB 就可以保证即使数据库发生异常重启，之前提交的记录都不会丢失，这个能力称为 crash-safe。\n归档日志： Binlog binlog 是MySQL Server层的日志。 和redo log 的不同之处在于：\nredo log 是 InnoDB 引擎特有的；binlog 是 MySQL 的 Server 层实现的，所有引擎都可以使用。 redo log 是物理日志，记录的是\u0026quot;在某个数据页上做了什么修改\u0026quot;；binlog 是逻辑日志，记录的是这个语句的原始逻辑，比如\u0026quot;给 ID=2 这一行的 c 字段加 1 \u0026ldquo;。 redo log 是循环写的，空间固定会用完；binlog 是可以追加写入的。\u0026ldquo;追加写\u0026quot;是指 binlog 文件写到一定大小后会切换到下一个，并不会覆盖以前的日志。 例子： update T set c=c+1 where ID=2; 执行器和InnoDB在执行一个update语句时内部流程：\nsequenceDiagram 执行器 -\u0026gt;\u0026gt; InnoDB: 取ID=2这一行 alt 数据页不在内存中 InnoDB -\u0026gt;\u0026gt; 磁盘: 磁盘中读入内存 else InnoDB -\u0026gt;\u0026gt; 执行器: 返回行数据 end 执行器 -\u0026gt;\u0026gt; 执行器: 将这行的C值+1 执行器 -\u0026gt;\u0026gt; InnoDB: Write new value InnoDB -\u0026gt;\u0026gt; InnoDB: Write to memory InnoDB -\u0026gt;\u0026gt; 磁盘: Write redo log Note over InnoDB: Prepare InnoDB -\u0026gt;\u0026gt; 执行器: Done 执行器 -\u0026gt;\u0026gt; 磁盘: Write bin log 执行器 -\u0026gt;\u0026gt; InnoDB: Commit Note over InnoDB: Committed 两阶段提交 由于 redo log 和 binlog 是两个独立的逻辑，如果不用两阶段提交，要么就是先写完 redo log 再写 binlog，或者采用反过来的顺序。我们看看这两种方式会有什么问题。 仍然用前面的 update 语句来做例子。假设当前 ID=2 的行，字段 c 的值是 0，再假设执行 update 语句过程中在写完第一个日志后，第二个日志还没有写完期间发生了 crash，会出现什么情况呢？\n先写 redo log 后写 binlog。假设在 redo log 写完，binlog 还没有写完的时候，MySQL 进程异常重启。由于我们前面说过的，redo log 写完之后，系统即使崩溃，仍然能够把数据恢复回来，所以恢复后这一行 c 的值是 1。 但是由于 binlog 没写完就 crash 了，这时候 binlog 里面就没有记录这个语句。因此，之后备份日志的时候，存起来的 binlog 里面就没有这条语句。 然后你会发现，如果需要用这个 binlog 来恢复临时库的话，由于这个语句的 binlog 丢失，这个临时库就会少了这一次更新，恢复出来的这一行 c 的值就是 0，与原库的值不同。 先写 binlog 后写 redo log。如果在 binlog 写完之后 crash，由于 redo log 还没写，崩溃恢复以后这个事务无效，所以这一行 c 的值是 0。但是 binlog 里面已经记录了\u0026quot;把 c 从 0 改成 1\u0026quot;这个日志。所以，在之后用 binlog 来恢复的时候就多了一个事务出来，恢复出来的这一行 c 的值就是 1，与原库的值不同。 可以看到，如果不使用\u0026quot;两阶段提交\u0026rdquo;，那么数据库的状态就有可能和用它的日志恢复出来的库的状态不一致。 简单说，redo log 和 binlog 都可以用于表示事务的提交状态，而两阶段提交就是让这两个状态保持逻辑上的一致。 ","permalink":"https://catundercar.github.io/2023/03/12/mysql-01-%E6%97%A5%E5%BF%97%E7%B3%BB%E7%BB%9F/","summary":"\u003cul\u003e\n\u003cli\u003eredo log\u003c/li\u003e\n\u003cli\u003ebinlog\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"重做日志redo-log\"\u003e重做日志：redo log\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003eredo log\u003c/code\u003e 是InnoDB特有的日志。\nWAL（Write-Ahead-Log）: 先写日志，再写磁盘。\n具体来说：\n当有一条记录需要更新的时候，InnoDB 引擎就会先把记录写到 redo log（粉板）里面，并更新内存，这个时候更新就算完成了。同时，InnoDB 引擎会在适当的时候，将这个操作记录更新到磁盘里面，而这个更新往往是在系统比较空闲的时候做。\nInnoDB 的 redo log 是固定大小的，比如可以配置为一组 4 个文件，每个文件的大小是 1GB，那么这块\u0026quot;粉板\u0026quot;总共就可以记录 4GB 的操作。从头开始写，写到末尾就又回到开头循环写，如下面这个图所示。\n\u003cimg loading=\"lazy\" src=\"/images/post/MySQL/Pasted%20image%2020230306153314.png\"\u003e\n有了 redo log，InnoDB 就可以保证即使数据库发生异常重启，之前提交的记录都不会丢失，这个能力称为 crash-safe。\u003c/p\u003e\n\u003ch2 id=\"归档日志-binlog\"\u003e归档日志： Binlog\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003ebinlog\u003c/code\u003e 是MySQL Server层的日志。\n和\u003ccode\u003eredo log\u003c/code\u003e 的不同之处在于：\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eredo log 是 InnoDB 引擎特有的；binlog 是 MySQL 的 Server 层实现的，所有引擎都可以使用。\u003c/li\u003e\n\u003cli\u003eredo log 是物理日志，记录的是\u0026quot;在某个数据页上做了什么修改\u0026quot;；binlog 是逻辑日志，记录的是这个语句的原始逻辑，比如\u0026quot;给 ID=2 这一行的 c 字段加 1 \u0026ldquo;。\u003c/li\u003e\n\u003cli\u003eredo log 是循环写的，空间固定会用完；binlog 是可以追加写入的。\u0026ldquo;追加写\u0026quot;是指 binlog 文件写到一定大小后会切换到下一个，并不会覆盖以前的日志。\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e例子：\n\u003ccode\u003eupdate T set c=c+1 where ID=2;\u003c/code\u003e\n执行器和InnoDB在执行一个update语句时内部流程：\u003c/p\u003e","title":"MySQL-01-日志系统"},{"content":"事务 事务就是一组SQL语句，作为一个工作单元以原子方式进行处理。如果数据库引擎能够成功地对数据库应用整组语句，那么就执行该组语句。如果其中有任何一条语句因为崩溃或其他原因无法执行，那么整组语句都不执行。也就是说，作为事务的一组语句，要么全部执行成功，要么全部执行失败。 ACID代表原子性(atomicity)、一致性(consistency)、隔离性(isolation)和持久性(durability)\n隔离性和隔离级别 隔离性 通常来说，一个事务所做的修改在最终提交以前，对其他事务是不可见的，这就是隔离性带来的结果。在前面的例子中，当执行完第3条语句、第4条语句还未开始时，此时有另外一个账户汇总程序开始运行，其看到的支票账户的余额并没有被减去200美元。后面我们讨论隔离级别(isolation level)的时候，会发现为什么我们要说\u0026quot;通常来说\u0026quot;是不可见的。\n隔离级别 多个事务同时执行的时候，可能出现的问题：\n脏读 不可重复读 幻读 为了解决这些问题，就有了\u0026quot;隔离级别\u0026quot;的概念。 SQL标准的事务隔离级别有： 读未提交（read uncommitted） 一个事务还没提交时，它的变更就能被别的事务看到。 读提交（read committed） 一个事务提交后，它的变更才会被其他事务看到。 这个级别仍然允许不可重复读(nonrepeatable read)，这意味着同一事务中两次执行相同语句，可能会看到不同的数据结果。 可重复读（repeatable read） 一个事务执行过程中看到的数据，总是跟这个事务在启动时看到的数据是一致的。当然在可重复读隔离级别下，未提交变更对其他事务也是不可见的。 串行化（serializable） 顾名思义是对于同一行记录，\u0026ldquo;写\u0026quot;会加\u0026quot;写锁\u0026rdquo;，\u0026ldquo;读\u0026quot;会加\u0026quot;读锁\u0026rdquo;。当出现读写锁冲突的时候，后访问的事务必须等前一个事务执行完成，才能继续执行。 ","permalink":"https://catundercar.github.io/2023/03/12/mysql-02-%E4%BA%8B%E5%8A%A1%E9%9A%94%E7%A6%BB/","summary":"\u003ch2 id=\"事务\"\u003e事务\u003c/h2\u003e\n\u003cp\u003e事务就是一组SQL语句，作为一个工作单元以原子方式进行处理。如果数据库引擎能够成功地对数据库应用整组语句，那么就执行该组语句。如果其中有任何一条语句因为崩溃或其他原因无法执行，那么整组语句都不执行。也就是说，作为事务的一组语句，要么全部执行成功，要么全部执行失败。\nACID代表原子性(atomicity)、一致性(consistency)、隔离性(isolation)和持久性(durability)\u003c/p\u003e\n\u003ch2 id=\"隔离性和隔离级别\"\u003e隔离性和隔离级别\u003c/h2\u003e\n\u003ch4 id=\"隔离性\"\u003e隔离性\u003c/h4\u003e\n\u003cp\u003e通常来说，一个事务所做的修改在最终提交以前，对其他事务是不可见的，这就是隔离性带来的结果。在前面的例子中，当执行完第3条语句、第4条语句还未开始时，此时有另外一个账户汇总程序开始运行，其看到的支票账户的余额并没有被减去200美元。后面我们讨论隔离级别(isolation level)的时候，会发现为什么我们要说\u0026quot;通常来说\u0026quot;是不可见的。\u003c/p\u003e\n\u003ch3 id=\"隔离级别\"\u003e隔离级别\u003c/h3\u003e\n\u003cp\u003e多个事务同时执行的时候，可能出现的问题：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e脏读\u003c/li\u003e\n\u003cli\u003e不可重复读\u003c/li\u003e\n\u003cli\u003e幻读\n为了解决这些问题，就有了\u0026quot;隔离级别\u0026quot;的概念。\nSQL标准的事务隔离级别有：\u003c/li\u003e\n\u003cli\u003e读未提交（read uncommitted）\n一个事务还没提交时，它的变更就能被别的事务看到。\u003c/li\u003e\n\u003cli\u003e读提交（read committed）\n一个事务提交后，它的变更才会被其他事务看到。\n这个级别仍然允许不可重复读(nonrepeatable read)，这意味着同一事务中两次执行相同语句，可能会看到不同的数据结果。\u003c/li\u003e\n\u003cli\u003e可重复读（repeatable read）\n一个事务执行过程中看到的数据，总是跟这个事务在启动时看到的数据是一致的。当然在可重复读隔离级别下，未提交变更对其他事务也是不可见的。\u003c/li\u003e\n\u003cli\u003e串行化（serializable）\n顾名思义是对于同一行记录，\u0026ldquo;写\u0026quot;会加\u0026quot;写锁\u0026rdquo;，\u0026ldquo;读\u0026quot;会加\u0026quot;读锁\u0026rdquo;。当出现读写锁冲突的时候，后访问的事务必须等前一个事务执行完成，才能继续执行。\u003c/li\u003e\n\u003c/ul\u003e","title":"MySQL-02-事务隔离"},{"content":"索引 提高数据查询的效率\n数据存储在磁盘上面，需要索引按某个key（搜索码）记录数据存储的位置，提高查询效率。\n索引常见模型 有两种基本的索引类型：\n顺序索引\n基于值的顺序排序。\n散列索引（hash）\n基于将值平均分布到若干散列桶中。一个值所属的散列桶是由一个函数决定的，该函数称为散列函数（hash function）\n指标 查询类型\n能有效支持的访问类型。访问类型可以包括找到具体特定属性值的记录，以及找到属性值落在某个特定范围内的记录。\n查询时间\n查询一个特定数据项或数据项集所需的时间。\n写入时间\n插入一个新数据项所需的时间。该值包括找到待删除项所需的时间，以及更新索引结构所需的时间。\n删除时间\n删除一个数据项所需的时间。该值包括找到待删除项所需的时间，以及更新索引结构所需的时间。\n空间开销\n索引结构所占用的额外存储空间。倘若存储索引结构的额外空间大小适度，通常牺牲一定的空间代价来换取性能的提高是值得的。\n顺序索引 为了快速随机访问文件中的记录，可以使用索引结构。每个索引结构与一个特定的搜索码相关联。顺序索引按顺序存储搜索码的值，并将每个搜索码与包含该搜索码的记录关联起来。\ngraph LR key1(1) --\u0026gt; record1(id:1 name: a) key2(2) --\u0026gt; record2(id:2 name: b) key3(3) --\u0026gt; record3(id:3 name: c) record1 --\u0026gt; file record2 --\u0026gt; file record3 --\u0026gt; file 文件中的记录自身也可以按照某种排序顺序存储。一个文件可以有多个索引，分别基于不同的搜索码。\n聚簇索引（clustering index）(primary index)\n如果文件按照某个搜索码指定的顺序排序，那么该搜索码对应的索引称为聚簇索引。聚簇索引也称为主索引。\n非聚簇索引(non-clustering index)（二级索引）（secondary index）\n搜索码指定的顺序与文件中记录的物理顺序不同的索引称为非聚簇索引。也称为辅助索引。\n索引顺序文件\n按照聚簇索引顺序排列的文件称为索引顺序文件。\nB+树索引文件 // TODO\n哈希表 //TODO\n有序数组 //TODO\n搜索树 //TODO\n","permalink":"https://catundercar.github.io/2023/03/12/mysql-03-%E7%B4%A2%E5%BC%95/","summary":"\u003ch2 id=\"索引\"\u003e索引\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003cp\u003e提高数据查询的效率\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e数据存储在磁盘上面，需要索引按某个key（搜索码）记录数据存储的位置，提高查询效率。\u003c/p\u003e\n\u003ch3 id=\"索引常见模型\"\u003e索引常见模型\u003c/h3\u003e\n\u003cp\u003e有两种基本的索引类型：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e顺序索引\u003c/p\u003e\n\u003cp\u003e基于值的顺序排序。\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e散列索引（hash）\u003c/p\u003e\n\u003cp\u003e基于将值平均分布到若干散列桶中。一个值所属的散列桶是由一个函数决定的，该函数称为散列函数（hash function）\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"指标\"\u003e指标\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e查询类型\u003c/p\u003e\n\u003cp\u003e能有效支持的访问类型。访问类型可以包括找到具体特定属性值的记录，以及找到属性值落在某个特定范围内的记录。\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e查询时间\u003c/p\u003e\n\u003cp\u003e查询一个特定数据项或数据项集所需的时间。\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e写入时间\u003c/p\u003e\n\u003cp\u003e插入一个新数据项所需的时间。该值包括找到待删除项所需的时间，以及更新索引结构所需的时间。\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e删除时间\u003c/p\u003e\n\u003cp\u003e删除一个数据项所需的时间。该值包括找到待删除项所需的时间，以及更新索引结构所需的时间。\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e空间开销\u003c/p\u003e\n\u003cp\u003e索引结构所占用的额外存储空间。倘若存储索引结构的额外空间大小适度，通常牺牲一定的空间代价来换取性能的提高是值得的。\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"顺序索引\"\u003e顺序索引\u003c/h3\u003e\n\u003cp\u003e为了快速随机访问文件中的记录，可以使用索引结构。每个索引结构与一个特定的搜索码相关联。顺序索引按顺序存储搜索码的值，并将每个搜索码与包含该搜索码的记录关联起来。\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-mermaid\" data-lang=\"mermaid\"\u003egraph LR\n    key1(1) --\u0026gt; record1(id:1 name: a)\n    key2(2) --\u0026gt; record2(id:2 name: b)\n    key3(3) --\u0026gt; record3(id:3 name: c)\n    record1 --\u0026gt; file\n    record2 --\u0026gt; file\n    record3 --\u0026gt; file\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e文件中的记录自身也可以按照某种排序顺序存储。一个文件可以有多个索引，分别基于不同的搜索码。\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e聚簇索引（clustering index）(primary index)\u003c/p\u003e\n\u003cp\u003e如果文件按照某个搜索码指定的顺序排序，那么该搜索码对应的索引称为聚簇索引。聚簇索引也称为主索引。\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e非聚簇索引(non-clustering index)（二级索引）（secondary index）\u003c/p\u003e\n\u003cp\u003e搜索码指定的顺序与文件中记录的物理顺序不同的索引称为非聚簇索引。也称为辅助索引。\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e索引顺序文件\u003c/p\u003e\n\u003cp\u003e按照聚簇索引顺序排列的文件称为索引顺序文件。\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"b树索引文件\"\u003eB+树索引文件\u003c/h3\u003e\n\u003cp\u003e// TODO\u003c/p\u003e\n\u003ch3 id=\"哈希表\"\u003e哈希表\u003c/h3\u003e\n\u003cp\u003e//TODO\u003c/p\u003e\n\u003ch3 id=\"有序数组\"\u003e有序数组\u003c/h3\u003e\n\u003cp\u003e//TODO\u003c/p\u003e\n\u003ch3 id=\"搜索树\"\u003e搜索树\u003c/h3\u003e\n\u003cp\u003e//TODO\u003c/p\u003e","title":"MySQL-03 索引"},{"content":"道阻且长，行则将至\n联系 GitHub：@catundercar Skill Keywords Software Engineer Keywords Go Rust gRPC\n","permalink":"https://catundercar.github.io/about/","summary":"\u003cp\u003e道阻且长，行则将至\u003c/p\u003e\n\u003ch2 id=\"联系\"\u003e联系\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003eGitHub：\u003ca href=\"https://github.com/catundercar\"\u003e@catundercar\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"skill-keywords\"\u003eSkill Keywords\u003c/h2\u003e\n\u003ch3 id=\"software-engineer-keywords\"\u003eSoftware Engineer Keywords\u003c/h3\u003e\n\u003cp\u003e\u003ccode\u003eGo\u003c/code\u003e \u003ccode\u003eRust\u003c/code\u003e \u003ccode\u003egRPC\u003c/code\u003e\u003c/p\u003e","title":"关于"}]