UUID বনাম ULID বনাম সিরিয়াল আইডি: আপনার ডাটাবেসের জন্য সঠিক আইডি বেছে নেওয়া
UUID বনাম ULID বনাম সিরিয়াল আইডি: শিখুন প্রতিটি কীভাবে ইনডেক্সিং, সাজানো, শার্ডিং এবং প্রোজেক্টে নিরাপদ এক্সপোর্ট/ইম্পোর্ট প্রভাবিত করে।

প্রকৃত সমস্যা: এমন একটি আইডি বাছাই করা যা পরে আপনি অনুশোচনা করবেন না
প্রথম সপ্তাহে আইডি নির্বাচন সাধারণত নীরস মনে হয়। তারপর আপনি প্রোডাক্ট লঞ্চ করেন, ডাটা বাড়ে, এবং সেই "সহজ" সিদ্ধান্তটি সব জায়গায় দেখা দেয়: ইনডেক্স, URL, লগ, এক্সপোর্ট এবং ইন্টিগ্রেশনগুলোতে।
প্রকৃত প্রশ্ন হলো "কোনটি সেরা?" না — বরং "ভবিষ্যতে আমি কোন ব্যথা এড়াতে চাই?" আইডি বদলানো কঠিন কারণ সেগুলো অন্য টেবিলে কপি হয়ে যায়, ক্লায়েন্টে ক্যাশ হয়, এবং অন্যান্য সিস্টেমে নির্ভরশীল হয়ে পড়ে।
আইডি যখন প্রোডাক্টের বিবর্তনের সাথে মেলে না, তখন সাধারণত আপনি এটি কয়েকটি জায়গায় দেখতে পাবেন:
- প্রাইমারি কী ইনডেক্স অনুকূলভাবে না বাড়লে ইনসার্ট ও কোয়েরি ধীর হয়ে যায়।
- আইডি দিয়ে সাজালে তৈরি সময়ের সাথে মিল না হলে পেজিনেশন জটিল হয়।
- অন্য সিস্টেমগুলো আপনার আইডি ফরম্যাটের উপর নির্ভর করলে মাইগ্রেশন ঝুঁকিপূর্ণ হয়।
- আইডি পড়তে বা তুলনা করতে কঠিন হলে ডিবাগ দীর্ঘ হয়।
- একাধিক উৎস থেকে ডেটা মার্জ করলে ইম্পোর্টে সংঘাত হয়।
সুবিধার কথা এখন আর ভবিষ্যতের নমনীয়তার কথা পরে—এই ট্রেডঅফ সর্বদা থাকে। সিরিয়াল ইন্টেজার পড়তে সহজ এবং প্রায়ই দ্রুত, কিন্তু এগুলো রেকর্ড কাউন্ট ফাঁস করতে পারে এবং ডেটাসেট মার্জ করা কঠিন করে তোলে। র্যান্ডম UUIDগুলো সিস্টেম জুড়ে ইউনিকনেস দেয়, কিন্তু ইনডেক্সগুলোর উপর খারাপ প্রভাব ফেলে এবং মানুষ সহজে পড়তে পারে না। ULID গ্লোবাল ইউনিকনেস ও সময়-অনুমানযোগ্য অর্ডার চেষ্টা করে, তবে স্টোরেজ ও টুলিং-এ তারও কিছু ট্রেড-অফ আছে।
একটি কার্যকর ভাবনার উপায়: আইডি মূলত কার জন্য?
যদি আইডি প্রধানত মানুষের জন্য (সাপোর্ট, ডিবাগ, অপস), ছোট ও সহজ-স্ক্যানযোগ্য জিনিস ভালো থাকে। যদি এটা মেশিনের জন্য (ডিস্ট্রিবিউটেড রাইট, অফলাইন ক্লায়েন্ট, মাল্টি-রিজিয়ন সিস্টেম), তাহলে গ্লোবাল ইউনিকনেস ও সংঘাত এড়ানো বেশি গুরুত্বপূর্ণ।
সহজ ভাষায় সংজ্ঞা
লোকেরা যখন "UUID বনাম ULID বনাম সিরিয়াল আইডি" নিয়ে আলোচনা করে, তারা আসলে নির্বাচন করছে কিভাবে প্রতিটি সারি একটি ইউনিক লেবেল পাবে। সেই লেবেল পরে ইনসার্ট, সাজানো, মার্জ এবং ডেটা সরানোর জটিলতা নির্ধারণ করে।
সিরিয়াল আইডি (integer/bigint)
সিরিয়াল আইডি হল একটি কাউন্টার। ডাটাবেস 1 দেয়, তারপর 2, তারপর 3, ইত্যাদি (সাধারণত integer বা bigint হিসেবে সংরক্ষিত)। এটি পড়তে সহজ, স্টোরেজে সস্তা, এবং সাধারণত দ্রুত কারণ নতুন সারিগুলো ইনডেক্সের শেষে আসে।
UUID
UUID হলো 128-বিট আইডেন্টিফায়ার যা র্যান্ডম দেখা যায়, যেমন 3f8a...। বেশিরভাগ কনফিগারেশনে এটি ডাটাবেস থেকে নেক্সট নাম্বার না জিজ্ঞেস করেই জেনারেট করা যায়, তাই বিভিন্ন সিস্টেম স্বাধীনভাবে আইডি তৈরি করতে পারে। ট্রেড-অফ হলো যে র্যান্ডম-লুকিং ইনসার্ট ইনডেক্সকে বেশী কাজ করায় এবং একটি সরল bigint-এর তুলনায় বেশি স্পেস নেয়।
ULID
ULID ও 128-বিট কিন্তু এটি প্রায় টাইম-অর্ডার বজায় রাখার জন্য ডিজাইন করা। নতুন ULID সাধারণত পুরোনোদের পরে আসে, একই সঙ্গে গ্লোবালি ইউনিক থেকেও যায়। ফলে আপনি কিছুটা UUID-এর "যেকোনো জায়গায় জেনারেট করা যায়" সুবিধা পান সঙ্গে আরও বন্ধুত্বপূর্ণ সাজানো আচরণ।
সোজা সারাংশ:
- Serial: সবচেয়ে ছোট এবং ডিফল্টভাবে অর্ডারড।
- UUID: স্বাধীনভাবে জেনারেট করা সবচেয়ে সহজ, মানুষের পক্ষে কম বন্ধুত্বপূর্ণ, প্রায়শই র্যান্ডম অর্ডার।
- ULID: UUID-এর মতো স্বাধীন, কিন্তু সাধারণত টাইম-অর্ডার।
সিরিয়াল আইডি সাধারণত এক-ডাটাবেস অ্যাপ ও অভ্যন্তরীণ টুলে দেখা যায়। UUID তখন ব্যবহৃত হয় যখন ডেটা বিভিন্ন সার্ভিস, ডিভাইস বা রিজিয়নে তৈরি হয়। ULID জনপ্রিয় যখন টিমগুলো ডিস্ট্রিবিউটেড আইডি চান কিন্তু সাজানো, পেজিনেশন বা "সবচেয়ে নতুন আগে" কুয়েরি সম্পর্কে কেয়ার করে।
ইনডেক্সিং ও পারফরম্যান্স: বাস্তবে কী বদলায়
প্রাইমারি কী সাধারণত একটি ইনডেক্স দ্বারা ব্যাকড হয় (প্রায়শই B-tree)। সেই ইনডেক্সকে একটি সাজানো ফোনবুকের মতো ভাবুন: প্রতিটি নতুন সারি একটি এন্ট্রি যুক্ত করতে হয় যাতে লুকআপ দ্রুত থাকে।
র্যান্ডম আইডি (ক্লাসিক UUIDv4) হলে নতুন এন্ট্রি ইনডেক্স জুড়ে সব জায়গায় ল্যান্ড করে। এর ফলে ডাটাবেস অনেক ইনডেক্স পেইজে টাচ করে, পেইজ স্প্লিট হয় বেশি, এবং অতিরিক্ত লেখার কাজ হয়। সময়ের সাথে ইনডেক্স চর্ন বাড়ে: ইনসার্ট প্রতি বেশি কাজ, বেশি ক্যাশ মিস, এবং ইনডেক্স প্রত্যাশার চেয়ে বড় হয়ে যায়।
প্রায় বৃদ্ধি-পথে থাকা আইডি (সিরিয়াল/bigint, অথবা টাইম-অর্ডার আইডি যেমন অনেক ULID) হলে ডাটাবেস সাধারণত ইনডেক্সের শেষে নতুন এন্ট্রি অ্যাপেন্ড করতে পারে। এটি ক্যাশ-ফ্রেন্ডলি কারণ সাম্প্রতিক পেইজগুলো হট থাকে, এবং উচ্চ রাইট রেটে ইনসার্টগুলো মসৃণ হয়।
কী সাইজ গুরুত্বপূর্ণ কারণ ইনডেক্স এন্ট্রি বিনামূল্যের নয়:
- serial bigint: 8 bytes
- UUID: 16 bytes
- ULID: 16 bytes যদি বাইনারি হিসেবে সংরক্ষণ করা হয়, 26-অক্ষরের স্ট্রিং হলে অনেক বড়
বড় কীগুলি মানে প্রতিটি ইনডেক্স পেইজে কম এন্ট্রি ফিট করে। এতে সাধারণত গভীর ইনডেক্স, কিউয়েরি প্রতি বেশি পেইজ পড়া, এবং দ্রুত থাকতে বেশি RAM প্রয়োজন হয়।
যদি আপনার একটি "events" টেবিল থাকে যা ধারাবাহিক ইনসার্ট পায়, একটি র্যান্ডম UUID প্রাইমারি কী একটি bigint কীয়ের তুলনায় বেশী দ্রুত ধীর অনুভূত হতে পারে, যদিও একক-সারি লুকআপ এখনও ঠিকই কাজ করে। ভারি লেখার প্রত্যাশা থাকলে ইনডেক্সিং খরচই সাধারণত প্রথম বাস্তব পার্থক্য হিসেবে দেখা যায়।
সাজানো, পেজিনেশন এবং টাইম অর্ডার
আপনি যদি "আরও লোড করুন" বা ইনফিনাইট স্ক্রল বানিয়েছেন, তাহলে এমন আইডির কষ্ট আপনি ইতিমধ্যে ভোগ করেছেন যা ভালভাবে সাজায় না। একটি আইডি "ভালভাবে সাজে" যখন তার উপর অর্ডার করলে আপনাকে স্থিতিশীল, অর্থপূর্ণ অর্ডার দেয় (প্রায়শই ক্রিয়েশন টাইম) যাতে পেজিনেশন পূর্বনির্ধারিত থাকে।
র্যান্ডম আইডি (যেমন UUIDv4) হলে নতুন সারি বিস্তৃতভাবে ছড়িয়ে থাকে। id দ্বারা অর্ডার করলে এটি সময়ের সাথে মিলবে না, এবং "এই id-এর পরে" কার্সর পেজিনেশন অবিশ্বাসযোগ্য হয়ে পড়ে। সাধারণত আপনি created_at-এ ফিরে যান, যা ঠিক আছে, কিন্তু তা সতর্কভাবে করতে হবে।
ULID টাইম-অর্ডার বজায় রাখার জন্য ডিজাইন করা। যদি আপনি ULID দিয়ে সাজান (স্ট্রিং বা তার বাইনারি ফর্মে), নতুন আইটেম সাধারণত পরে আসে। এতে কার্সর পেজিনেশন সহজ হয় কারণ কার্সর হিসেবে শেষ দেখা ULID ব্যবহার করা যায়।
ULID আপনাকে কী দেয় (এবং কী দেয় না)
ULID ফিড, সহজ কার্সর, এবং UUIDv4-এ দেখা যায় এমন র্যান্ডম ইনসার্টের তুলনায় কম সমস্যা দেয়।
কিন্তু ULID এমনকি এক মিলিসেকেন্ডে একাধিক মেশিনে বহু আইডি জেনারেট হলে নিখুঁত টাইম অর্ডার গ্যারান্টি করে না। যদি নির্দিষ্ট অর্ডার লাগায়, তখনও প্রকৃত টাইমস্ট্যাম্প ব্যবহার করা ভাল।
কখন created_at এখনও ভালো
created_at দিয়ে সাজানো প্রায়শই নিরাপদ যখন আপনি ব্যাকফিল করবেন, ইতিহাস ইম্পোর্ট করবেন, বা স্পষ্ট টাই-ব্রেকিং লাগবে।
একটি ব্যবহারিক প্যাটার্ন হলো (created_at, id) দিয়ে অর্ডার করা, যেখানে id কেবল টাই-ব্রেকারের কাজ করে।
পরে শার্ডিং: আইডি সংঘাত এড়ানো
শার্ডিং মানে একটি ডাটাবেসকে কয়েকটি ছোট ডাটাবেসে ভাগ করা যাতে প্রতিটি শার্ড ডেটার অংশ রাখে। দলগুলো সাধারণত পরে এটাই করে যখন একক ডাটাবেস স্কেল করা কঠিন বা রিস্কি হয়ে যায়।
আপনার আইডি পছন্দ শার্ডিংকে বা তো পরিচালনাযোগ্য করে বা কষ্টকর করে তোলে।
সিকুয়েন্সিয়াল আইডি (auto-increment serial বা bigint) হলে প্রতিটি শার্ড আনন্দের সাথে 1, 2, 3... জেনারেট করবে। একই আইডি একাধিক শার্ডে থাকতে পারে। ডেটা মার্জ, সারি মুভ করা, বা ক্রস-শার্ড ফিচার তৈরি করার সময় সংঘাত দেখা যায়।
আপনি সমন্বয়ের মাধ্যমে সংঘাত এড়াতে পারেন (একটি সেন্ট্রাল আইডি সার্ভিস, বা শার্ড প্রতি রেঞ্জ), কিন্তু এতে ভিন্ন অংশ যোগ হয় এবং তা বটলনেক হয়ে যেতে পারে।
UUIDs এবং ULIDs সমন্বয় কমায় কারণ প্রতিটি শার্ড স্বাধীনভাবে আইডি জেনারেট করতে পারে অত্যন্ত কম ডুপ্লিকেট ঝুঁকি নিয়ে। যদি আপনি ডাটাবেস বিভক্ত করবেন বলে মনে করেন, এটা সিরিয়াল সিকোয়েন্সের বিরুদ্ধে সবচেয়ে শক্তিশালী যুক্তি।
একটা সহজ পরিকল্পনা যা কাজ করে (এবং এর খরচ)
একটি সাধারণ সমঝোতা হলো শার্ড প্রিফিক্স যোগ করা এবং প্রতিটি শার্ডে লোকাল সিকোয়েন্স ব্যবহার করা। আপনি এটি দুইটি কলামে রাখতে পারেন, বা এক ভ্যালুতে প্যাক করতে পারেন।
এটা কাজ করে, কিন্তু কাস্টম আইডি ফরম্যাট তৈরি করে। প্রতিটি ইন্টিগ্রেশনকে এটা বুঝতে হবে, সাজানো বিশ্ব-অর্ডার না থাকে অতিরিক্ত লজিক দরকার হয়, এবং ডেটা শার্ডগুলোর মধ্যে মুভ করলে আইডি রিরাইট করা লাগে (যা যদি সেই আইডি শেয়ার করা থাকে তবে রেফারেন্স ভেঙে দেয়)।
একটি প্রধান প্রশ্ন আগে জিজ্ঞেস করুন: কখনও আপনি একাধিক ডাটাবেস থেকে ডেটা কম্বাইন করতে এবং রেফারেন্স স্থির রাখতে চান? যদি হ্যাঁ, তাহলে প্রথম দিন থেকেই গ্লোবালি ইউনিক আইডি প্ল্যান করুন, অথবা পরে মাইগ্রেশনের জন্য বাজেট রাখুন।
ডেটা এক্সপোর্ট এবং ইম্পোর্ট ওয়ার্কফ্লো
এক্সপোর্ট ও ইম্পোর্ট হতেই আইডি পছন্দ তাত্ত্বিক না থেকে বাস্তবে আসে। যখনই আপনি প্রোডাকশন ক্লোন করে স্টেজিং তে নিয়ে যান, ব্যাকআপ রিস্টোর করেন, বা দুই সিস্টেমের ডেটা মার্জ করেন—তৎক্ষণাৎ আপনি জানতে পারবেন আপনার আইডি স্থিতিশীল ও পোর্টেবল কি না।
সিরিয়াল (auto-increment) আইডি হলে সাধারণত আপনি অন্য ডাটাবেসে ইনসার্ট রি-প্লে করে রেফারেন্স ঠিক রাখবেন না যদি না আপনি মূল সংখ্যাগুলো সংরক্ষণ করেন। যদি আপনি আংশিক রোই ইম্পোর্ট করেন (ধরা যাক 200 কাস্টমার এবং তাদের অর্ডার), আপনাকে টেবিলগুলো সঠিক ক্রমে লোড করতে হবে এবং এক্স্যাক্ট প্রাইমারি কি রাখতে হবে। যদি কিছু রি-নাম্বার করা হয়, ফরেন কি ভেঙে পড়ে।
UUIDs এবং ULIDs ডাটাবেস সিকোয়েন্স থেকে আলাদা জেনারেট হওয়ার কারণে পরিবেশগুলোর মধ্যে সরানো সহজ। আপনি রো কপি করে আইডি ঠিক রাখলে সম্পর্কগুলো ঠিক থাকবে। এটি ব্যাকআপ রিস্টোর, আংশিক এক্সপোর্ট অথবা ডেটাসেট মার্জের সময় সহায়ক।
উদাহরণ: প্রোডাকশন থেকে 50টি অ্যাকাউন্ট এক্সপোর্ট করে স্টেজিং-এ ডিবাগ করা। UUID/ULID প্রাইমারি কি থাকলে আপনি সেই অ্যাকাউন্ট এবং সম্পর্কিত রো (প্রজেক্ট, ইনভয়েস, লগ) ইম্পোর্ট করতে পারেন এবং সবকিছু ঠিক প্যারেন্টকে পয়েন্ট করবে। সিরিয়াল আইডি হলে প্রায়ই আপনাকে অনুবাদ টেবিল (old_id -> new_id) তৈরি করে ফরেন কি রিরাইট করতে হয়।
বাল্ক ইম্পোর্টের জন্য মৌলিক বিষয়গুলো আইডি টাইপের চেয়েও বেশি গুরুত্বপূর্ণ:
- নিশ্চিত করুন ইম্পোর্টার ডিফল্টভাবে নতুন আইডি জেনারেট না করে।
- প্যারেন্টগুলোকে আগে ইম্পোর্ট করুন, এবং লোডের পরে ফরেন কি ভ্যালিডেট করুন।
- সিরিয়াল আইডি ব্যবহার করলে সিকোয়েন্স রিসেট করুন না হলে পরবর্তী ইনসার্ট সংঘাত করবে।
- ULID-এর ক্ষেত্রে এগুলো কনসিস্টেন্টভাবে স্টোর ও এক্সপোর্ট করুন (স্ট্রিং বনাম বাইনারি)।
১০ মিনিটে কিভাবে সিদ্ধান্ত নেবেন
আপনি যদি কোনটি বেছে নিতে আটকে থাকেন, ভবিষ্যতে কোনটা কষ্ট দেবে সেটা লক্ষ্য করে দ্রুত একটি যুক্তিসঙ্গত সিদ্ধান্ত নিতে পারবেন।
- আপনার শীর্ষ ভবিষ্যৎ ঝুঁকি লিখে নিন। নির্দিষ্ট ঘটনা সাহায্য করে: একাধিক ডাটাবেসে বিভক্ত করা, অন্য সিস্টেম থেকে কাস্টমার ডেটা মার্জ করা, অফলাইন রাইট, পরিবেশগুলোর মধ্যে ফ্রিকুয়েন্ট ডেটা কপি।
- নির্ধারণ করুন আইডি সাজানো কি সময়ের সাথে মিলতে হবে। যদি "নতুনগুলো আগে" চান কোনো অতিরিক্ত কলামের ছাড়াই, ULID (বা UUIDv7) ভালো। যদি
created_atদিয়ে সাজানো ঠিক থাকে, UUID এবং serial উভয় কার্যকর। - লিখিত পরিমাণ ও ইনডেক্স সংবেদনশীলতা অনুমান করুন। ভারি ইনসার্ট প্রত্যাশা থাকলে এবং প্রাইমারি কী ইনডেক্সই বেশি ক্ষতিগ্রস্ত হবে বলে মনে হলে serial
BIGINTসাধারণত B-tree-এ সহজ। র্যান্ডম UUID বেশি চর্ন করে। - একটি ডিফল্ট বেছে নিন, তারপর ব্যতিক্রমগুলো ডকুমেন্ট করুন। সহজ রাখুন: বেশিরভাগ টেবিলের জন্য এক ডিফল্ট, এবং কখন ভিন্ন হবে তার স্পষ্ট নিয়ম (প্রায়তঃ: পাবলিক-ফেসিং আইডি বনাম অভ্যন্তরীণ আইডি)।
- পরিবর্তনের জায়গা রাখুন। আইডির মধ্যে অর্থ এনকোড করবেন না, সিদ্ধান্ত নিন কোথায় আইডি জেনারেট হবে (DB বনাম অ্যাপ), এবং কনস্ট্রেইন্টগুলো স্পষ্ট রাখুন।
সাধারণ ভুল এবং ফাঁদ
সবচেয়ে বড় ফাঁদ হল কোনও আইডি পপুলার্য দেখেই বেছে নেওয়া, তারপর দেখেন এটি আপনার কুয়েরি, স্কেল, অথবা ডেটা শেয়ারের সাথে মেলে না। বেশিরভাগ সমস্যা মাস পরেই দেখা দেয়।
সাধারণ ব্যর্থতা:
- সাবধানতা ছাড়াই সব জায়গায় UUID ব্যবহার করা। UUIDv4 ইনডেক্স ফুলিয়ে দেয় এবং ক্যাশ-ফ্রেন্ডলি কমায়। অ্যাপ কাজ করেও লেখায় ধীরতা ও বড় ব্যাকআপের মূল্য দিতে হতে পারে।
- সিরিয়াল আইডির উপর নির্ভর করে পরে একাধিক সিস্টেম, অঞ্চল বা শার্ড থেকে ডেটা মার্জ করতে গেলে সংঘাত দেখা। দ্রুত সমাধান হিসেবে অফসেট বা প্রিফিক্স দেয়া সিস্টেম জুড়ে লিক হয়ে যায়।
- ULID সবকিছু দ্রুত করে তুলবে এমন ধারণা ভুল। এটি ইনসার্ট অর্ডার ও টাইম-ভিত্তিক সাজানোর সাহায্য করে, কিন্তু ধীর JOIN, অনুপস্থিত ইনডেক্স, বা ওয়াইড রো ঠিক করবে না। কিছু জেনারেটর ভারি কনকারেন্সির সময় কঠোরভাবে মনোটোনিক নাও থাকতে পারে।
- সিকুয়েন্সিয়াল আইডি পাবলিকলি প্রকাশ করা। যদি আপনার URLs বা API-তে
123, 124, 125থাকে, মানুষ কাছের রেকর্ডগুলো অনুমান করে আপনার সিস্টেম পরীক্ষা করতে পারে। - প্রজেক্টের মাঝপথে আইডি টাইপ বদলানো ছাড়া মাইগ্রেশন প্ল্যান না রাখা। ফরেন কি, ক্যাশ, লগ, এবং বাইরের পেলোড পুরনো আইডি রেফার করে অনেক সময় দীর্ঘকাল ধরে থাকে।
সতর্কতা সংকেত যা আপনাকে আগে ঠিক করতে বলবে:
- আপনি পার্টনার থেকে ইম্পোর্ট আশা করেন বা এনভায়রনমেন্ট জুড়ে ডেটা প্রায়ই মার্জ করেন।
- আপনি আলাদা টাইমস্ট্যাম্প ছাড়া টাইম-অর্ডার পেজিনেশন চান।
- আপনি আইডি বাইরে শেয়ার করবেন (URL, webhook, মোবাইল অ্যাপ)।
- বড় আইডি মাইগ্রেশনের জন্য ডাউনটাইম চান না।
- খুব বড় টেবিলের প্রত্যাশা যেখানে ইনডেক্স সাইজ ও লিখার গতি গুরুত্বপূর্ণ।
সিদ্ধান্ত নেওয়ার আগে দ্রুত চেকলিস্ট
ডাটাবেস ও কুয়েরি বাস্তবতা চেক
একটা প্রাইমারি কী টাইপ বেছে নিন এবং বেশিরভাগ টেবিলে এটাই ব্যবহার করুন। টাইপ মিশানো (এক জায়গায় bigint, অন্য জায়গায় UUID) JOIN, API এবং মাইগ্রেশন কঠিন করে দেয়।
আপনার প্রত্যাশিত স্কেলে ইনডেক্স সাইজ অনুমান করুন। প্রশস্ত কী মানে বড় প্রাইমারি ইনডেক্স এবং বেশি মেমরি ও IO।
আপনি কিভাবে পেজিনেট করবেন তা ঠিক করুন। যদি আইডি দিয়ে পেজিনেট করেন, নিশ্চিত করুন আইডি-র অর্ডার পূর্বানুমানযোগ্য (অথবা তা মেনে নিন যে তা হবে না)। যদি টাইমস্ট্যাম্প দিয়ে পেজিনেট করেন, created_at ইনডেক্স করুন এবং ধারাবাহিকভাবে ব্যবহার করুন।
ভবিষ্যৎ-প্রমাণ করার চেক
প্রোডাকশন-সমান ডেটায় আপনার ইম্পোর্ট প্ল্যান পরীক্ষা করুন। যাচাই করুন আপনি রেকর্ডগুলো পুনরায় তৈরি করতে পারেন ফরেন কি না ভাঙছে এবং পুনরায় ইম্পোর্ট করলে নতুন আইডি চুপচাপ তৈরি হচ্ছে না।
আপনার সংঘাতকরণ কৌশল লিখে রাখুন। কে আইডি জেনারেট করে (DB না অ্যাপ), এবং যদি দুটি সিস্টেম অফলাইনে রেকর্ড তৈরি করে পরে সিঙ্ক করলে কি ঘটবে?
পাবলিক URL ও লগ এমন কিছু প্রকাশ না করে তা নিশ্চিত করুন (রেকর্ড কাউন্ট, তৈরি হার, অভ্যন্তরীণ শার্ড হিন্ট)। সিরিয়াল আইডি ব্যবহার করলে ধরে নিন মানুষ কাছের আইডিগুলি অনুমান করতে পারে।
বাস্তবসম্মত উদাহরণ: MVP থেকে মাল্টি-সিস্টেম ডেটা
একজন একক প্রতিষ্ঠাতা একটি সাধারণ CRM লঞ্চ করেন: contacts, deals, notes. একটি Postgres ডাটাবেস, একটি ওয়েব অ্যাপ, এবং প্রধান লক্ষ্য হচ্ছে শিপ করা।
শুরুতে, একটি serial bigint প্রাইমারি কী পারফেক্ট মনে হয়। ইনসার্ট দ্রুত, ইনডেক্স গুছানো থাকে, এবং লগে পড়তেও সহজ।
এক বছর পরে, একটি গ্রাহক একটি অডিটের জন্য কোয়ার্টারলি এক্সপোর্ট চায়, এবং প্রতিষ্ঠাতা মার্কেটিং টুল থেকে লিড ইম্পোর্ট করতে শুরু করেন। আগে অভ্যন্তরীণ যে আইডি ছিল তা এখন CSV, ইমেইল এবং সাপোর্ট টিকেটে দেখা যায়। যদি দুই সিস্টেম আলাদাভাবে 1, 2, 3... ব্যবহার করে, মার্জ জটিল হয়ে পড়ে। আপনাকে সোর্স কলাম যোগ করতে, ম্যাপিং টেবিল ব্যবহার করতে, বা ইম্পোর্টের সময় আইডি রিরাইট করতে হতে পারে।
দ্বিতীয় বছরে, একটি মোবাইল অ্যাপ তৈরি হয়। এটি অফলাইন অবস্থায় রেকর্ড তৈরি করবে এবং পরে সিঙ্ক করবে। তখন আপনাকে এমন আইডি দরকার যা ক্লায়েন্টে DB ছাড়া জেনারেট করা যায় এবং যখন ডেটা বিভিন্ন পরিবেশে এসে পড়ে তখন সংঘাতের ঝুঁকি কম থাকে।
একটি দীর্ঘমেয়াদি সমঝোতা যে বেশ ভালো কাজ করে:
- অভ্যন্তরীণ জয়েন ও স্টোরেজ দক্ষতার জন্য bigint প্রাইমারি কী রাখুন।
- শেয়ার, সিঙ্ক ও ইম্পোর্টের জন্য আলাদা পরিবর্তনশীল পাবলিক আইডি (ULID, বা UUIDv7 যদি উপলব্ধ) যোগ করুন।
- এক্সপোর্ট ফাইলে এবং সিস্টেম জুড়ে মার্জ কী হিসেবে পাবলিক আইডি ব্যবহার করুন।
আপনার প্রজেক্টের জন্য ব্যবহারিক পরবর্তী পদক্ষেপ
আপনি যদি UUID, ULID এবং serial IDs-এর মাঝে আটকে থাকেন, সিদ্ধান্ত নিন কিভাবে আপনার ডেটা চলবে ও বেড়ে উঠবে তার উপর ভিত্তি করে।
এক বাক্যে পছন্দগুলো সাধারণ ক্ষেত্রে:
- একক ডাটাবেস এবং কম ইন্টিগ্রেশন ঝুঁকি:
bigintserial প্রাইমারি কী ব্যবহার করুন। - পাবলিক অ্যাপ যেখানে URL শেয়ার করা হয় বা ক্লায়েন্ট পাশে ক্রিয়েশন হবে: UUID ব্যবহার করুন (অনুমান করা কঠিন, এবং সিস্টেম জুড়ে নিরাপদ)।
- ভবিষ্যতে টেন্যান্ট বা রিজিয়ন অনুযায়ী বিভাজন হতে পারে এমন SaaS: ULID (বা UUIDv7) ব্যবহার করুন যাতে নতুন রোদের ইনডেক্সে কাছাকাছি আসে।
- পার্টনার থেকে প্রচুর ইম্পোর্ট এবং অফলাইন ডিভাইস: বাইরের সত্তার জন্য খাঁটি সিরিয়াল আইডি এড়ান।
মিশ্রণ প্রায়শই সেরা উত্তর। অভ্যন্তরীণ টেবিল (জয়েন টেবিল, ব্যাকগ্রাউন্ড জব) যেখানে ডেটা বাইরে যাবে না সেগুলোর জন্য serial bigint ব্যবহার করুন, এবং ব্যবহারকারী, অর্গ, ইনভয়েস ইত্যাদি পাবলিক সত্তাগুলোর জন্য UUID/ULID ব্যবহার করুন যেগুলো আপনি এক্সপোর্ট, সিঙ্ক বা অন্য সার্ভিসে রেফার করতে পারেন।
যদি আপনি Koder.ai (koder.ai)-এ বিল্ড করেন, তাহলে অনেক টেবিল ও API জেনারেট করার আগে আপনার আইডি প্যাটার্ন নির্ধারণ করা যথেষ্ট সুবিধাজনক। প্ল্যাটফর্মের প্ল্যানিং মোড এবং snapshots/rollback ডেটাবেস পরিবর্তন প্রয়োগ ও যাচাই করা সহজ করে—তখন সিস্টেম ছোট থাকাকালীন বদল করাও নিরাপদ থাকে।
সাধারণ প্রশ্ন
কীভাবে খুব বেশি চিন্তা না করে serial IDs, UUIDs, এবং ULIDs-এর মধ্যে বেছে নেব?
ভবিষ্যতে কোন ব্যথা এড়াতে চান সেটা চিন্তা করে শুরু করুন: র্যেন্ডম ইনডেক্স লেখার কারণে ধীর ইনসার্ট, অদ্ভুত পেজিনেশন, ঝুঁকিপূর্ণ মাইগ্রেশন, নাকি ইম্পোর্ট/মার্জের সময় আইডি সংঘাত। যদি ডেটা একাধিক সিস্টেমে চলে বা একাধিক জায়গায় তৈরি হয় বলে মনে করেন, তাহলে ডিফল্ট হিসেবে গ্লোবালি ইউনিক আইডি (UUID/ULID) বেছে নিন এবং টাইম-অর্ডারের উদ্বেগ আলাদা রাখুন।
কখন serial bigint প্রাইমারি কী সেরা পছন্দ?
Serial bigint তখনই ভালো ডিফল্ট যখন আপনার একটাই ডাটাবেস আছে, লেখার পরিমাণ বেশি, এবং আইডিগুলো অভ্যন্তরীণ থাকে। এটি কমপ্যাক্ট, B-tree ইনডেক্সের জন্য দ্রুত, এবং লগে পড়তে সহজ। প্রধান অসুবিধা হলো পরে ডেটা মার্জ করার সময় সংঘাত হওয়া এবং এটিকে প্রকাশ করলে রেকর্ডের সংখ্যা ফাঁস হওয়া।
কখন UUID কে প্রাইমারি কী হিসেবে ব্যবহার করা উচিত?
UUID তখনই বেছে নিন যখন রেকর্ডগুলো একাধিক সার্ভিস, অঞ্চল, ডিভাইস, বা অফলাইন ক্লায়েন্টে তৈরি হতে পারে এবং আপনি সমন্বয় ছাড়াই অত্যন্ত কম সংঘাত ঝুঁকি চান। UUID পাবলিক-ফেসিং আইডি হিসেবে ভাল কারণ এগুলো অনুমান করা কঠিন। ট্রেড-অফ হলো বড় ইনডেক্স এবং সিকুয়েন্সিয়াল কীয়ের তুলনায় বেশি র্যান্ডম ইনসার্ট প্যাটার্ন।
ULID-র প্র্যাকটিক্যাল সুবিধা UUID-র তুলনায় কী?
ULID উপযুক্ত যদি আপনি চান যে আইডি যেখানেই জেনারেট করা হোক এবং সাধারণত ক্রিয়েশন টাইম অনুযায়ী সাজানো থাকে। এর ফলে কার্সর পেজিনেশন সহজ হয় এবং UUIDv4-এ দেখা যায় এমন ‘র্যান্ডম ইনসার্ট’ সমস্যাটা কমে। তবুও ULID-কে নিখুঁত টাইমস্ট্যাম্প ভাববেন না; কঠিন অর্ডারিং বা ব্যাকফিল সুরক্ষার জন্য created_at ব্যবহার করুন।
Postgres-এ র্যান্ডম UUID প্রাইমারি কী সত্যিই পারফরম্যান্সে ক্ষতি করে?
হ্যাঁ, বিশেষত UUIDv4-স্টাইল র্যান্ডমনেস লিখন-ভিত্তিক টেবিলগুলোতে কার্যকারিতায় খারাপ প্রভাব ফেলে। র্যান্ডম ইনসার্ট প্রাইমারি কী ইনডেক্স জুড়ে ছড়িয়ে দেয়, যার ফলে পেইজ স্প্লিট, ক্যাশ চর্ন এবং ইনডেক্স সময়ের সাথে বড় হওয়া দেখা যায়। আপনি সাধারণত প্রথমে অনুভব করবেন স্থায়ীভাবে ধীর ইনসার্ট রেট এবং বেশি মেমরি/IO প্রয়োজন হিসেবে, না যে একক সারি অনুসন্ধান ধীর।
কেন UUID দিয়ে সাজালে পেজিনেশন অদ্ভুত হয়ে যায়?
র্যানডম আইডি (যেমন UUIDv4) দ্বারা সাজানো হলে সেটা ক্রিয়েশন টাইমের সাথে মেলে না, তাই “এই আইডির পরে” টাইপের কার্সরগুলো স্থির টাইমলাইন দেয় না। নির্ভরযোগ্য সমাধান হচ্ছে created_at দিয়ে পেজিনেট করা এবং টাই-ব্রেকারের জন্য আইডি যোগ করা, যেমন (created_at, id)। একা আইডি দিয়ে পেজিনেট করতে চাইলে ULID-এর মতো টাইম-সোর্টেবল আইডি সাধারণত সহজ করে।
আমার আইডি নির্বাচন পরে শার্ডিংকে কীভাবে প্রভাবিত করে?
সিরিয়াল আইডি শার্ডগুলোর মধ্যে সংঘাত করে কারণ প্রতিটি শার্ড স্বাধীনভাবে 1, 2, 3... জেনারেট করবে। সংঘাত এড়াতে সমন্বয় (শার্ড প্রতি রেঞ্জ বা একটি সেন্ট্রাল আইডি সার্ভিস) লাগবে, যা অপারেশনাল জটিলতা বাড়ায় এবং বটলনেক তৈরি করতে পারে। UUID/ULID ব্যবহার করলে প্রতিটি শার্ড নিজে নিজে নিরাপদভাবে আইডি জেনারেট করতে পারে।
এক্সপোর্ট, ইম্পোর্ট এবং ডেটাসেট মার্জের জন্য কোন আইডি টাইপ সবচেয়ে নিরাপদ?
UUID/ULID-গুলো সহজ কারণ আপনি রোগুলো এক্সপোর্ট করে অন্যত্র ইম্পোর্ট করলে রিলেশনশিপ অক্ষত থাকে। সিরিয়াল আইডি হলে আংশিক ইম্পোর্টে সাধারণত একটি অনুবাদ টেবিল (old_id -> new_id) তৈরি করে ফরেন কি রিরাইট করতে হয়, যা করণীয় ভুল হওয়ার সম্ভাবনা বাড়ায়। যদি আপনি প্রায়ই এনভায়রনমেন্ট ক্লোন বা ডেটা মার্জ করেন, গ্লোবালি ইউনিক আইডি সময় বাঁচায়।
সবকিছুর জন্য একটাই আইডি ব্যবহার করা ভালো নাকি অভ্যন্তরীণ ও পাবলিক—উভয় ব্যবহার করা উচিত?
একটি সাধারণ প্যাটার্ন হলো দুটি আইডি রাখা: সংযুক্তিকরণ এবং স্টোরেজ দক্ষতার জন্য একটি কমপ্যাক্ট অভ্যন্তরীণ প্রাইমারি কী (serial bigint), এবং URL, API, এক্সপোর্ট ও ক্রস-সিস্টেম রেফারেন্সের জন্য একটি পরিবর্তনহীন পাবলিক আইডি (ULID বা UUID)। এতে ডাটাবেস দ্রুত থাকে এবং ইন্টিগ্রেশন/মাইগ্রেশন সহজ হয়। পাবলিক আইডিটিকে স্থায়ী হিসেবে বিবেচনা করুন এবং পুনরায় ব্যবহার বা পুনরায় ব্যাখ্যা করবেন না।
Koder.ai-এ তৈরি করার সময় আইডি কৌশল স্থির করার সবচেয়ে নিরাপদ উপায় কী?
আগে থেকেই পরিকল্পনা করে ধারাবাহিকভাবে প্রয়োগ করা। Koder.ai-তে আপনার ডিফল্ট আইডি কৌশল প্ল্যানিং মোডে নির্ধারণ করুন, তারপর অনেক স্কিমা ও এন্ডপয়েন্ট জেনারেট করার আগে snapshots/rollback ব্যবহার করে পরিবর্তনগুলো যাচাই করুন। সবচেয়ে কঠিন কাজ নতুন আইডি তৈরি করা নয়—এটা হল ফরেন কি, ক্যাশ, লগ এবং বাইরের ইন্টিগ্রেশন আপডেট করা যা পুরনো রেফারেন্স ব্যবহার করে।