<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                為了在 Redis 服務器中執行 Lua 腳本, Redis 在服務器內嵌了一個 Lua 環境(environment), 并對這個 Lua 環境進行了一系列修改, 從而確保這個 Lua 環境可以滿足 Redis 服務器的需要。 Redis 服務器創建并修改 Lua 環境的整個過程由以下步驟組成: 1. 創建一個基礎的 Lua 環境, 之后的所有修改都是針對這個環境進行的。 2. 載入多個函數庫到 Lua 環境里面, 讓 Lua 腳本可以使用這些函數庫來進行數據操作。 3. 創建全局表格?`redis`?, 這個表格包含了對 Redis 進行操作的函數, 比如用于在 Lua 腳本中執行 Redis 命令的?`redis.call`?函數。 4. 使用 Redis 自制的隨機函數來替換 Lua 原有的帶有副作用的隨機函數, 從而避免在腳本中引入副作用。 5. 創建排序輔助函數, Lua 環境使用這個輔佐函數來對一部分 Redis 命令的結果進行排序, 從而消除這些命令的不確定性。 6. 創建?`redis.pcall`?函數的錯誤報告輔助函數, 這個函數可以提供更詳細的出錯信息。 7. 對 Lua 環境里面的全局環境進行保護, 防止用戶在執行 Lua 腳本的過程中, 將額外的全局變量添加到了 Lua 環境里面。 8. 將完成修改的 Lua 環境保存到服務器狀態的?`lua`?屬性里面, 等待執行服務器傳來的 Lua 腳本。 接下來的各個小節將分別介紹這些步驟。 ## 創建 Lua 環境 在最開始的這一步, 服務器首先調用 Lua 的 C API 函數?`lua_open`?, 創建一個新的 Lua 環境。 因為 lua_open 函數創建的只是一個基本的 Lua 環境, 為了讓這個 Lua 環境可以滿足 Redis 的操作要求, 接下來服務器將對這個 Lua 環境進行一系列修改。 ## 載入函數庫 Redis 修改 Lua 環境的第一步, 就是將以下函數庫載入到 Lua 環境里面: * 基礎庫(base library): 這個庫包含 Lua 的核心(core)函數, 比如?`assert`?、?`error`?、?`pairs`?、?`tostring`?、?`pcall`?, 等等。 另外, 為了防止用戶從外部文件中引入不安全的代碼, 庫中的?`loadfile`?函數會被刪除。 * 表格庫(table library): 這個庫包含用于處理表格的通用函數, 比如?`table.concat`?、?`table.insert`?、?`table.remove`?、?`table.sort`, 等等。 * 字符串庫(string library): 這個庫包含用于處理字符串的通用函數, 比如用于對字符串進行查找的?`string.find`?函數, 對字符串進行格式化的?`string.format`?函數, 查看字符串長度的?`string.len`?函數, 對字符串進行翻轉的?`string.reverse`?函數, 等等。 * 數學庫(math library): 這個庫是標準 C 語言數學庫的接口, 它包括計算絕對值的?`math.abs`?函數, 返回多個數中的最大值和最小值的?`math.max`?函數和?`math.min`?函數, 計算二次方根的?`math.sqrt`?函數, 計算對數的?`math.log`?函數, 等等。 * 調試庫(debug library): 這個庫提供了對程序進行調試所需的函數, 比如對程序設置鉤子和取得鉤子的?`debug.sethook`?函數和`debug.gethook`?函數, 返回給定函數相關信息的?`debug.getinfo`?函數, 為對象設置元數據的?`debug.setmetatable`?函數, 獲取對象元數據的`debug.getmetatable`?函數, 等等。 * Lua CJSON 庫([http://www.kyne.com.au/~mark/software/lua-cjson.php](http://www.kyne.com.au/~mark/software/lua-cjson.php)): 這個庫用于處理 UTF-8 編碼的 JSON 格式, 其中`cjson.decode`?函數將一個 JSON 格式的字符串轉換為一個 Lua 值, 而?`cjson.encode`?函數將一個 Lua 值序列化為 JSON 格式的字符串。 * Struct 庫([http://www.inf.puc-rio.br/~roberto/struct/](http://www.inf.puc-rio.br/~roberto/struct/)): 這個庫用于在 Lua 值和 C 結構(struct)之間進行轉換, 函數`struct.pack`?將多個 Lua 值打包成一個類結構(struct-like)字符串, 而函數?`struct.unpack`?則從一個類結構字符串中解包出多個 Lua 值。 * Lua cmsgpack 庫([https://github.com/antirez/lua-cmsgpack](https://github.com/antirez/lua-cmsgpack)): 這個庫用于處理 MessagePack 格式的數據, 其中?`cmsgpack.pack`?函數將 Lua 值轉換為 MessagePack 數據, 而?`cmsgpack.unpack`?函數則將 MessagePack 數據轉換為 Lua 值。 通過使用這些功能強大的函數庫, Lua 腳本可以直接對執行 Redis 命令獲得的數據進行復雜的操作。 ## 創建?`redis`?全局表格 在這一步, 服務器將在 Lua 環境中創建一個?`redis`?表格(table), 并將它設為全局變量。 這個?`redis`?表格包含以下函數: * 用于執行 Redis 命令的?`redis.call`?和?`redis.pcall`?函數。 * 用于記錄 Redis 日志(log)的?`redis.log`?函數, 以及相應的日志級別(level)常量:?`redis.LOG_DEBUG`?,?`redis.LOG_VERBOSE`?,`redis.LOG_NOTICE`?, 以及?`redis.LOG_WARNING`?。 * 用于計算 SHA1 校驗和的?`redis.sha1hex`?函數。 * 用于返回錯誤信息的?`redis.error_reply`?函數和?`redis.status_reply`?函數。 在這些函數里面, 最常用也最重要的要數?`redis.call`?函數和?`redis.pcall`?函數 —— 通過這兩個函數, 用戶可以直接在 Lua 腳本中執行 Redis 命令: ~~~ redis> EVAL "return redis.call('PING')" 0 PONG ~~~ ## 使用 Redis 自制的隨機函數來替換 Lua 原有的隨機函數 為了保證相同的腳本可以在不同的機器上產生相同的結果, Redis 要求所有傳入服務器的 Lua 腳本, 以及 Lua 環境中的所有函數, 都必須是無副作用([side effect](http://en.wikipedia.org/wiki/Side_effect_(computer_science)))的純函數([pure function](http://en.wikipedia.org/wiki/Pure_function))。 但是, 在之前載入到 Lua 環境的?`math`?函數庫中, 用于生成隨機數的?`math.random`?函數和?`math.randomseed`?函數都是帶有副作用的, 它們不符合 Redis 對 Lua 環境的無副作用要求。 因為這個原因, Redis 使用自制的函數替換了?`math`?庫中原有的?`math.random`?函數和?`math.randomseed`?函數, 替換之后的兩個函數有以下特征: * 對于相同的 seed 來說,?`math.random`?總產生相同的隨機數序列, 這個函數是一個純函數。 * 除非在腳本中使用?`math.randomseed`?顯式地修改 seed , 否則每次運行腳本時, Lua 環境都使用固定的?`math.randomseed(0)`?語句來初始化 seed 。 比如說, 使用以下腳本, 我們可以打印 seed 值為?`0`?時,?`math.random`?對于輸入?`10`?至?`1`?所產生的隨機序列: 無論執行這個腳本多少次, 產生的值都是相同的: ~~~ $ redis-cli --eval random-with-default-seed.lua 1) (integer) 1 2) (integer) 2 3) (integer) 2 4) (integer) 3 5) (integer) 4 6) (integer) 4 7) (integer) 7 8) (integer) 1 9) (integer) 7 10) (integer) 2 ~~~ 但是, 如果我們在另一個腳本里面, 調用?`math.randomseed`?將 seed 修改為?`10086`?: 那么這個腳本生成的隨機數序列將和使用默認 seed 值?`0`?時生成的隨機序列不同: ~~~ $ redis-cli --eval random-with-new-seed.lua 1) (integer) 1 2) (integer) 1 3) (integer) 2 4) (integer) 1 5) (integer) 1 6) (integer) 3 7) (integer) 1 8) (integer) 1 9) (integer) 3 10) (integer) 1 ~~~ ## 創建排序輔助函數 上一個小節說到, 為了防止帶有副作用的函數令腳本產生不一致的數據, Redis 對?`math`?庫的?`math.random`?函數和?`math.randomseed`?函數進行了替換。 對于 Lua 腳本來說, 另一個可能產生不一致數據的地方是那些帶有不確定性質的命令。 比如對于一個集合鍵來說, 因為集合元素的排列是無序的, 所以即使兩個集合的元素完全相同, 它們的輸出結果也可能并不相同。 考慮下面這個集合例子: ~~~ redis> SADD fruit apple banana cherry (integer) 3 redis> SMEMBERS fruit 1) "cherry" 2) "banana" 3) "apple" redis> SADD another-fruit cherry banana apple (integer) 3 redis> SMEMBERS another-fruit 1) "apple" 2) "banana" 3) "cherry" ~~~ 這個例子中的?`fruit`?集合和?`another-fruit`?集合包含的元素是完全相同的, 只是因為集合添加元素的順序不同,?SMEMBERS?命令的輸出就產生了不同的結果。 Redis 將?SMEMBERS?這種在相同數據集上可能會產生不同輸出的命令稱為“帶有不確定性的命令”, 這些命令包括: * SINTER * SUNION * SDIFF * SMEMBERS * HKEYS * HVALS * KEYS 為了消除這些命令帶來的不確定性, 服務器會為 Lua 環境創建一個排序輔助函數?`__redis__compare_helper`?, 當 Lua 腳本執行完一個帶有不確定性的命令之后, 程序會使用?`__redis__compare_helper`?作為對比函數, 自動調用?`table.sort`?函數對命令的返回值做一次排序, 以此來保證相同的數據集總是產生相同的輸出。 舉個例子, 如果我們在 Lua 腳本中對?`fruit`?集合和?`another-fruit`?集合執行?SMEMBERS?命令, 那么兩個腳本將得出相同的結果 —— 因為腳本已經對?SMEMBERS?命令的輸出進行過排序了: ~~~ redis> EVAL "return redis.call('SMEMBERS', KEYS[1])" 1 fruit 1) "apple" 2) "banana" 3) "cherry" redis> EVAL "return redis.call('SMEMBERS', KEYS[1])" 1 another-fruit 1) "apple" 2) "banana" 3) "cherry" ~~~ ## 創建?`redis.pcall`?函數的錯誤報告輔助函數 在這一步, 服務器將為 Lua 環境創建一個名為?`__redis__err__handler`?的錯誤處理函數, 當腳本調用?`redis.pcall`?函數執行 Redis 命令, 并且被執行的命令出現錯誤時,?`__redis__err__handler`?就會打印出錯代碼的來源和發生錯誤的行數, 為程序的調試提供方便。 舉個例子, 如果客戶端要求服務器執行以下 Lua 腳本: 那么服務器將向客戶端返回一個錯誤: ~~~ $ redis-cli --eval wrong-command.lua (error) @user_script: 4: Unknown Redis command called from Lua script ~~~ 其中?`@user_script`?說明這是一個用戶定義的函數, 而之后的?`4`?則說明出錯的代碼位于 Lua 腳本的第四行。 ## 保護 Lua 的全局環境 在這一步, 服務器將對 Lua 環境中的全局環境進行保護, 確保傳入服務器的腳本不會因為忘記使用?`local`?關鍵字而將額外的全局變量添加到了 Lua 環境里面。 因為全局變量保護的原因, 當一個腳本試圖創建一個全局變量時, 服務器將報告一個錯誤: ~~~ redis> EVAL "x = 10" 0 (error) ERR Error running script (call to f_df1ad3745c2d2f078f0f41377a92bb6f8ac79af0): @enable_strict_lua:7: user_script:1: Script attempted to create global variable 'x' ~~~ 除此之外, 試圖獲取一個不存在的全局變量也會引發一個錯誤: ~~~ redis> EVAL "return x" 0 (error) ERR Error running script (call to f_03c387736bb5cc009ff35151572cee04677aa374): @enable_strict_lua:14: user_script:1: Script attempted to access unexisting global variable 'x' ~~~ 不過 Redis 并未禁止用戶修改已存在的全局變量, 所以在執行 Lua 腳本的時候, 必須非常小心, 以免錯誤地修改了已存在的全局變量: ~~~ redis> EVAL "redis = 10086; return redis" 0 (integer) 10086 ~~~ ## 將 Lua 環境保存到服務器狀態的?`lua`?屬性里面 經過以上的一系列修改, Redis 服務器對 Lua 環境的修改工作到此就結束了, 在最后的這一步, 服務器會將 Lua 環境和服務器狀態的?`lua`屬性關聯起來, 如圖 IMAGE_REDIS_SERVER_LUA 所示。 ![](https://box.kancloud.cn/2015-09-13_55f52c077aabd.png) 因為 Redis 使用串行化的方式來執行 Redis 命令, 所以在任何特定時間里, 最多都只會有一個腳本能夠被放進 Lua 環境里面運行, 因此, 整個 Redis 服務器只需要創建一個 Lua 環境即可。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看