最高級的紅酒,一定要摻上雪碧才好喝。
基於這樣的品味,我設計出了一套在經典nosql數據庫redis上實現SQL引擎的方法。既然redis號稱nosql,而我偏要把SQL加到redis上,於是這個技術方案取名為【YesSql】。
1.在redis上實現SQL查詢的技術基礎
- redis上可以執行lua。整個SQL引擎就是在lua上解析SQL語句,執行,並返回結果。
- lua有很好的正則表達式引擎,因此解析SQL語法變得簡單。
- redis提供map, zset這樣的數據結構,很容易實現列存儲
- 關系數據庫不也就是索引+遍歷,核心邏輯完全能用lua來實現。
2.實現細節
2.1 create table
- 假定我只支持number和string兩種數據結構
- 把整個按行組織的表看成由N個字段組成的列存儲
- 也就是說,字段的組織是: table_column -> map 或 zset
- 用一個 table_rowid -> int 來產生一個rowid
- map或者zset中的key使用rowid:
${table}_rowid -> int_value
${table}_${column1} -> map
${rowid1} -> column1 data of row 1
${rowid2} -> column1 data of row 2
${table}_${column2} -> map
${rowid1} -> column2 data of row 1
${rowid2} -> column2 data of row 2
- 可以有唯一索引:
${table}_unique_index_1 -> map
${unique_key_1} -> rowid1
${unique_key_2} -> rowid
- 可以有數值類型的排序索引:
${table}_number_index_1 -> zset
$rowid1 -> number1
$rowid2 -> number2
- 所以:create table就是建立上面的KEY結構
- 建表的語法大致如下:
create table my_first_redis_table_1(
column1 number,
column2 string,
column2 number
unique index column2,
number_index column3
)
2.2 insert
- 插入時先要使用redis的INC指令得到一個新的rowid
- 插入其實就是在
${table}_${column}
字段的下面增加二級KEY
2.3 update
- update可以指定rowid或者唯一索引中的字段
- 如果where條件比較復雜,則只能遍歷字段,並最終取多個rowid集合的交集
- set中的字段,先找到rowid,然后根據rowid更新就好了
2.3 delete
- where條件中的搜索如同上面
- 刪除行就是逐個刪除每個column key下面的rowid對應的二級KEY
2.4 select
這部分相對復雜,划分為不同的場景:
2.4.1 from部分
解析出表名是第一步的,然后就確定了KEY的前綴。
2.4.2 where條件
上面講update和delete的where部分一筆帶過了,具體有這樣的一些場景:
- 使用rowid=xxx或者rowid in ()的方式,比較簡單
- 使用unique_key=xxx或者unique_key in ()的方式,也比較簡單,先通過唯一索引得到rowid,然后再根據rowid查詢
- 使用number_index的范圍查詢的情況,先使用ZRANGEBYSCORE找到多個rowid,然后再查詢
- 使用and/or/in及其其他字段上的表達式,無非也就是層層加過濾,知道最終確定rowid的集合
2.4.3 select部分
- 每選擇一個列,就意味着要輸出這個列的值給查詢方
- 字段上的表達式,也比較容易實現
2.4.4 group by部分
- 可以建立一個所有group by中字段名組合起來的臨時KEY作為二級KEY的map
例如:
select column1, column2, count(1), sum(column3)
from table
group by column1, column2
可以建立這樣的KEY結構:
temp_group_by_1 -> map
${column1_value}_${column2_value} -> rowid
temp_group_by_1_stat1 -> map
$rowid1 -> count_value
temp_group_by_1_stat2 -> map
$rowid1 -> sum_value
2.4.5 having部分
- having部分,也就是在上一步匯總好的基礎上,對rowid指向的值做過濾。
- 還有一個優化點:如果沒有having字句,匯總采用map結構;有having字句,采用zset結構,直接根據范圍做過濾
2.4.6 join部分
不再敘述,猜測不會有那么無聊的人真的希望用上這么一套SQL引擎。
3.最后
- 這是一個惡意的玩笑
- 某種程度上可以作為一種思維訓練,讓我們知道SQL引擎可能是怎么去運行的