- Knowledge
- technology
- OOP
- Tips
- Programming
- Tips
- Tutorial
- SEO
- Ranking
- Knowledge
- Special Day
- Seo
- Bug
- Data science
- Seo
- artificial intelligence
- Machine Learning
- Robotics
- happyNewYear2021
- newYearEve
- 2021
- Automation
- Smart Home
- Career
- Best Practices
- Git
- Logging
- Web Fundamentals
- DNS
- HTTPS
- Performance
- AI Tools
- ChatGPT
- Claude
- Gemini
- Laravel
- Eloquent
- MySQL
- HTTPS
- TLS
- Web Security
- Certificates
- Developer Life
- Debugging
- Docker
- DevOps
- Transactions
- Queues
- LLMs
- AI
- AI Coding
- Developer Tools
- React Native
- Expo
- Kate PMS
- Mobile Apps
- Laravel
- Authentication
- Sanctum
- Cookies
- API Design
- Payments
- Idempotency
- DeepSeek
- Open Source AI
- LLMs
- AI News
- Git
- Version Control
- AI Coding
- Prompting
- PHP
- Checklist
- MCP
- AI Agents
- OpenAI
- Architecture
- Microservices
- Modular Monolith
- Estimation
- Developer Life
- Project Planning
- Humour
- OAuth
- OpenID Connect
- Authentication
- Embeddings
- Vector Search
- RAG
- pgvector
- OpenAI
- GPT-4.1
- Codex CLI
- Events
- Testing
- Clean Code
- Maintainability
- Code Review
- Webhooks
- API
- Security
- Claude Code
- Workflow
- AI
- LLM
- Prompt Injection
- Mobile
- React
- Networking
- TCP
- UDP
- HTTP/3
- CLAUDE.md
- AWS
- Cloud Security
- Backups
- PHPUnit
- Software Engineering
- Leadership
- Communication
- RAG
- Embeddings
- AI Engineering
- IT Infrastructure
- Networking
- Access Control
- CI/CD
- GitHub Actions
- Gemini CLI
- Claude Code
- JavaScript
- Async/Await
- Node.js
- Promises
- Security
- Cryptography
- Passwords
- MySQL
- Database
- Vibe Coding
- Software Quality
- DNS
- Code Reading
- Onboarding
- Productivity
- Background Jobs
- Developer Humour
- Estimates
- Dev Life
- JWT
- o3-mini
- DeepSeek R1
- Rate Limiting
- Kate PMS
- E-Signing
- Audit Trail
- REST
- GraphQL
- API Design
- Laravel 12
- Upgrade Guide
- Open Source
- Self-Hosting
- Task Scheduling
- Cron
- Secrets
- CORS
- PHP
- PHP-FPM
- OPcache
- GitHub Copilot
- Software Architecture
- Engineering
- TypeScript
- JavaScript
- Type Safety
- AI Security
- React Native
- Product Design
- AI Agents
- Kiro
- Queues
- Redis
- RabbitMQ
- AWS SQS
- Nginx
- Apache
- GPT-5
- gpt-oss
- Clean Code
- Architecture
- Naming
- Documentation
- Career
- ADR
- Teamwork
- Supply Chain
- Kate HRM
- HR Software
- Permissions
- System Design
- Pagination
- SSH
- Linux
- Big O
- Databases
- Laravel Boost
- MCP
- Developer Skills
- Validation
- Databases
- Indexes
- Code Quality
- Deployment
- Developer Humour
- Feature Flags
- Code Review
- Pull Requests
- Docker
- Cursor
- Authorization
- RBAC
- Gemini
- Long Context
- PHP 8.4
- Caching
- Dependency Injection
- Web Performance
- Browser
- CSS
- Database
- Migrations
- ChatGPT
- AI for Developers
- Monitoring
- On-Call
- REST
- Backend
- SQL
- NoSQL
- Database Design
- Coding Agents
- Claude 4
- API Resources
- REST API
- Load Balancing
- Scaling
- AWS
- AI Tools
- Claude
- Sora 2
- CTE
- 2FA
- TOTP
- Programming Languages
- Prompts
- Developer Workflow
- API Gateway
- APIs
- Passport
- API Auth
- Learning
- Burnout
- Developer Growth
- Web Development
- SEO
- Kate Mall
- ChatGPT Atlas
- Agent Skills
- Middleware
- Laravel 12
- Collections
- Context Window
- Monitoring
- Commit Messages
- Self Review
- Growth
- Regex
- Programming Basics
- Text Processing
- Database Design
- Normalization
- Linux
- Server Security
- Linux Foundation
- Open Standards
- Legacy Code
- Documentation
- AI Workflow
- File Uploads
- Test Data
- Hashing
- Performance
- Caching
- Enums
- Scope Creep
- Estimation
- Codex
- Gemini CLI
- Timezones
- Carbon
- Bugs
- PHP 8.5
- Gemini 3
- GPT-5.1
- Data Integrity
- Event Loop
- Async
- Opus 4.5
- AI Models
- React
- Forms
- Frontend
- Backups
- AI Images
- DALL-E
- Midjourney
- Race Conditions
- Concurrency
- Legacy Code
- Refactoring
- Senior Engineer
- Scope
- LLM
- CDN
- Web
- Sub-Agents
- Soft Deletes
- Audit Log
- Concurrency
- AI Learning
- NestJS
- AI Evals
- Policies
- SPF DKIM DMARC
- Unicode
- UTF-8
- Knowledge Graph
- Value Objects
- Technical Debt
- Feature Flags
- Laravel Pennant
- Deployment
- Copilot
- Composer
- Dependencies
- Artisan
- Automation
- AWS S3
- Object Storage
- Cloud
- Small Language Models
- Ollama
- Production
- Sessions
- HTTP
- Mentoring
- SQL
- Virtual Machines
- Web Development
- HTTP/2
- QUIC
- Web Performance
- AI Integration
- LLM API
- SOLID
- OOP
- Hosting
- Serverless
- Merge Conflicts
- Temperature
- AI Development
- Reverse Proxy
- Nginx
- Infrastructure
- Verification
- Passkeys
- WebAuthn
- Teams
- Communication
- Stakeholders
- Monorepo
- CI/CD
- Versioning
- JSON Schema
- Livewire
- Inertia
- Meetings
- Distributed Systems
- Privacy
- Full-Stack
- T-Shaped Skills
- Money
- Notifications
- Web Security
- HTTP Headers
- CSP
- Function Calling
- Load Testing
- k6
- Data Extraction
- Debugging
- WebSockets
- SSE
- Real-Time
- Laravel Reverb
- Infrastructure as Code
- Terraform
- Side Projects
- Laravel Pint
- OpenAPI
- Swagger
- UX
- Multimodal
- Jest
- Pair Programming
- APIs
- Rate Limiting
- Resilience
- Dev Humour
- Design Tokens
- JWT
- API Keys
- Sessions
- PHPStan
- Rector
- Incidents
- Reporting
- Dashboards
- Zero Trust
- IAM
- Search
- Laravel Scout
- Junior Developers
- Mentoring
- Images
- WebP
- AVIF
- Bug Reports
- Let's Encrypt
- Design Docs
- Software Design
- Observers
- Replication
- Accountability
- Data Structures
- Reliability
- LLM Memory
- Error Handling
- Payments
- Payment Gateway
- Webhooks
- PCI DSS
- Observability
- OpenTelemetry
- Personal Brand
- Writing
- Conventions
- Dates
- Scheduling
- Disaster Recovery
- Compression
- Brotli
- Deadlines
- Developer Habits
- State Machines
- Tech Roles
- UUID
- ULID
- Horizon
- Planning
- Engineering Culture
- Ownership
- Soft Skills
- Socialite
- Cost Control
- Collations
- Unicode
- Octane
- PostgreSQL
How a Database Index Works: A B-Tree in Five Minutes
About Post
Most developers learn indexes as a magic spell. Query slow? Add an index. Query fast. Nobody asks why.
Then one day the spell stops working. The index is there, EXPLAIN says it isn't being used, and the query is still scanning the whole table. Or you add indexes everywhere "just in case" and your imports get slower every month.
The fix for both is the same: be able to picture what an index actually is. It takes about five minutes. Let's go minute by minute.
Minute 1: the phone book
Imagine a phone book with a million names, printed in random order. To find "Shiroz, Mohammed" you'd have to read every single entry. That's a full table scan, and it's what your database does when there's no useful index.
Now imagine the same phone book sorted by surname. You'd open it near the middle, see "M", flip forward, see "R", flip a little further, and land on "S" in a handful of moves. You never read most of the book.
That's all an index is: a separate, sorted copy of one or more columns, with a pointer back to the full row. The table stays as it is. The index is the sorted list you search instead.
Minute 2: why it's a tree, not a list
A sorted list is great for reading, but databases live on disk, and they read in fixed-size chunks called pages (16 KB by default in MySQL's InnoDB). Jumping around a giant sorted list would still mean reading lots of pages.
So the sorted data is organised as a B-tree (strictly, InnoDB uses a variant called a B+tree). Picture three levels:
[ root: G | P ]
/ | \
[ A..F ] [ G..O ] [ P..Z ] <- branch pages
/ | \ / | \ / | \
leaf pages: sorted keys + pointer to each row
The root page says "names before G go left, G to O go middle, P onwards go right". Each branch page narrows it down again. The leaf pages hold the actual sorted keys.
The trick is that each page holds hundreds of keys, so the tree is very wide and very short. Three or four levels can cover millions of rows. Finding any single value means reading one page per level: a few page reads instead of the whole table. And the root and upper levels are used constantly, so they're almost always already in memory.
Minute 3: what "pointer to the row" really means
In InnoDB, the table itself is stored as a B-tree ordered by the primary key. This is called the clustered index: the leaf pages of the primary key are the rows.
Every other index (a secondary index) stores the indexed columns plus the primary key value. So a lookup by email is really two lookups:
- Walk the
emailindex to find the matching entry and its primary key. - Walk the primary key tree to fetch the full row.
For one row, that's nothing. For thousands of rows, that second hop happens thousands of times, and it's often why "it uses the index, but it's still slow".
Minute 4: composite and covering indexes
A phone book is really sorted by surname, then first name. That's a composite index:
CREATE INDEX idx_payments_tenant_date
ON payments (tenant_id, paid_at);
Like a phone book, it's only useful from the left. It helps with:
WHERE tenant_id = 42WHERE tenant_id = 42 AND paid_at >= '2025-01-01'WHERE tenant_id = 42 ORDER BY paid_at(already sorted, no extra sort step)
It does not help with WHERE paid_at >= '2025-01-01' alone. Finding everyone whose first name is "Sara" in a phone book sorted by surname means reading the whole book. This is the leftmost prefix rule, and it's the reason column order in a composite index matters so much: put the column you filter by equality first, and the range or sort column after it.
Now the best trick in the book. If an index contains every column the query needs, the database can answer from the index alone and skip the second hop entirely. That's a covering index:
-- Answered entirely from idx_payments_tenant_date:
SELECT paid_at FROM payments
WHERE tenant_id = 42
ORDER BY paid_at DESC;
(Remember the primary key is stored in every secondary index, so selecting id is free too.) In MySQL's EXPLAIN, look for Using index in the Extra column. That's your sign the query never touched the table.
Minute 5: the price you pay
Indexes aren't free. Every index is another sorted structure that must be kept sorted.
- Every write updates every index. An insert into a table with six indexes is really seven inserts. Updates to an indexed column mean removing and re-inserting index entries.
- They take space, on disk and in the memory buffer that keeps hot pages cached.
- Random keys hurt. Inserting random values (like random UUIDs as a primary key) lands new entries all over the tree, causing page splits and scattered writes. Sequential keys just append to the right-hand edge.
- Low-selectivity columns rarely help. An index on a
statuscolumn with three values often isn't worth it on its own, because each value matches a huge share of the table anyway. As part of a composite index, it can be very useful.
Rule of thumb: index for the queries you actually run, not the columns you have. Start from the slow query, read its EXPLAIN, and design one index that serves its WHERE, then its ORDER BY, and if it's hot enough, its SELECT.
Bonus: why your index is being ignored
With the tree in your head, the classic "index not used" cases suddenly make sense. The database can only walk the tree if it can compare your value against the sorted values as stored:
WHERE YEAR(created_at) = 2025wraps the column in a function, so the sorted order can't be used. Writecreated_at >= '2025-01-01' AND created_at < '2026-01-01'instead.WHERE email LIKE '%@gmail.com'has a leading wildcard. Like searching a phone book for surnames ending in "-son".- Comparing a string column to a number forces a conversion on every row.
- Skipping the first column of a composite index, as we saw in minute four.
The five-minute summary
- An index is a sorted copy of some columns, stored as a short, wide B-tree.
- Lookups are fast because you read one page per level, not the whole table.
- Secondary indexes point to the primary key, so many matches mean many extra hops.
- Composite indexes work from the left. Covering indexes skip the table entirely.
- Every index makes writes a little slower. Add them on purpose.
What's the index that made the biggest difference in a project of yours, and what was the query it fixed?

Be first to comment it...