問題
假設我們有一張超大的訂單表(N億),裡面有order_id、user_id等欄位。
- 能透過order_id快速查詢對應訂單
- 能透過user_id快速查詢該使用者具有的訂單列表
像上面這樣的要求改怎樣做呢?透過雜湊訂單ID取模?那如何滿足透過使用者ID快速查詢訂單列表呢?
什麼是基因演算法
理論:
He meant that taking number mod 2^n is equivalent to stripping off all but the n lowest-order (right-most) bits of number
即: 一個數取餘2的n次方,那麼餘數就是這個數的二進位制的最後n位數。所有我們可以位運算子把高位清零就可以得到餘數.
使用者user_id=666 (666的二進位制表示為:0000 0010 1001 1010)的使用者生成一個訂單步驟:
-
使用user_id%16分庫,決定這行資料要插入到哪個庫中
-
分庫基因是user_id的最後4個bit,即1010
-
在生成order_id時,先使用一種分散式ID生成演算法生成前60bit(上圖中淺灰色部分)
-
將分庫基因加入到order_id的最後4個bit(上圖中綠色部分)
-
拼裝成最終的64bit訂單order_id(上圖中藍色部分)
-
這般,保證了同一個使用者釋出的所有訂單的order_id,都落在同一個庫上,order_id的最後4個bit都相同,於是:
-
透過order_id%16能夠定位到庫
-
透過user_id%16也能定位到庫
-
Python程式碼實現
import random
# 假設我們有16個庫,每個庫有16個表
num_databases = 16
num_tables = 16
# 假設我們有一些使用者
user_ids = [random.randint(1, 10000) for _ in range(10)]
def generate_order_id(user_id):
# 使用分散式ID生成演算法生成前60bit(這裡我們簡化為一個隨機數)
distributed_id = random.randint(1, 2**60)
# 從 user_id 中提取基因
gene = user_id % 16
# 將基因加入到 order_id 的最後幾個bit
order_id = (distributed_id << 4) | gene
return order_id
def get_database_and_table_from_user_id(user_id):
# 使用 user_id 的最後4位作為基因
gene = user_id % 16
# 使用基因決定資料庫和表
database = gene % num_databases
table = gene % num_tables
return database, table
def get_database_and_table_from_order_id(order_id):
# 從 order_id 中提取基因
gene = order_id % 16
# 使用基因決定資料庫和表
database = gene % num_databases
table = gene % num_tables
return database, table
if __name__ == '__main__':
for user_id in user_ids:
u_database, u_table = get_database_and_table_from_user_id(user_id)
# 生成訂單ID
order_id = generate_order_id(user_id)
o_database, o_table = get_database_and_table_from_order_id(order_id)
print(f"user_id: {user_id}, order_id: {order_id} "
f"user database: {u_database}, user table: {u_table} "
f"order database: {o_database} order table: {o_table}")